Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

WPF INotifyPropertyChanged for linked read-only properties

I am trying to understand how to update the UI if I have a read-only property that is dependent on another property, so that changes to one property update both UI elements (in this case a textbox and a read-only textbox. For example:

public class raz : INotifyPropertyChanged
{

  int _foo;
  public int foo
  {
    get
    {
      return _foo;
    }
    set
    {
      _foo = value;
      onPropertyChanged(this, "foo");
    }
  }

  public int bar
  {
    get
    {
      return foo*foo;
    }
  }

  public raz()
  {

  }

  public event PropertyChangedEventHandler PropertyChanged;
  private void onPropertyChanged(object sender, string propertyName)
  {
    if(this.PropertyChanged != null)
    {
      PropertyChanged(sender, new PropertyChangedEventArgs(propertyName));
    }
  }
}

My understanding is that bar will not automatically update the UI when foo is modified. Whats the correct way to do this?

like image 480
tbischel Avatar asked Aug 26 '10 18:08

tbischel


2 Answers

I realize this is an old question, but it's the first Google result of "NotifyPropertyChanged of linked properties", so I think it's appropriate to add this answer so that there's some concrete code.

I used Robert Rossney's suggestion and created a custom attribute, then used it in a base view model's PropertyChanged event.

The attribute class:

[AttributeUsage(AttributeTargets.Property,AllowMultiple = true)]
public class DependsOnPropertyAttribute : Attribute
{
    public readonly string Dependence;

    public DependsOnPropertyAttribute(string otherProperty)
    {
        Dependence = otherProperty;
    }
}

And in my base view model (which all other WPF view models inherit from):

public abstract class BaseViewModel : INotifyPropertyChanged
{
    protected Dictionary<string, List<string>> DependencyMap;

    protected BaseViewModel()
    {
        DependencyMap = new Dictionary<string, List<string>>();

        foreach (var property in GetType().GetProperties())
        {
            var attributes = property.GetCustomAttributes<DependsOnPropertyAttribute>();
            foreach (var dependsAttr in attributes)
            {
                if (dependsAttr == null)
                    continue;

                var dependence = dependsAttr.Dependence;
                if (!DependencyMap.ContainsKey(dependence))
                    DependencyMap.Add(dependence, new List<string>());
                DependencyMap[dependence].Add(property.Name);
            }
        }
    }

    public event PropertyChangedEventHandler PropertyChanged;
    protected virtual void OnPropertyChanged([CallerMemberName] string propertyName = null)
    {
        var handler = PropertyChanged;
        if (handler == null)
            return;

        handler(this, new PropertyChangedEventArgs(propertyName));

        if (!DependencyMap.ContainsKey(propertyName))
            return;

        foreach (var dependentProperty in DependencyMap[propertyName])
        {
            handler(this, new PropertyChangedEventArgs(dependentProperty));
        }
    }
}

This now allows me to mark properties easily, like so:

public int NormalProperty
{
    get {return _model.modelProperty; }
    set 
    {
        _model.modelProperty = value;
        OnPropertyChanged();
    }
}

[DependsOnProperty(nameof(NormalProperty))]
public int CalculatedProperty
{
    get { return _model.modelProperty + 1; }
}
like image 176
Mage Xy Avatar answered Sep 21 '22 15:09

Mage Xy


One way to indicate that bar has changed is to add a call to onPropertyChanged(this, "bar") in the foo setter. Ugly as hell, I know, but there you have it.

If foo is defined in an ancestor class or you otherwise don't have access to the implementation of the setter, I suppose you could subscribe to the PropertyChanged event so that when you see a "foo" change, you can also fire a "bar" change notification. Subscribing to events on your own object instance is equally ugly, but will get the job done.

like image 28
dthorpe Avatar answered Sep 19 '22 15:09

dthorpe