none
How does the data binding system know when a property is changed?

    Question

  • I thought I had the whole WPF data binding system down cold.  Today I stumbled across something which threw me back out to sea.  According to my understanding of the data binding system in WPF, an object must somehow notify the "outside world" when one of its properties is changed if it wants the new value to appear in the UI.  Either it can raise a PropNameChanged event, implement INotifyPropertyChanged and raise PropertyChanged, or implement the property as a dependency property.  So far, so good.  However...

     

    I was playing around with some code to answer a fellow's question on this forum, and found that a property which has no change notification in place appeared to be "notifying" the binding system that it was updated.  I suspect that this might be some internal housekeeping logic in WPF, but cannot for the life of me remember ever reading about this feature.  Here's my setup:

     

    Code Snippet

    public class Person

    {

     string name;

     public string Name

     {

      get { return name; }

      set { name = value; }

     }

    }

     

    Code Snippet

    <StackPanel>

     <StackPanel.DataContext>

      <local:Person Name="Ludwig" />

     </StackPanel.DataContext>

     

     <TextBlock Text="{Binding Name}" />

     <TextBox Text="{Binding Name}" />

     <Button>Steal Focus</Button>

    </StackPanel>

     

    If you edit the name of the Person in the TextBox and then move input focus to the Button, the TextBlock's Text changes to the new name!  I don't get it.  Does the binding system keep track of all properties bound to the Name property on my Person object and then update them automatically when the property is modified from the UI?  Is this documented somewhere?

     

    Thanks for any tips.

     

    Monday, May 28, 2007 4:58 PM

Answers

  • DataContextChanged Event is raised and it affects all the elements which inherits the datacontext and all the databound properties are updated
    Tuesday, May 29, 2007 12:18 PM
    Moderator
  • I'm not so sure DataContextChanged is responsible, I think the magic lies in the binding system's use of PropertyDescriptor (SetValue presumably raises a ValueChanged - the PropertyDescriptor is likely shared, while the events are raised on a per-object basis).
    Tuesday, May 29, 2007 2:04 PM
  • I'm not at Microsoft, but I can confirm it. If PropertyDescriptor is used to update the value, as it will be, then relevant change notifications are automatically propagated.
    Tuesday, May 29, 2007 5:18 PM
  • lee,

    I've been doing some experiments around this topic.  DataContextChanged is raised when the DataContext property is set on an element, or on an element's ancestor in the logical tree.  It is not raised when a property on the DataContext object is modified.  Here's my experiment:

     

     The data object is of type Person:

    Code Snippet

    public class Person : INotifyPropertyChanged

    {

     string name;

     public string Name

     {

      get { return name; }

      set

      {

       name = value;

       if( this.PropertyChanged != null )

        this.PropertyChanged(

         this, new PropertyChangedEventArgs( "Name" ) );

      }

     }

     public event PropertyChangedEventHandler PropertyChanged;

    }

     

     

    Here's the code which modifies the DataContext:

     

    Code Snippet

    public Window1()

    {

     InitializeComponent();

     

     StackPanel root = this.Content as StackPanel;

     this.DataContextChanged += Window1_DataContextChanged;

     root.DataContextChanged += StackPanel_DataContextChanged;

     

     Person p = new Person();

     this.DataContext = p;

     p.Name = "Josh";

     this.DataContext = null;

     root.DataContext = p;

    }

     

    void Window1_DataContextChanged(

      object sender, DependencyPropertyChangedEventArgs e )

    {

     Debug.WriteLine( "Window: " + e.NewValue );

    }

     

    void StackPanel_DataContextChanged(

      object sender, DependencyPropertyChangedEventArgs e )

    {

     Debug.WriteLine( "StackPanel: " + e.NewValue );

    }

     

    Here's the output of running the app, in the Output window:

     

    Code Snippet

    Window: WPF_Test.Person

    StackPanel: WPF_Test.Person

    Window:

    StackPanel:

    StackPanel: WPF_Test.Person

     

    Notice that the DataContextChanged event handlers are only called when the DataContext property is set, not when the Person's Name property is set to a new value.

    Wednesday, May 30, 2007 12:57 PM

