【问题标题】:How to fire Property Changed Event when it's not actually changing如何在没有实际更改时触发属性更改事件
【发布时间】:2016-12-14 04:27:51
【问题描述】:

所以我有一个模型,其中包含 2 个变量,一个列表和一个日期时间。在我的 UserControl 中,我有一个 DependencyProperty,我还定义了一个 PropertyChangedCallback。

public static readonly DependencyProperty MyProperty = DependencyProperty.Register("My", typeof(List<MyContainer>), typeof(UC), new FrameworkPropertyMetadata(null, new PropertyChangedCallback(OnMyProperty)));

public List<MyContainer> My
{
    get
    {
        return GetValue(MyProperty) as List<MyContainer>;
    }
    set
    {
        SetValue(MyProperty, value);
    }
}
private static void OnMyProperty(DependencyObject d, DependencyPropertyChangedEventArgs e)
{
    UC control = d as UC;
    //do stuff
}

在我的表单上有一个按钮,它可以对另一个模型变量(在 DateTime 上)进行更改。

private void Date_Click(object sender, RoutedEventArgs e)
{
    MyModel model = DataContext as MyModel;
    if (model != null)
    {
        model.Date = model.Date.AddDays(1);
    }
}

最后这是我的模型。

public class MyModel : INotifyPropertyChanged
{
    private List<MyContainer> _My;
    private DateTime _Date;

    public MyModel()
    {
        _Date = DateTime.Now.Date;
        _My = new List<MyContainer>();
    }

    public List<MyContainer> My
    {
        get
        {
            return _My;
        }
        set
        {
            _My = value;
            OnPropertyChanged("My");
        }
    }

    public DateTime Date
    {
        get
        {
            return _Date;
        }
        set
        {
            _Date = value;
            OnPropertyChanged("Date");
            OnPropertyChanged("My");
        }
    }

    #region INotifyPropertyChanged
    public event PropertyChangedEventHandler PropertyChanged;

    private void OnPropertyChanged(string propertyName)
    {
        PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
    }
    #endregion
}

XAML 声明如下。

<local:UC My="{Binding My}" />

所以我的问题是,在我运行后,它会触发 OnMyProperty 一次,之后如果我点击按钮,它会很好地更改 DateTime 属性,但 OnMyProperty 回调不会再次触发。但是我注意到,如果我像这样修改我的模型

public DateTime Date
{
    get
    {
       return _Date;
    }
    set
    {
        _Date = value;
        _My = new List<MyContainer>(_My); //added
        OnPropertyChanged("Date");
        OnPropertyChanged("My");
    }
}

现在每次我按下按钮时它都会触发它。如果不进行该修改,如何触发第二个行为?

【问题讨论】:

标签: c# wpf data-binding viewmodel


【解决方案1】:

设置DependencyProperty 的值后,它首先检查新值是否与旧值不同。只有在这种情况下,您使用该DependencyProperty 注册的PropertyChangedCallback 方法才会被调用。因此,PropertyChanged 这个名称是有意义的。

在您(未修改)的情况下,您甚至不会尝试更改My(仅Date)。所以没有理由引发回调函数。

【讨论】:

  • 好的,但是对于我的情况,在 My 或 Date 更改时触发事件的正确方案是什么?
  • 例如,您可以从OnMyPropertyDate_Click 调用一个新函数OnChange。也许这不是最好的方式,但我对你的架构和目的了解不够。这取决于您想对变化做出反应的地点和原因。
【解决方案2】:

答案是您几乎可以肯定不需要这样做。当你问一个关于如何让框架做它不想做的事情的问题时,总是说为什么你认为你需要这样做。很可能有一个其他人已经在使用的更简单的答案。

您绑定到控件的唯一内容是My。因此,如果My 没有改变,那么控件的状态不应该改变。如果您希望在Date 更改时更改控件的状态,请将Date 绑定到控件的某个属性。 唯一控件应该从任何视图模型获取信息的方式是将其依赖属性之一绑定到视图模型的属性。

控件不应该知道或关心谁或什么为其属性提供值。它应该能够只知道它被赋予的属性值来完成它的工作。

如果My 的内容发生了变化——您添加了一项或删除了一项——当然控件无法知道这一点,因为您拒绝告诉它。你只是告诉它有一个新列表。它检查,发现它仍然有相同的旧列表,然后忽略你。您的视图模型的My 属性应该是ObservableCollection,因为当您添加或删除集合中的项目时,它将通知控件。

如果您希望能够在它们显示在 UI 中时更改它们的属性,那么这些项目本身(您的 MyContainer 类)也必须实现 INofityPropertyChanged

控件上的依赖属性My 不能是List&lt;T&gt; 类型。它可能应该是类型object,就像ItemsControl.ItemsSource。然后你的控制模板可以在ItemsControl 中显示它,它知道如何处理它。如果ObservableCollection 如我上面建议的那样绑定到它,ItemsControl 将自动更新。在OnMyProperty 中,您的控件类也可以检查它是否也是可观察的集合:

private static void OnMyProperty(DependencyObject d, DependencyPropertyChangedEventArgs e)
{
    UC control = d as UC;

    if (e.NewValue is INotifyCollectionChanged)
    {
        (e.NewValue as INotifyCollectionChanged).CollectionChanged +=
            (s, ecc) => {
                //  Do stuff with UC and ecc.NewItems, ecc.OldItems, etc.
            };
    }
}

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2011-06-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2011-02-08
    • 2012-06-16
    相关资源
    最近更新 更多