A nicer way to handle dependent values on PropertyChanged
Here’s a contrived example of how PropertyChanged can get difficult. The property Total depends on Values A and B, and if any of them change, Total has to be read again.
public int Total
{
get { return A + B; }
}
public int A
{
get { return m_A; }
set { m_A = value; RaisePropertyChanged("A"); RaisePropertyChanged("Total"); }
}
public int B
{
get { return m_B: }
set { m_B = value; RaisePropertyChanged("B"); RaisePropertyChanged("Total"); }
}
We can solve this by using a helper attribute, so that the new code looks like this. Notice the increased clarity and ease of adding more dependent variables:
[DependsOn("A")]
[DependsOn("B")]
public int Total
{
get { return A + B; }
}
public int A
{
get { return m_A; }
set { m_A = value; RaisePropertyChanged("A"); }
}
public int B
{
get { return m_B: }
set { m_B = value; RaisePropertyChanged("B"); }
}
Here’s the supporting code
internal Dictionary<string, IEnumerable<string>> m_DependentAttributes = new Dictionary<string, IEnumerable<string>>();
private void SetupDependentAttributes()
{
foreach (var property in this.GetType().GetProperties())
{
string propertyName = property.Name;
DependsOnAttribute[] attrs = (DependsOnAttribute[])property.GetCustomAttributes(typeof(DependsOnAttribute), true);
foreach (var dependOnAttr in attrs)
{
foreach (string dependsOnPropertyName in dependOnAttr.DependsOnPropertyNames)
{
if (!m_DependentAttributes.ContainsKey(dependsOnPropertyName))
m_DependentAttributes[dependsOnPropertyName] = new List<string>();
(m_DependentAttributes[dependsOnPropertyName] as List<string>).Add(propertyName);
}
}
// If a property is ObservableCollection, then CollectionChanged should also raise PropertyChanged.
object obs = property.GetGetMethod().Invoke(this, null);
if (obs != null && obs is System.Collections.Specialized.INotifyCollectionChanged)
{
EventInfo collectionChanged = obs.GetType().GetEvent("CollectionChanged");
if (collectionChanged != null)
{
(obs as System.Collections.Specialized.INotifyCollectionChanged).CollectionChanged +=
(s, e) => { RaisePropertyChanged(propertyName); };
}
}
}
}
public void RaisePropertyChanged(string propertyName)
{
if (PropertyChanged != null)
{
PropertyChanged(this, new PropertyChangedEventArgs(propertyName));
if (m_DependentAttributes.ContainsKey(propertyName))
foreach (var dependentPropertyName in m_DependentAttributes[propertyName])
{
RaisePropertyChanged(dependentPropertyName);
}
}
}
[AttributeUsage(AttributeTargets.Property, Inherited = false, AllowMultiple = true)]
sealed class DependsOnAttribute : Attribute
{
// See the attribute guidelines at
// http://go.microsoft.com/fwlink/?LinkId=85236
readonly string[] dependsOnPropertyNames;
// This is a positional argument
public DependsOnAttribute(params string[] dependsOnPropertyNames)
{
this.dependsOnPropertyNames = dependsOnPropertyNames;
}
public string[] DependsOnPropertyNames
{
get { return dependsOnPropertyNames; }
}
// This is a named argument
public int NamedInt { get; set; }
}
About this entry
You’re currently reading “ A nicer way to handle dependent values on PropertyChanged ,” an entry on Chui's Counterpoint
- Published:
- 1.17.12 / 7am
- Category:
- .Net
Comments are closed
Comments are currently closed on this entry.