WPF INotifyPropertyChanged for read-only related properties

I'm trying to figure out how to update an interface if I have a read-only property that depends on another property, so changes to one property update both interface elements (in this case, a text field and a read-only text field 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)); } } } 

I understand that the panel will not automatically update the user interface when foo changes. What is the right way to do this?

+7
c # wpf inotifypropertychanged
source share
7 answers

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

If foo is defined in the ancestor class or you don’t have access to the setter implementation, I suppose you could subscribe to the PropertyChanged event so that when you change β€œfoo” you can also trigger a notification about the change to β€œbar”. Subscribing to events on your own instance of an object is equally ugly, but will do the job.

+6
source share

I understand that this is an old question, but this is the first Google result "NotifyPropertyChanged of linked properties", so I think it is advisable to add this answer so that there is a specific code.

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

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)); } } } 

Now this allows me to easily label properties:

 public int NormalProperty { get {return _model.modelProperty; } set { _model.modelProperty = value; OnPropertyChanged(); } } [DependsOnProperty(nameof(NormalProperty))] public int CalculatedProperty { get { return _model.modelProperty + 1; } } 
+9
source share

If this is a serious problem ("seriously", I mean that you have a non-trivial number of read-only dependent properties), you can create a map of property dependencies, for example:

 private static Dictionary<string, string[]> _DependencyMap = new Dictionary<string, string[]> { {"Foo", new[] { "Bar", "Baz" } }, }; 

and then reference it in OnPropertyChanged:

 PropertyChanged(this, new PropertyChangedEventArgs(propertyName)) if (_DependencyMap.ContainsKey(propertyName)) { foreach (string p in _DependencyMap[propertyName]) { PropertyChanged(this, new PropertyChangedEventArgs(p)) } } 

This is not inherently very different from just adding a few OnPropertyChanged calls to the Foo installer, since you need to update the dependency map for each dependent property you add.

But this allows subsequently to implement PropertyChangeDependsOnAttribute and use reflection to scan the type and build the dependency map. So your property will look something like this:

 [PropertyChangeDependsOn("Foo")] public int Bar { get { return Foo * Foo; } } 
+8
source share

You can just call

 OnPropertyChanged(this, "bar"); 

from anywhere in this class ... It’s cold even with you:

  public raz() { this.PropertyChanged += new PropertyChangedEventHandler(raz_PropertyChanged); } void raz_PropertyChanged(object sender, PropertyChangedEventArgs e) { if(e.PropertyName == "foo") { onPropertyChanged(this, "bar"); } } 
+5
source share

If you use only the panel for UI purposes, you can completely remove it from your model. You can bind the user interface element to the foo property and use a custom value converter to change the result from foo to foo * foo.

In WPF, there are often many ways to do the same. Often there is the wrong way, only personal preference.

+2
source share

Depending on the expense of the calculation and how often you expect to use it, it may be useful to make it a private set of properties and calculate the value when foo set, rather than calculate on the fly when bar get is called. This is basically a caching solution, and you can make a property change notice as part of a bar private setter. I usually prefer this approach, mainly because I use AOP (via Postsharp) to implement the actual INotifyPropertyChanged template.

Dan

0
source share

I am sure that this should be possible in a declarative way, but my first attempt to solve this problem was unsuccessful. The solution I want to execute is using Lambda Expressions to determine this. Because expressions can be parsed (??), it should be possible to parse expressions and attach NotifyPropertyChanged to all events to receive notifications of dependent data changes.

in ContinuousLinq, this works great for collections.

 private SelfUpdatingExpression<int> m_fooExp = new SelfUpdatingExpression<int>(this, ()=> Foo * Foo); public int Foo { get { return m_fooExp.Value; } } 

but, unfortunately, I lack fundamental knowledge in expressions and Linq :(

0
source share

All Articles