-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathNotifyingValue.cs
More file actions
232 lines (214 loc) · 8.23 KB
/
Copy pathNotifyingValue.cs
File metadata and controls
232 lines (214 loc) · 8.23 KB
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
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
using System.Collections.Generic;
namespace BaseLib
{
/// <summary>
/// Generic value that sends ValuePreChangeEvent before and ValueChanged event
/// after the value is changed.
///
/// For value types uses value.Equals() for comparisons.
/// For reference types tests reference equality (null == null) then value.Equals().
///
/// Don't need for IObserved, IPropertyNotifying container types that
/// aren't changed directly but contain fields that do change.
/// </summary>
/// <typeparam name="T"></typeparam>
public class NotifyingValue<T> : BaseCloseable, ICloseable
{
public new const string ClassName = "NotifyingValue<T>";
public delegate void ValueChangedEventHandler(object sender, T newValue);
/// <summary> Value is about to change </summary>
public event ValueChangedEventHandler ValuePreChangeEvent;
/// <summary> Value has just changed </summary>
public event ValueChangedEventHandler ValueChanged;
private bool _enableNotify;
private readonly bool _isValueType = typeof(T).IsValueType;
private T _value;
/// <summary> Get / set the value.
/// If the new value does not match the old value (null == null, null != any
/// non-null value) then:
/// <list type="bullet">
/// <item><description>ValuePreChangeEvent event is fired if enableNotify is true</description></item>
/// <item><description>the value is updated</description></item>
/// <item><description>ValueChanged event is fired if enableNotify is true</description></item>
/// </list>
/// When modifying the value without using property set (e.g. for a Point3D using Value.SetTo()) you
/// must use .ForceNotify() to cause the events.
/// </summary>
public virtual T Value
{
get { return _value; }
set
{
// Attempt to properly handle value vs. reference types with minimal overhead.
if (_isValueType
? !value.Equals(_value)
// ReSharper disable CompareNonConstrainedGenericWithNull
: ((null == value) && (null != _value)) ||
((null != value) && (null == _value)) ||
((null != value) && (null != _value) && !value.Equals(_value)))
// ReSharper restore CompareNonConstrainedGenericWithNull
{
if (_enableNotify && (null != ValuePreChangeEvent))
{
ValuePreChangeEvent(this, value);
}
_value = value;
if (_enableNotify && (null != ValueChanged))
{
ValueChanged(this, value);
}
}
}
}
/// <summary> REALLY test-only </summary>
public ValueChangedEventHandler TestOnlyValuePreChangeEvent
{
get { return ValuePreChangeEvent; }
}
/// <summary> REALLY test-only </summary>
public ValueChangedEventHandler TestOnlyValueChangedEvent
{
get { return ValueChanged; }
}
/// <summary>
/// Ctor, initial value is default for T, enableNotify == true
/// </summary>
public NotifyingValue()
{
_enableNotify = true;
}
/// <summary>
/// Ctor with initial value, enableNotify == true
/// </summary>
/// <param name="value"></param>
public NotifyingValue(T value)
{
_value = value;
_enableNotify = true;
}
/// <summary>
/// Ctor with initial value, enableNotify == true, add this to iCloseable list
/// </summary>
/// <param name="value"></param>
/// <param name="iCloseable"></param>
public NotifyingValue(T value, List<ICloseable> iCloseable)
: this(value)
{
// Register self for closing later.
iCloseable.Add(this);
}
public void CutEventHandlers()
{
ValuePreChangeEvent = null;
ValueChanged = null;
}
/// <summary>
/// Close, cut event handlers, if value is IObservable unregister.
/// </summary>
protected override void DoClose()
{
base.DoClose();
CutEventHandlers();
}
/// <summary>
/// Suspend / resume notifications resulting from changing Value (doesn't
/// affect other notifications)
/// </summary>
public void EnableNotify(bool enableNotify)
{
_enableNotify = enableNotify;
}
/// <summary>
/// For use after we add our event handlers to get everything updated.
/// Fires ValuePreChangeEvent, ValueChanged events.
/// NOTE: If anyone assumes the value DID change for them to get an event, could cause problems.
/// </summary>
public void ForceNotify()
{
if (null != ValuePreChangeEvent)
{
ValuePreChangeEvent(this, _value);
}
if (null != ValueChanged)
{
ValueChanged(this, _value);
}
}
/// <summary>
/// Use for initialization without updates.
/// </summary>
/// <param name="value"></param>
public virtual void SetValueNoUpdate(T value)
{
_value = value;
}
/// <summary>
/// Set value with fine-grained control of updates.
/// Notify if (value changes AND allowNotify) OR forceNotify.
/// </summary>
/// <param name="value"></param>
/// <param name="allowNotify"></param>
/// <param name="forceNotify"></param>
public virtual void SetValueConditionalUpdate(T value, bool allowNotify, bool forceNotify)
{
bool doNotify = forceNotify ||
(allowNotify && _enableNotify &&
// Attempt to properly handle value vs. reference types with minimal overhead.
(_isValueType
? !value.Equals(_value)
// ReSharper disable CompareNonConstrainedGenericWithNull
: (((null == value) && (null != _value)) ||
((null != value) && (null == _value)) ||
((null != value) && (null != _value) && (!value.Equals(_value))))));
// ReSharper restore CompareNonConstrainedGenericWithNull
if (doNotify && (null != ValuePreChangeEvent))
{
ValuePreChangeEvent(this, value);
}
_value = value;
if (doNotify && (null != ValueChanged))
{
ValueChanged(this, value);
}
}
/// <summary>
/// Use when manually handling updates.
/// </summary>
public virtual void SetValueForceUpdate(T value)
{
if (null != ValuePreChangeEvent)
{
ValuePreChangeEvent(this, value);
}
_value = value;
if (null != ValueChanged)
{
ValueChanged(this, value);
}
}
/// <summary>
/// Customized, includes class name.
/// </summary>
/// <returns></returns>
public override string ToString()
{
return "NotifyingValue<" + typeof(T) + "(" + (_isValueType ? "V" : "R") + ")>(" + _value + ",en=" + _enableNotify + ")";
}
/// <summary>
/// Short-form string, no class name
/// </summary>
/// <returns></returns>
public virtual string ToShortString()
{
return "(" + _value + ",en=" + _enableNotify + ")";
}
/// <summary>
/// Shortest-form string, no class name or parentheses
/// </summary>
/// <returns></returns>
protected virtual string ToStringInner()
{
return _value + "," + _enableNotify;
}
}
}