首页 > 解决方案 > 绑定无法通过自定义控件

问题描述

我正在尝试制作一个 WPF 用户控件,该控件可以倒计时到特定日期并将文本变为红色以在剩余指定时间跨度时提醒用户。

当我传入静态参数时,我的代码按计划工作。当我传入绑定参数时,我的代码失败。

这是我的调用代码;

<!-- WORKS (STATIC PARAMETERS) -->
<controls:TextEditDateCountDown                
    AlertAtMinutesLeft="2100"
    DateStampCountDownTo="{Binding Source={x:Static system:DateTime.Now},StringFormat='HH:mm:ss tt'}" />

<!-- DOES NOT WORK (BOUND PARAMETERS) -->
<controls:TextEditDateCountDown
    AlertAtMinutesLeft="2100"
    DateStampCountDownTo="{Binding Entity.MarketDescription.SuspendTime,Mode=OneTime}"/>

<!-- DOES NOT WORKING (BOUND PARAMETERS TRYING PRIORITY BINDING AS AT http://www.blackwasp.co.uk/WPFPriorityBinding.aspx) -->                       
<controls:TextEditDateCountDown    
    AlertAtMinutesLeft="2100">
    <controls:TextEditDateCountDown.DateStampCountDownTo>
        <PriorityBinding>
             <Binding Path="Entity.MarketDescription.SuspendTime" Mode="OneTime" IsAsync="True"/>
             <Binding Path="Entity.MarketId" Mode="OneTime" IsAsync="True"/>
        </PriorityBinding>
    </controls:TextEditDateCountDown.DateStampCountDownTo>
</controls:TextEditDateCountDown>

控制代码如下所示;

<!-- THE XAML -->
<Grid>
    <dxe:TextEdit
        x:Name="myTextEdit"
        IsPrintingMode="True"
        Mask="dd\d HH\h mm\m ss\s"
        MaskType="DateTimeAdvancingCaret"
        MaskUseAsDisplayFormat="True">
        <dxe:TextEdit.Style>
            <Style TargetType="dxe:TextEdit">
                <Style.Triggers>
                    <DataTrigger Value="True" Binding="{Binding IsAlertOn, RelativeSource={RelativeSource AncestorType=controls:TextEditDateCountDown},Mode=OneTime}">
                        <Setter Property="Foreground" Value="Red" />
                        <Setter Property="FontWeight" Value="Bold" />
                    </DataTrigger>
                    <DataTrigger Value="False" Binding="{Binding IsAlertOn, RelativeSource={RelativeSource AncestorType=controls:TextEditDateCountDown},Mode=OneTime}">                            <Setter Property="Foreground" Value="Black" />
                        <Setter Property="FontWeight" Value="Regular" />
                    </DataTrigger>
                </Style.Triggers>
            </Style>
        </dxe:TextEdit.Style>
    </dxe:TextEdit>
</Grid>

<!-- THE CODE BEHIND -->
public partial class TextEditDateCountDown : UserControl {
    public TextEditDateCountDown() {            
        InitializeComponent();
        timer.Interval = new TimeSpan(0, 0, 1);
        timer.Tick += Timer_Tick;
        timer.Start();
    }
    DispatcherTimer timer = new DispatcherTimer();
    public static readonly DependencyProperty DateStampCountDownToProperty = DependencyProperty.Register("DateStampCountDownTo", typeof(DateTime), typeof(TextEditDateCountDown), new PropertyMetadata(DateTime.MinValue, CountDownToChanged));
    public static readonly DependencyProperty AlertAtMinutesLeftProperty = DependencyProperty.Register("AlertAtMinutesLeft", typeof(string), typeof(TextEditDateCountDown), new PropertyMetadata(string.Empty, Changed));
    public static readonly DependencyProperty IsAlertOnProperty = DependencyProperty.Register("IsAlertOn", typeof(bool), typeof(TextEditDateCountDown), new PropertyMetadata(false));

    public DateTime? DateStampCountDownTo {
        get {                
            return (DateTime?)GetValue(DateStampCountDownToProperty);
        }
        set { SetValue(DateStampCountDownToProperty, value); }
    }

    public virtual string AlertAtMinutesLeft {
        get {                                
            return (string)GetValue(AlertAtMinutesLeftProperty);
        }
        set { SetValue(AlertAtMinutesLeftProperty, value); }
    }

    public bool IsAlertOn {
        get { return (bool)GetValue(IsAlertOnProperty); }
        set { SetValue(IsAlertOnProperty, value); }
    }


    private bool UpdateIsAlertOn(string value) {

        if (value != null && value != "") {
            int i = int.Parse(AlertAtMinutesLeft);
            return DateTime.Now > DateStampCountDownTo.Value.AddMinutes(i);
        } else
            return false;
    }

    private DateTime? UpdateDateStampCountDownTo(DateTime? value) {
        return DateStampCountDownTo.Value;
    }

    private static void Changed(DependencyObject d, DependencyPropertyChangedEventArgs e) {
        var textEditControl = d as TextEditDateCountDown;
        if (textEditControl == null) return;            
        textEditControl.IsAlertOn = textEditControl.UpdateIsAlertOn(textEditControl.AlertAtMinutesLeft);            
    }

    private static void CountDownToChanged(DependencyObject d, DependencyPropertyChangedEventArgs e) {
        var textEditControl = d as TextEditDateCountDown;
        if (textEditControl == null) return;
        textEditControl.DateStampCountDownTo = textEditControl.UpdateDateStampCountDownTo(textEditControl.DateStampCountDownTo);
   }

    void Timer_Tick(object sender, EventArgs e) {         
        if (DateStampCountDownTo != null) {
            TimeSpan t = DateStampCountDownTo.Value - TimeProvider.Current.UtcNow;
            myTextEdit.MaskType = MaskType.None;
            if (t < TimeSpan.Zero) {
                myTextEdit.MaskType = MaskType.None;
                myTextEdit.EditValue = "Closed";
            }               
            myTextEdit.EditValue = t;
        } else {
            myTextEdit.EditValue = null;
        }            
    }

}

这是一个复杂的问题(我希望不会因为使用 DevExpress 的 Textedit 控件而变得复杂)。我想知道是否有人给我任何关于我应该去哪里修复它的指示?(或如何解决)。谢谢你的时间。

UPDATE1:我不知道为什么,但是;此事件处理程序从不运行 -> private static void CountDownToChanged(DependencyObj... 此事件处理程序执行 -> private static void Changed(DependencyObj...

问题可能出在 DependencyProperty 声明中吗?

UPDATE2:除了 update1,我还发现 CountDownToChanged 事件处理程序使用静态参数输入运行,但在使用绑定参数时不运行。

UPDATE3:我已更新此处的代码以删除 datacontext = 此代码。并且还更正了控件 xaml 中的相对绑定。

问题仍然存在。

更新4:好的。这是工作。经过数小时的无数次尝试后,老实说,我无法弄清楚我做了什么让它发挥作用。我怀疑 Clemens 链接副本是答案,我犯了某种形式的绑定错误,掩盖了它确实有效的事实。

出于这个原因,可能值得删除这篇文章,因为我不知道真正的解决方案,而且我怀疑没有人会觉得这很有用。

标签: c#wpfxaml

解决方案


你的Entity.MarketDescription.SuspendTime班级看起来怎么样?

该属性SuspendTime应实现接口INotifyPropertyChanged以在每次更新时通知您的控件。

顺便说一句,我注意到Mode=OneTime您的工作代码中没有。有什么理由吗?

是实施 INPC 的示例

就像是:

public class MarketDescription : INotifyPropertyChanged
{
    // implements your interface here
    ...
    // your property to bind

    private DateTime suspendTime;
    public DateTime SuspendTime
    {
        get { return suspendTime; }
        set 
        {
            suspendTime = value;
            NotifyPropertyChanged("SuspendTime");
        }
    }
}

推荐阅读