Michbeckable
Michbeckable

Reputation: 1891

What triggers change notification in DependencyProperties?

I am exploring databinding in C# and want to understand what kind of changes to a property (backed by a dependency property) actually trigger an update notification to bound targets.

For testing I use a simple setup of classes where I can test changes to nested properties (sub-properties):

// Person with a name
public class Person
{
    public String Name { get; set; }
}

// Class giving the best friend as DependencyProperty
public class Friends : DependencyObject
{
    public static DependencyProperty BestFriendProperty =
        DependencyProperty.Register("BestFriend", typeof(Person), typeof(Friends));

    public Person BestFriend
    {
        get { return (Person)this.GetValue(BestFriendProperty); }
        set { this.SetValue(BestFriendProperty, value); }        
    }
}

Now I bind the BestFriendProperty (as source) to some Person property of another class. I expected that update notifications via this binding only works if I use the setter of BestFriend:

myBoundFriends.BestFriend = new Person(); // myBoundFriends is of type Friends

But I discovered that even a direct change of the (nested) Name Property triggers a notification and synchronisation to the bound target works:

myBoundFriends.BestFriend.Name = "Otto"; // why does this trigger update?

The databinding seems some kind of mysterious to me. Actually I thought that only Freezable objects (used extensively in WPF) are capable of triggering update when any of its sub-properties changes?!


Edit: The notification mechanism works as expected when I add a simple Person to Person converter to the binding:

public class PersonPersonConverter : IValueConverter
{
    public object Convert(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture)
    {
        Person p = (Person)value;
        return new Person(String.Copy(p.Name));
    }

    public object ConvertBack(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture)
    {
        Person p = (Person)value;
        return new Person(String.Copy(p.Name));
    }
}

Notice that the converter does a deep copy of the Person object by copying the Name string. I guess this might be a relevant point for the solution.

Upvotes: 0

Views: 279

Answers (1)

Stígandr
Stígandr

Reputation: 2902

Here is your problem, your datamodel does not implement the INotifyPropertyChanged interface.

public class Person
{
    public String Name { get; set; }
}

Change it to

public class Person : INotifyPropertyChanged
{
    private string name;

    public String Name
    {
        get { return name; }
        set
        {
            if (value == name) return;
            name = value;
            OnPropertyChanged();
        }
    }

    public event PropertyChangedEventHandler PropertyChanged;

    [NotifyPropertyChangedInvocator] // Comment out this attribute if you don't have R#
    protected virtual void OnPropertyChanged([CallerMemberName] string propertyName = null)
    {
        PropertyChangedEventHandler handler = PropertyChanged;
        if (handler != null) handler(this, new PropertyChangedEventArgs(propertyName));
    }
}

This will signal the GUI to redraw anything that's bound to your Name property, when it changes in your code. Alternativly you may use DO as a baseclass and make the Name property a DP, but that's just overkill. I usually have a baseclass that implements the interface for both my viewmodels and datamodels.

And yes I am a resharper addict ;)

Upvotes: 0

Related Questions