并非每次都触发UserControl DependencyProperty中的WPF PropertyChanged

时间:2019-01-07 23:50:45

标签: c# wpf data-binding user-controls dependency-properties

我已经研究了以下情况很多小时了:我有一个xaml定义的窗口,该窗口利用了具有某些依赖项属性的usercontrol(ToggleButton)。

窗口的基础视图模型包含一些布尔对象,这些布尔对象表示设备的状态(打开/关闭),而另一些对象则表示请求切换带有正/反侧翼的设备(将PLC连接到这些对象,并且通讯正常)。

因此,用户控件上有2个DP:

用于切换设备的设备(将模式OneWayToSource与 UpdateSourceTrigger.Explicit 绑定可以正常工作(向我表明,共享DataContext之类的基础很好,并且在任何地方都不会“中断”)。

但是,表示另一个DP的绑定(绑定模式为OneWay的设备状态)显示以下症状:

  1. 启动程序之前(PLC-)设备已关闭(false)

    结果:DeviceState属性的默认值为false。 设备首次开机时称为Set (基础viewmodel对象更改为true,通过 PropertyChanged通知),并且DependencyPropertyChanged为 被正确调用。进一步切换到关闭/打开(false / true) 再次不会导致再次调用“ set”(尽管 再次调用基础对象上的PropertyChanged。)

  2. 启动程序之前,设备已开启(true)

    结果:DP处理程序在以下位置触发 程序的开始,没有更改 false或true可以再次调用它。

我已经尝试过追踪的内容是:

  1. 实现了 DummyDebugConverter

    结果:我看到它也只被触发了一次。所以不再给我任何线索

  2. 分析“输出”窗口并找到以下消息:

    System.Windows.Data信息:21:BindingExpression无法从空数据项检索值。当绑定分离或绑定到没有值的Nullable类型时,可能会发生这种情况。 BindingExpression:Path = bLightState.Value; DataItem ='ControlPanelModel'(HashCode = 45596481);目标元素是“ AdsButton”(名称=“ btnLight”);目标属性为“ DeviceState”(类型为“ Boolean”)

    调试它并没有给我任何提示。我的断点在调试转换器或set方法中从未在任何地方向我显示空值。 viewmodel构造函数中的所有值均使用默认值初始化。但是我看到消息总是只有一次,我认为它与问题有某种联系。

  3. 除了我的用户控件之外,还对其他一些元素(标签和切换按钮)使用了相同的绑定表达式进行测试。它们工作得很好,并且只要视图模型中的对象发生更改(期望的行为),它们就会按预期更新其值。如果我删除用户控件,则会在2个分配器中显示该消息。

所以我得出的结论是,错误在于我对DP的定义。

以下是相关的代码段:

AdsButton.xaml.cs

    [Description("When set to true the device is shown as on"), Category("Default")]
    public bool DeviceState
    {
        get { return (bool)GetValue(DeviceStateProperty); }
        set { SetValue(DeviceStateProperty, value); }
    }

public static readonly DependencyProperty DeviceStateProperty =
            DependencyProperty.Register(
                "DeviceState", typeof(bool),
                typeof(AdsButton), 
                new FrameworkPropertyMetadata(
                  false, 
                  FrameworkPropertyMetadataOptions.None,
                  DeviceStateChanged, 
                  CoerceDeviceStateProperty,
                  true,
                  UpdateSourceTrigger.Explicit));

        private static void DeviceStateChanged(DependencyObject d, 
               DependencyPropertyChangedEventArgs e)
        {
            (d as AdsButton).DeviceState = (bool) e.NewValue;
        }

        private static object CoerceDeviceStateProperty(DependencyObject d, object value)
        {
            return value ?? false;
        }

ControlPanel.xaml

      <src:AdsButton x:Name="btnLight"  
                  Value="{Binding Path=bLight.Value, Mode=OneWayToSource}"
                  DeviceState="{Binding Path=bLightState.Value, Mode=OneWay}" />
      <Label Content="{Binding bLightState.Value, Mode=OneWay}" />
      <ToggleButton Content="Button" IsChecked="{Binding bLightState.Value, Mode=OneWay}" />

所以有人知道:为什么我自己的DP的反应与标准控件中的反应不同?

1 个答案:

答案 0 :(得分:0)

由于罗杰(Roger)的最初评论,答案很明显:

使用setter方法设置DP本身会用固定值(有效地将其删除)覆盖绑定,但前提是新值与旧值不同。