views:

182

answers:

2

When using databinding in WPF, the target dependency object gets updated when it is notified that the source has changed through the INotifyPropertyChanged interface.

For example:

<TextBlock Text="{Binding Path=SomeField}"/>

The text field will change to correctly reflect the value of SomeField whenever PropertyChanged(this, new PropertyChangedEventArgs("SomeField")) is called from the source.

What if I use a complex path like the following:

<TextBlock Text="{Binding Path=SomeObjField.AnotherField}"/>

Will the text field get updated for PropertyChanged(this, new PropertyChangedEventArgs("SomeObjField")) on the source?

What about PropertyChanged(this, new PropertyChangedEventArgs("AnotherField")) on the intermediate object (the object contained within the SomeObjField)?

Source objects and fields are NOT dependency objects or properties! Assume that the property/classes are implemented something like the following:

public class Data : INotifyPropertyChanged
{
   // INotifyPropertyChanged implementation...

   public string SomeField
   {
      get { return val; }
      set
      {
         val = value;
         // fire PropertyChanged()
      }
   }

   public SubData SomeObjField
   {
      get { return val; }
      set
      {
         val = value;
         // fire PropertyChanged()
      }
   }   
}

public class SubData : INotifyPropertyChanged
{
   // INotifyPropertyChanged implementation...

   public string AnotherField
   {
      get { return val; }
      set
      {
         val = value;
         // fire PropertyChanged()
      }
   }
}
A: 

I'm not 100% sure what your asking with the PropertyChanged part of the question. But if the properties involved are all DependencyProperty backed properties then this should work as expected. I drew up the following example

Window1.xaml

<Window x:Class="WpfApplication1.Window1"
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
    Title="Window1" Height="300" Width="300">
    <StackPanel Name="m_panel">
        <TextBlock Text="{Binding Path=SomeField}" />
        <TextBlock Text="{Binding Path=SomeField.AnotherField }"  />
        <Button Click="Button_Click">Update Root Object</Button>
        <Button Click="Button_Click_1">Update Another Field</Button>
    </StackPanel>
</Window>

Window1.xaml.cs

public partial class Window1 : Window
{
    public Window1()
    {
        InitializeComponent();
        m_panel.DataContext = new Class1();
    }

    private void Button_Click(object sender, RoutedEventArgs e)
    {
        ((Class1)m_panel.DataContext).SomeField = new Class2();
    }

    private void Button_Click_1(object sender, RoutedEventArgs e)
    {
        ((Class1)m_panel.DataContext).SomeField.AnotherField = "Updated field";
    }
}

And the classes

public class Class1 : DependencyObject
{
    public static DependencyProperty SomeFieldProperty = DependencyProperty.Register(
        "SomeField",
        typeof(Class2),
        typeof(Class1));

    public Class2 SomeField
    {
        get { return (Class2)GetValue(SomeFieldProperty); }
        set { SetValue(SomeFieldProperty, value); }
    }

    public Class1()
    {
        SomeField = new Class2();
    }
}

public class Class2 : DependencyObject
{
    public static DependencyProperty AnotherFieldProperty = DependencyProperty.Register(
        "AnotherField",
        typeof(string),
        typeof(Class2));

    public string AnotherField
    {
        get { return (string)GetValue(AnotherFieldProperty); }
        set { SetValue(AnotherFieldProperty, value); }
    }

    public Class2()
    {
        AnotherField = "Default Value";
    }
}
JaredPar
Only the target property (TextBlock.Text in this case) can be assumed to be a dependency property. SomeField, SomeObjField, and AnotherField are not. If they were, there would be no need to implement the INotifyPropertyChanged interface.
Josh G
Thanks for the post. I followed your example and created a simple test project. I was hoping someone would know the answer off of the top of their head.
Josh G
A: 

After further investigation, it appears that when any part of the complex path sends a change notification the binding is updated. Thus, if the source object OR the intermediate object is changed the binding will be updated.

I built a test project like Jared's:

<StackPanel Name="m_panel">
    <TextBox IsReadOnly="True" Text="{Binding Path=SomeObjField.AnotherField }"  />
    <TextBox x:Name="field1"/>
    <Button Click="Button1_Click">Edit Root Object</Button>
    <TextBox x:Name="field2"/>
    <Button Click="Button2_Click">Edit Sub Object</Button>
</StackPanel>

And the code behind:

public Window1()
{
    InitializeComponent();
    m_panel.DataContext = new Data();
}

private void Button1_Click(object sender, RoutedEventArgs e)
{
    Data d = m_panel.DataContext as Data;
    d.SomeObjField = new SubData(field1.Text);
}

private void Button2_Click(object sender, RoutedEventArgs e)
{
    Data d = m_panel.DataContext as Data;
    d.SomeObjField.AnotherField = field2.Text;
}

I am using the basic data implementation that I provided in the question.

Josh G