C# 用户控件依赖项属性不更新,因为它们是引用

C# 用户控件依赖项属性不更新,因为它们是引用,c#,wpf,dynamic,binding,custom-controls,C#,Wpf,Dynamic,Binding,Custom Controls,我对WPF非常陌生,可能缺少一些简单的东西,但我尝试了一些自定义用户控件绑定的简单示例,它们都可以工作,但是当我尝试将它们应用到我们的情况时,绑定仅在我修改自定义用户控件中的值时才起作用,但是如果我从其他地方更改它,自定义用户控件不会反映该更改。 我们使用的是Prism,我们有一个ViewModel和一个视图来显示和编辑设备的属性。这些属性在启动时从XML中读取,并作为设置字典添加到设备中。设置和设备都实现INotifyPropertyChanged,当设置的值更改时,设备也会引发该设置的属性更

我对WPF非常陌生,可能缺少一些简单的东西,但我尝试了一些自定义用户控件绑定的简单示例,它们都可以工作,但是当我尝试将它们应用到我们的情况时,绑定仅在我修改自定义用户控件中的值时才起作用,但是如果我从其他地方更改它,自定义用户控件不会反映该更改。

我们使用的是Prism,我们有一个ViewModel和一个视图来显示和编辑设备的属性。这些属性在启动时从XML中读取,并作为设置字典添加到设备中。设置和设备都实现INotifyPropertyChanged,当设置的值更改时,设备也会引发该设置的属性更改事件

因此,当程序启动时,值如下所示,其中红色箭头是新的自定义用户控件,蓝色箭头直接指向处于视图初始状态的工作代码:

如果我更改自定义用户控件中的值,它也会在另一个控件中更新。绑定到源ok:

但是如果我在另一个控件上更改它,它不会在自定义用户控件中更新。从源绑定不正常:

此外,如果我更改str_pressureUnit条件的值,则转换仅在旧代码中执行。转换条件绑定不正常:

但是启用/禁用控件对这两种情况都正常工作。启用ok:

