简体   繁体   中英

How to fire Property Changed Event when it's not actually changing

so I have a model which contains 2 variables, a List and a DateTime. In my UserControl I have a DependencyProperty and I also defined a 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
}

On my form there is a button, which do the changes on the other model variable (on the DateTime).

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

And finally here is my model.

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 declaration is the following.

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

So my problem is the after I hit the run, it fires the OnMyProperty once, after that if I hit the button, it changes the DateTime property well, but the OnMyProperty callback doesn't firing again. However I noticed that if I modify my model like this

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

now it fires it every time when I hit the button. How can I trigger the second behaviour without that modification?

After setting the value of a DependencyProperty it first checks if the new value is different to the old one. Only in this case the PropertyChangedCallback method you registered with that DependencyProperty is called. So the name Property Changed makes sense.

In your (not modified) case you not even try to change My (only Date ). So there is no reason to raise the callback function.

The answer is that you almost certainly do not need to do this. When you ask a question about how to make the framework do something it really does not want to do, always say why you think you need to do that . It's very likely that there's a much easier answer that everybody else is already using.

The only thing you have bound to the control is My . Therefore, if My hasn't changed, then the state of the control should not change. If you want the state of the control to change when Date changes, bind Date to some property of the control. The only way the control should ever get information from any viewmodel is through binding one of its dependency properties to a property of the viewmodel.

The control should not ever know or care who or what is providing values for its properties. It should be able to do its job knowing only the property values it has been given.

If the contents of My have changed -- you added an item or removed one -- of course the control has no way of knowing that, because you refused to tell it. You're just telling it there's a new list. It checks, sees it's still got the same old list, and ignores you. The My property of your viewmodel should be an ObservableCollection , because that will notify the control when you add or remove items in the collection.

The items themselves, your MyContainer class, must implement INofityPropertyChanged as well, if you want to be able to change their properties while they are displayed in the UI.

The dependency property My on your control must not be of type List<T> . It should probably be type object , just like ItemsControl.ItemsSource . Then your control template can display it in an ItemsControl which knows what to do with it. If an ObservableCollection is bound to it as I suggested above, the ItemsControl will update automatically. In OnMyProperty , your control class can check to see if it's an observable collection as well:

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.
            };
    }
}

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM