Dependency Properties' PropertyChangedCallback not getting called

schoola picture schoola · Oct 5, 2012 · Viewed 8.7k times · Source

If have an own user control with a DependencyProperty and the corresponding callback method like below:

public partial class PieChart : UserControl
{
    public static readonly DependencyProperty RatesProperty = DependencyProperty.Register("Rates", typeof(ObservableCollection<double>), typeof(PieChart),
        new PropertyMetadata(new ObservableCollection<double>() { 1.0, 1.0 }, new PropertyChangedCallback(OnRatesChanged)));

[...]

    public static void OnRatesChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
    {
        ((PieChart)d).drawChart();
    }

When using this user control, I bind an ObservableCollection called "Rates" to the RatesProperty. Rates and the related methods look like this:

private ObservableCollection<double> rates;
public ObservableCollection<double> Rates
{
    get { return this.rates; }
    set
    {
        if (this.rates != value)
        {
            this.rates = value;
            OnPropertyChanged("Rates");
        }
    }
}

public event PropertyChangedEventHandler PropertyChanged;
protected virtual void OnPropertyChanged(string propertyName)
{
    if (PropertyChanged != null)
        PropertyChanged(this, new PropertyChangedEventArgs(propertyName));
}

When I change the ObservableCollection Rates (e.g. with this.Rates = new ObservableCollection<double>() { 1.0, 2.0 }) the OnRatesChanged() method of the user-control is called like expected. But when I execute the following, it is not called:

this.Rates[0] = (double)1;
this.Rates[1] = (double)2;
OnPropertyChanged("Rates");

I expected that when I raise the PropertyChanged event with the correct property name, the corresponding callback in the user control is always called. Is that wrong? I found this thread: Getting PropertyChangedCallback of a dependency property always - Silverlight which covers silverlight but I think then that the same is true in WPF.

So the Framework in the background checks if the bound property (in my example "Rates") changed and only if it changed, it calls the associated callback, correct? Thus changing the elements of my collection has no effect, I always have to change the complete collection?

Thank you!

Answer

Clemens picture Clemens · Oct 5, 2012

Your conclusion is right, your OnRatesChanged callback will only be called when the Rates dependency property is set to a new collection (or null).

In order to get notified about changes in the collection, you would also have to register a NotifyCollectionChangedEventHandler:

private static void OnRatesChanged(
    DependencyObject d, DependencyPropertyChangedEventArgs e)
{
    var pieChart = (PieChart)d;
    var oldRates = e.OldValue as INotifyCollectionChanged;
    var newRates = e.NewValue as INotifyCollectionChanged;

    if (oldRates != null)
    {
        oldRates.CollectionChanged -= pieChart.OnRatesCollectionChanged;
    }

    if (newRates != null)
    {
        newRates.CollectionChanged += pieChart.OnRatesCollectionChanged;
    }

    pieChart.drawChart();
}

private void OnRatesCollectionChanged(
    object sender, NotifyCollectionChangedEventArgs e)
{
    switch (e.Action)
    {
        ...
    }

    drawChart();
}