如果我使用`Mode=“TwoWay”,则带有另一个自定义用户控件和设备中非动态属性的简单示例可以正常工作。我想这可能是多重绑定或动态设置配置的问题,但由于启用属性的配置(这是ViewModel的正常属性)起作用,我怀疑这可能与动态属性有关

这就是我们的设备类的外观:

  public class Device : DynamicObject, INotifyPropertyChanged
  {
            #region Properties

            ...
            public Dictionary<string, ISetting> DynamicSettings { get; private set; } = new Dictionary<string, ISetting>(); 
            ...
            public Device(SettingDefinitionsProvider settingDefinitionsProvider, ICommunicationChannel communicationChannel, DeviceType deviceType)
            {
                ...
                foreach(ISetting s in DynamicSettings.Values)
                {
                    s.PropertyChanged += OnSettingValueUpdated;
                }

            }
        ...
        #region INotifyPropertyChanged

        public event PropertyChangedEventHandler PropertyChanged;
        protected void OnPropertyChanged([CallerMemberName]string propertyname = null)
        {
            PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyname));
        }

        #endregion //INotifyPropertyChanged

        #region EventHandlers

        private void OnSettingValueUpdated(object sender, PropertyChangedEventArgs e)
        {
            if(sender is ISetting)
            {
                OnPropertyChanged((sender as ISetting).Name);
            }
        }

        #endregion //EventHandlers
  }
这是视图中的新代码:

<controls:CustomTextBox Grid.Row="3" 
    Setting="{Binding SelectedDevice.SafetyMargin, Mode=TwoWay}" 
    ConditionSetting="{Binding SelectedDevice.PressureUnit, Mode=TwoWay}" 
    Enabled="{Binding EnableControls}"/>

如果您有任何建议,我们将不胜感激。您调试了吗

  • 设备类中的回调
    OnSettingValueUpdated
    是否被调用 在这两种情况下,有什么区别吗

  • 是UserControl中ConditionSetting/Setting的设置程序 打电话


我想说,不知何故,PropertyChanged没有正确执行或没有到达属性…

问题是使用引用作为DependencyProperties的值。所有结构都在工作,DependencyProperty听到设置的NotifyPropertyChanged事件,但它在更新值之前检查是否相等,然后发现引用是相同的,因此没有更新它

强制不起作用,因为它是在检查平等性之前完成的

我们尝试的解决方案是:

  • 每次设置值更改时创建新设置。这是可行的,但我们担心可能出现的问题,例如,对旧设置的引用没有更新到代码中没有侦听PropertyChanged事件的地方

  • 向UserControl的xaml中的转换器多重绑定添加其他绑定,特别是Setting.Value。该属性还有一个PropertyChangedEvent,因此当它发生更改时,转换器将重新评估,UserControl将按照我们的预期工作。除了XAML之外,我们不必更改实际的代码,所以我们最终使用了这个解决方案


你好,安迪,谢谢你的评论!是的,我尝试过调试,在这两种情况下,都调用了ConditionSetting/Setting的setter(在UserControl的情况下,只有Setting的值被更改,所以只调用setter),而且OnSettingValueUpdated似乎在这两种情况下都以相同的方式触发。。。我倾向于认为,这一事件没有达到UserControl@AlejandroAD:尝试使用“SetCurrentValue”而不是“SetValue”。查看此问题:问题可能是
SetValue()
覆盖了属性上的现有绑定,这就是您的更改不再传播到视图的原因。一旦您设置了听起来非常有希望的valueWow,绑定就会被删除,但在用SetCurrentValue替换SetValue后,我看不到行为上的任何变化:(.我现在已经将str_pressureUnit放入它自己的用户控件中,我发现该值在模型中更新,并且直接在视图中的文本框使用转换器更改其值,但是背景颜色没有更新,因此它不会“听到”属性的变化。你找到解决方案了吗?
<UserControl ...
         x:Name="parent">

<Grid DataContext="{Binding ElementName=parent}">
    <Grid.ColumnDefinitions>
        <ColumnDefinition Width="Auto"/>
        <ColumnDefinition />
        <ColumnDefinition />
    </Grid.ColumnDefinitions>
    <Grid.RowDefinitions>
        <RowDefinition Height="Auto"/>
    </Grid.RowDefinitions>

    <TextBlock Text="{Binding Path=Setting.DisplayName}" Margin="5"/>
    <TextBox Grid.Column="1"  Margin="5">
        <TextBox.IsEnabled>
            <MultiBinding Converter="{conv:EnableControlConverter}">
                <Binding Path="Setting"/>
                <Binding Path="Enabled"/>
            </MultiBinding>
        </TextBox.IsEnabled>
        <MultiBinding Mode="TwoWay" Converter="{conv:ConversionConverter}">
            <Binding Path="Setting"/>
            <Binding Path="ConditionSetting"/>
        </MultiBinding>
    </TextBox>
    <TextBlock Grid.Column="2"  Margin="5">
        <TextBlock.Text>
            <MultiBinding Converter="{conv:UnitLabelConverter}">
                <Binding Path="Setting"/>
                <Binding Path="ConditionSetting"/>
            </MultiBinding>
        </TextBlock.Text>
    </TextBlock>
</Grid>
public partial class CustomTextBox : UserControl
{
    protected static Logger logger = LogManager.GetCurrentClassLogger();

    public static readonly DependencyProperty SettingProperty =
        DependencyProperty.Register("Setting", typeof(object),
          typeof(CustomTextBox), new PropertyMetadata(""));

    public static readonly DependencyProperty ConditionSettingProperty =
        DependencyProperty.Register("ConditionSetting", typeof(object),
          typeof(CustomTextBox), new PropertyMetadata(""));

    public static readonly DependencyProperty EnabledProperty =
        DependencyProperty.Register("Enabled", typeof(object),
          typeof(CustomTextBox), new PropertyMetadata(""));

    public object Setting
    {
        get { return (object)GetValue(SettingProperty); }
        set { SetValue(SettingProperty, value); }
    }

    public object ConditionSetting
    {
        get { return (object)GetValue(ConditionSettingProperty); }
        set { SetValue(ConditionSettingProperty, value); }
    }

    public object Enabled
    {
        get { return (object)GetValue(EnabledProperty); }
        set { SetValue(EnabledProperty, value); }
    }

    public CustomTextBox()
    {
        InitializeComponent();
    }

}
<controls:CustomTextBox Grid.Row="3" 
    Setting="{Binding SelectedDevice.SafetyMargin, Mode=TwoWay}" 
    ConditionSetting="{Binding SelectedDevice.PressureUnit, Mode=TwoWay}" 
    Enabled="{Binding EnableControls}"/>