How does the data binding system know when a property is changed?
-
Monday, May 28, 2007 4:58 PM
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 Snippetpublic
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.
All Replies
-
Tuesday, May 29, 2007 12:18 PMModerator
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 1:52 PM
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 Snippetpublic
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 2:04 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:26 PMModeratorCan you try using a setter, I cannot test it myself now
-
Tuesday, May 29, 2007 2:46 PMThanks Doug. That sounds right. Could someone at Microsoft please confirm this? Thanks.
-
Tuesday, May 29, 2007 5:18 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:30 PM
Thanks for the confirmation Tim.
-
Tuesday, May 29, 2007 6:39 PMCode 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 7:01 PM
Spaccabit,
How to implement property change notification is not the topic of this thread. Anyways, thanks for the snippet.
-
Wednesday, May 30, 2007 3:58 AM
maby there is "by reference optimization" try this
Code Snippetpublic string Name
{
get { return (string)name.Clone(); }
set { name = value; ++this.nameVersion; }
}
-
Wednesday, May 30, 2007 4:45 AMThanks 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 8:48 AMModeratorjust 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 12:57 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 Snippetpublic
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 Snippetpublic
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 SnippetWindow: 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 1:20 PMModeratorThanks. 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
-
Tuesday, January 01, 2008 1:22 PM
I'm not from microsoft either, but I can surely confirm that.
Try the code bellow.
Code Blockpublic
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"
:local="clr-namespacexmlns
ataBinding"
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(){
PropertyDescriptorCollection properties = TypeDescriptor.GetProperties(_hitechSlave); PropertyDescriptor nameProperty = properties[0];InitializeComponent();
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

