8

I want to bind a TextBox in the window to a property contained within a class that is a variable of the viewmodel and ensure that INotifyPropertyChanged's PropertyChanged event propagates from the class to the parent.

Let me illustrate with an example:

(Window's DataContext is set to an instance of ViewModel)

public class ViewModel {
    private OtherClass classInstance = new OtherClass();

    public int Attribute {
        get { return classInstance.Attribute; }
    }
}

public class OtherClass : INotifyPropertyChanged {
    private int _attribute;
    public int Attribute {
        get { return _attribute; }
        set { 
            _attribute = value;
            PropertyChanged("Attribute");
        }
    }
    ...
}

The problem in this example is that, when Attribute changes, the bound Textbox does not update the binding since I assume it's listening to the ViewModel's PropertyChanged event and not that of the instance of OtherClass.

Any ideas on how to remedy this situation? I was thinking about chaining OtherClass's INotifyPropertyChanged to that of its parent, but there has to be a better way.

aleph_null
  • 5,766
  • 2
  • 24
  • 39

4 Answers4

7

Why not bind directly to the class property instead of using a proxy?

public class ViewModel {
    private OtherClass classInstance = new OtherClass();

    public OtherClass MyOtherClass {
        get { return classInstance; }
    }
}

Then in your binding you can simply reference the property via the SubClass

{Binding MyOtherClass.Attribute}

A drop dead simple example, but it works!

The Code Behind:

public partial class MainWindow : Window {
   private readonly SomeClass _someClass = new SomeClass();

   public MainWindow() {
      InitializeComponent();
      DataContext = _someClass;
   }
}

public class SomeClass: INotifyPropertyChanged {      
   private readonly SomeSubClass _mySubClass = new SomeSubClass();

   public SomeSubClass MySubClass {
      get { return _mySubClass; }
   }

   private String _name;
   public String Name {
      get { return _name; }
      set {
         _name = value;
         OnPropertyChanged("Name");
      }
   }

   //Property Change Stuff
}

public class SomeSubClass : INotifyPropertyChanged {
   private String _name;
   public String Name {
      get {
         return _name;
      }
      set {
         _name = value;
         OnPropertyChanged("Name");
      }
   }

   //Property Change Stuff
}

The XAML:

<Window x:Class="JWC.Examples.WPF.MainWindow"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        Title="MainWindow">
    <StackPanel>
        <Label Content="Name" VerticalAlignment="Top" />
        <TextBox Text="{Binding Name}" />
        <Label Content="SubClass.Name" />
        <TextBox Text="{Binding MySubClass.Name}" />
        <Label Content="Bound to Name" />
        <TextBlock Text="{Binding Name}" />
        <Label Content="Bound to MySubClass.Name" />
        <TextBlock Text="{Binding MySubClass.Name}" />
    </StackPanel>
</Window>
Josh
  • 44,706
  • 7
  • 102
  • 124
  • Drop dead simple examples are the best kind of examples :) – Moulde Nov 07 '11 at 23:48
  • This isn't practical if SomeSubClass contains a pile of properties. Are you really going to duplicate all those properties? – Sean Mar 13 '17 at 19:20
  • @Sean - I'm not sure what you mean by "duplicate all those properties." In the example it's just coincidence that Name is defined on the parent and the child. It makes no difference to the underlying question. Mainly, you can bind directly to the child class properties instead of trying to use proxy variables. In that sense, there would be zero duplication. – Josh Mar 14 '17 at 00:32
  • 1
    What I mean is I should fully comprehend examples prior to commenting :) I thought you were drilling down to name by creating a proxy in the containing class. Having both properties of the name "Name" tripped me up, apologies. – Sean Mar 15 '17 at 02:08
5

You will need to do something like this:

public class ViewModel {
   public ViewModel() {
      classInstance = new OtherClass();
      classInstance.PropertyChanged += HandleAttributeChanged; 
   }

   private void HandleAttributeChanged(object Sender, NotifyPropertyChangedEventArgs args) {
      PropertyChanged("Attribute");
   }
}

I don't show it here, but you should also implement IDisposable and unsubscribe from the PropertyChanged event on your child, otherwise you will leak memory.

Alternatively you can expose the classInstance as a public property and set your binding to: ViewModel.classInstance. Note this needs to be a property and not the field itself.

CodingGorilla
  • 19,612
  • 4
  • 45
  • 65
0

I think the parent class should subscribe to the child propertyCahnged event..something like:

public class ViewModel
{
    private OtherClass classInstance = new OtherClass();

    public ViewModel()
    {
        classInstance.PropertyChanged += NotifyChildAttributeChanged;
    }
    public int Attribute
    {
        get { return classInstance.Attribute; }
    }
} 

NotifyChildAttributeChanged is basically a method that listens only to the "Attribute" property and fires a PropertyChanged notification of its own..

Of course our parent class must implement INotifyPropertyChanged as well as will all ViewModels (preferably) and your DataContext will detect the change.

Anas Karkoukli
  • 1,342
  • 8
  • 13
0

To get around this you need to implement INotifyPropertyChanged on your view model as well. Just add the interface and the event and the rest will take care of itself - no need to chain the events / calls together.

Check out this for using reflection to get the property as well.

http://tsells.wordpress.com/2011/02/08/using-reflection-with-wpf-and-the-inotifypropertychanged-interface/

tsells
  • 2,751
  • 1
  • 18
  • 20