All replies

  • DataContextChanged Event is raised and it affects all the elements which inherits the datacontext and all the databound properties are updated
    Tuesday, May 29, 2007 12:18 PM
    Moderator
  • I have a follow-up question.  If all databound properties are updated when the DataContext changes, why doesn't the NameVersion property below get updated when the Name is updated?

     

    Code Snippet

    public class Person

    {

     int nameVersion = 0;

     string name;

     public string Name

     {

      get { return name; }

      set { name = value; ++this.nameVersion; }

     }

     public int NameVersion

     {

      get { return nameVersion; }

     }

    }

     

    Code Snippet

    <StackPanel>

     <StackPanel.DataContext>

      <local:Person Name="Ludwig" />

     </StackPanel.DataContext>

     

     <TextBlock Text="{Binding NameVersion}" />

     <TextBlock Text="{Binding Name}" />

     <TextBox Text="{Binding Name}" />

     <Button>Steal Focus</Button>

    </StackPanel>

     

    Since NameVersion is incremented when Name is set, shouldn't the new NameVersion value be displayed when the new Name value is displayed?

    Tuesday, May 29, 2007 1:52 PM
  • I'm not so sure DataContextChanged is responsible, I think the magic lies in the binding system's use of PropertyDescriptor (SetValue presumably raises a ValueChanged - the PropertyDescriptor is likely shared, while the events are raised on a per-object basis).
    Tuesday, May 29, 2007 2:04 PM
  • Can you try using a setter, I cannot test it myself now
    Tuesday, May 29, 2007 2:26 PM
    Moderator
  • Thanks Doug.  That sounds right.  Could someone at Microsoft please confirm this?  Thanks.
    Tuesday, May 29, 2007 2:46 PM
  • I'm not at Microsoft, but I can confirm it. If PropertyDescriptor is used to update the value, as it will be, then relevant change notifications are automatically propagated.
    Tuesday, May 29, 2007 5:18 PM
  • Thanks for the confirmation Tim.

    Tuesday, May 29, 2007 5:30 PM
  • Code Snippet

    public class Person : INotifyPropertyChanged

    {

    int nameVersion = 0;

    string name;

    public string Name

    {

    get { return name; }

    set {

    name = value; ++this.nameVersion;

    OnDataChanged("NameVersion");

    }

    }

    public int NameVersion

    {

    get { return nameVersion; }

    }

     

    #region INotifyPropertyChanged Members

    [field:NonSerialized]

    public event PropertyChangedEventHandler PropertyChanged;

    /// <summary>

    /// Called when [data changed].

    /// </summary>

    /// <param name="PropertyName">Name of the property.</param>

    protected virtual void OnDataChanged(string PropertyName)

    {

    if (PropertyChanged != null)

    PropertyChanged(this, new PropertyChangedEventArgs(PropertyName));

    }

    #endregion

    }

     

    Tuesday, May 29, 2007 6:39 PM
  • Spaccabit,

     

    How to implement property change notification is not the topic of this thread. Anyways, thanks for the snippet.

    Tuesday, May 29, 2007 7:01 PM
  • maby there is "by reference optimization" try this

     

    Code Snippet

    public string Name

     {

      get { return (string)name.Clone(); }

      set { name = value; ++this.nameVersion; }

     }

     

    Wednesday, May 30, 2007 3:58 AM
  • Thanks arkhivania.  As Doug pointed out earlier in this thread, the behavior I mentioned can be traced back to the binding system's use of PropertyDescriptors to set the value on the data object.  Since a PropertyDescriptor is mostly concerned with a property, not the property's value, cloning the Name string makes no difference.
    Wednesday, May 30, 2007 4:45 AM
  • just to make sure that I understand this DataContext thing, what causes DataContextChanged event to be fired and what needs to change
    Wednesday, May 30, 2007 8:48 AM
    Moderator
  • lee,

    I've been doing some experiments around this topic.  DataContextChanged is raised when the DataContext property is set on an element, or on an element's ancestor in the logical tree.  It is not raised when a property on the DataContext object is modified.  Here's my experiment:

     

     The data object is of type Person:

    Code Snippet

    public class Person : INotifyPropertyChanged

    {

     string name;

     public string Name

     {

      get { return name; }

      set

      {

       name = value;

       if( this.PropertyChanged != null )

        this.PropertyChanged(

         this, new PropertyChangedEventArgs( "Name" ) );

      }

     }

     public event PropertyChangedEventHandler PropertyChanged;

    }

     

     

    Here's the code which modifies the DataContext:

     

    Code Snippet

    public Window1()

    {

     InitializeComponent();

     

     StackPanel root = this.Content as StackPanel;

     this.DataContextChanged += Window1_DataContextChanged;

     root.DataContextChanged += StackPanel_DataContextChanged;

     

     Person p = new Person();

     this.DataContext = p;

     p.Name = "Josh";

     this.DataContext = null;

     root.DataContext = p;

    }

     

    void Window1_DataContextChanged(

      object sender, DependencyPropertyChangedEventArgs e )

    {

     Debug.WriteLine( "Window: " + e.NewValue );

    }

     

    void StackPanel_DataContextChanged(

      object sender, DependencyPropertyChangedEventArgs e )

    {

     Debug.WriteLine( "StackPanel: " + e.NewValue );

    }

     

    Here's the output of running the app, in the Output window:

     

    Code Snippet

    Window: WPF_Test.Person

    StackPanel: WPF_Test.Person

    Window:

    StackPanel:

    StackPanel: WPF_Test.Person

     

    Notice that the DataContextChanged event handlers are only called when the DataContext property is set, not when the Person's Name property is set to a new value.

    Wednesday, May 30, 2007 12:57 PM
  • Thanks. I would tend to think from the name of the event  it gives the impression that it will be called if there is a change in object to which is bound also. obviously thats not the case
    Wednesday, May 30, 2007 1:20 PM
    Moderator
  • I'm not from microsoft either, but I can surely confirm that.

    Try the code bellow.

     

    Code Block

    public class Employee

    {

    public string Name { get; set; }

    }

     

    <Window x:Class="DataBinding.Window1"

    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"

    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"

    xmlns:local="clr-namespaceBig SmileataBinding"

    Title="Window1" Height="300" Width="300">

     

    <StackPanel>

    <StackPanel.DataContext>

    <local:Employee x:Name="_hitechSlave" Name="Tomer Shamam" />

    </StackPanel.DataContext>

     

    <TextBlock Text="{Binding Name}" />

    <TextBox Text="{Binding Name}" />

    <Button Content="{Binding Name}" Click="Button_Click" />

    </StackPanel>

    </Window>

     

    public partial class Window1 : Window

    {

    public Window1()

    {

    InitializeComponent();

    PropertyDescriptorCollection properties = TypeDescriptor.GetProperties(_hitechSlave);

    PropertyDescriptor nameProperty = properties[0];

    nameProperty.AddValueChanged(_hitechSlave, OnPropertyChanged);

    }

     

    void OnPropertyChanged(object sender, EventArgs e)

    {

    MessageBox.Show("Property Employee.Name Changed!");

    }

     

    void Button_Click(object sender, RoutedEventArgs e)

    {

    _hitechSlave.Name = string.Empty;

    }

    }

     

    As you can see, when changing the Name property through binding by changing the TextBox.Text property, the PropertyDescriptor of the Employee.Name property raises the event, but this is not the case when changing it directly through the Name property itself.

    • Proposed as answer by Nuno31415 Monday, July 28, 2008 3:35 PM
    Tuesday, January 01, 2008 1:22 PM