1

I have the following class as my DataContext of my UserControl:

public class ModelBase<T> : INotifyPropertyChanged where T : class
{
    public T Model { get; set; }

    public void UpdateUI()
    {
        OnPropertyChanged(string.Empty);
    }

    public event PropertyChangedEventHandler PropertyChanged;

    protected virtual void OnPropertyChanged(string propertyName)
    {
        PropertyChangedEventHandler handler = PropertyChanged;
        if (handler != null)
          handler(this, new PropertyChangedEventArgs(propertyName));
    }
}

I am setting Model as an arbitrary class the contains primitive types. I seem to have the binding done correctly, because I can see that the properties are being populated as I change them in the UI. But the problem is that when I change the properties from code behind, it won't update the view with it, even after calling UpdateUI(). I verified the properties in the DataContext of the UI (with WPF/XAML inspection software) and they have the correct values.

I believe it has something to do with the fact that it's a nested class inside the DataContext, because I tried adding properties to ModelBase to test it, and the bindings worked fine when I called UpdateUI().

I'm creating the controls/bindings and adding it to the UserControl in the code behind, I'm not sure if this would cause a problem:

var textBox = new TextBox();

// Setup Binding
var binding = new Binding
{
    Source = myModelBase.Model,
    Path = new PropertyPath(nameOfProperty),
    Mode = BindingMode.TwoWay,
    UpdateSourceTrigger = UpdateSourceTrigger.PropertyChanged
};
BindingOperations.SetBinding(textBox, TextBox.TextProperty, binding);

myUserControl.Content.Children.Add(textBox);
Drake
  • 2,679
  • 4
  • 45
  • 88
  • Why is this `OnPropertyChanged(string.Empty);`. Shouldn't it be property name as parameter? – Nikhil Agrawal Mar 29 '15 at 04:36
  • Yes, I did that so it would update everything it possibly can on the UI, for now...I'm planning on changing it after I figure out the problem :) – Drake Mar 29 '15 at 04:40
  • If you provide property name it will update that property. – Nikhil Agrawal Mar 29 '15 at 04:43
  • If the `Model` class does not implement `INotifyPropertyChanged`, then this probably will not work. I think the binding detects nested objects and attempts to subscribe to INPC. So if the nested object doesn't implement INPC, it will not work. – Ryan Mar 29 '15 at 05:01

1 Answers1

1

To have TwoWay binding you will have to have the following:

  • backing fields

  • public properties in which the setters raise a property change notification.

  • a Model that implements INotifyPropertyChanged interface or inherits from a class that implements it

Here is a nice way to do it:

rewrite your ModelBase to be (based on Implementing INotifyPropertyChanged - does a better way exist?):

public class ModelBase : INotifyPropertyChanged
{
    public event PropertyChangedEventHandler PropertyChanged;

    protected virtual void OnPropertyChanged(string propertyName)
        {
            PropertyChangedEventHandler handler = PropertyChanged;
            if (handler != null) handler(this, new PropertyChangedEventArgs(propertyName));
        }


     protected bool SetField<T>(ref T field, T value, [CallerMemberName] string propertyName = null)
        {
            //make sure we fire the event only when newvalue!=oldvalue
            if (EqualityComparer<T>.Default.Equals(field, value)) return false;
            field = value;
            OnPropertyChanged(propertyName);
            return true;
        }
}

and use it as

class Model:ModelBase{
   //example for a property that is appropriate for 2 way bidning
   private string _prop;
   public string prop{
      get{return _prop;}
      set{SetField(ref _prop,value);}
   }
}

if this is not sufficient please explain why it is not.

Update:based on what you want, you are doing it almost right your way but its just that you are not specifying the PropertyPath correctly, it should be "Model."+nameOfProperty and notice that you don't need to set the Source = myModelBase unless your container DataContext is not set to it.

So:

var binding = new Binding
            {
                Path = new PropertyPath("Model."+nameOfProperty),
                Mode = BindingMode.Default,
                UpdateSourceTrigger = UpdateSourceTrigger.PropertyChanged
            };

Or :

var binding = new Binding
                {
                    Source = myModelBase,
                    Path = new PropertyPath("Model."+nameOfProperty),
                    Mode = BindingMode.Default,
                    UpdateSourceTrigger = UpdateSourceTrigger.PropertyChanged
                };

not sure if that will work but its probably the problem, otherwise your idea is fine.

Community
  • 1
  • 1
Xi Sigma
  • 2,292
  • 2
  • 13
  • 16
  • It is not sufficient because I won't know what properties the model will have. What I'm trying to do, is create XAML based on a given data/model class during runtime, with all necessary bindings. So if a class T has 2 strings and 1 bool, I would need to inject 2 texboxes and 1 checkbox into my UserControl, and take care of all the bindings and PropertyChange events. – Drake Mar 29 '15 at 06:48
  • I'm not sure if the PropertyPath is the problem, because the binding is currently working OneWay, but I'll try it first thing tomorrow morning when I get back to my office. – Drake Mar 29 '15 at 16:18