当物业改变事件没有实际改变时,如何触发它

时间:2016-08-08 11:50:20

标签: c# wpf data-binding viewmodel

所以我有一个包含2个变量的模型,一个List和一个DateTime。在我的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");
    }
}

现在每次按下按钮时它都会触发它。如何在没有修改的情况下触发第二种行为?

2 个答案:

答案 0 :(得分:1)

设置DependencyProperty的值后,首先检查新值是否与旧值不同。只有在这种情况下才会调用您在PropertyChangedCallback注册的DependencyProperty方法。所以名称Property Changed 是有道理的。

在您的(未修改)案例中,您甚至不尝试更改My(仅Date)。所以没有理由提出回调函数。

答案 1 :(得分:0)

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

你唯一能控制的是My。因此,如果My未更改,则控件的状态不应更改。如果希望在Date更改时更改控件的状态,请将Date绑定到控件的某个属性。控件应该从任何viewmodel获取信息的 only 方式是通过将其依赖属性之一绑定到viewmodel的属性。

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

如果My的内容发生了变化 - 你添加了一个项目或删除了一个项目 - 当然控制无法知道这一点,因为你拒绝告诉它。你只是告诉它有一个新的清单。它检查,看到它仍然有相同的旧列表,并忽略你。您的viewmodel的My属性应该是ObservableCollection,因为这会在您添加或删除集合中的项目时通知控件。

项目本身,即MyContainer类,如果您希望能够在UI中显示属性时更改其属性,则必须实现INofityPropertyChanged

控件上的依赖项属性My不得为List<T>类型。它应该是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.
            };
    }
}