-
Notifications
You must be signed in to change notification settings - Fork 6
/
Copy pathNotifyPropertyChangedBase.cs
123 lines (95 loc) · 3.49 KB
/
NotifyPropertyChangedBase.cs
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Diagnostics;
using System.Linq;
using System.Linq.Expressions;
using System.Runtime.Serialization;
using System.Windows;
namespace ProverbTeleprompter
{
public abstract class NotifyPropertyChangedBase : DependencyObject, INotifyPropertyChanged
{
public event PropertyChangedEventHandler PropertyChanged;
#region Property Change Helpers
/// <summary>
/// Call when a property has been changed
/// </summary>
/// <param name="property">THe name of the property that has changed</param>
protected virtual void Changed(string property)
{
VerifyPropertyName(property);
if (PropertyChanged != null)
{
foreach (var propertyName in AllNotifiedProperties(property))
PropertyChanged(this, new PropertyChangedEventArgs(propertyName));
}
}
/// <summary>
/// Allows for strongly typed property change events (instead of hardcoding strings)
/// </summary>
/// <typeparam name="TObject">The type of object the property belongs</typeparam>
/// <param name="propertySelector">an expression specifying the property (i.e. </param>
public void Changed<TObject>(Expression<Func<TObject>> propertySelector)
{
if (PropertyChanged != null)
{
var memberExpression = propertySelector.Body as MemberExpression;
if (memberExpression != null)
{
Changed(memberExpression.Member.Name);
}
}
}
private IEnumerable<string> DependantProperties(string inputName)
{
return from property in GetType().GetProperties()
where property.GetCustomAttributes(typeof(DependsUponAttribute), true).Cast<DependsUponAttribute>()
.Any(attribute => attribute.DependancyName == inputName)
select property.Name;
}
private IEnumerable<string> NotifiedProperties(IEnumerable<string> inputs)
{
var dependancies = from input in inputs
from dependancy in DependantProperties(input)
select dependancy;
return inputs.Union(dependancies).Distinct();
}
private IEnumerable<string> AllNotifiedProperties(string inputName)
{
IEnumerable<string> results = new[] { inputName };
while (NotifiedProperties(results).Count() > results.Count())
results = NotifiedProperties(results);
return results;
}
#region Debugging Aides
/// <summary>
/// Warns the developer if this object does not have
/// a public property with the specified name. This
/// method does not exist in a Release build.
/// </summary>
[Conditional("DEBUG")]
[DebuggerStepThrough]
public void VerifyPropertyName(string propertyName)
{
// Verify that the property name matches a real,
// public, instance property on this object.
if (TypeDescriptor.GetProperties(this)[propertyName] == null)
{
string msg = "Invalid property name: " + propertyName;
if (ThrowOnInvalidPropertyName)
throw new Exception(msg);
Debug.Fail(msg);
}
}
/// <summary>
/// Returns whether an exception is thrown, or if a Debug.Fail() is used
/// when an invalid property name is passed to the VerifyPropertyName method.
/// The default value is false, but subclasses used by unit tests might
/// override this property's getter to return true.
/// </summary>
protected virtual bool ThrowOnInvalidPropertyName { get; private set; }
#endregion // Debugging Aides
#endregion PropertyChanged helpers
}
}