One of the approaches I've tried using to combat the PropertyChangedEventHandler nuisance is an extension method on INotifyPropertyChanged. Actually, a pair of them. One accepts a single property name and the other accepts a param array of property names, so that events can be fired for multiple events succinctly.
1: /// <summary>
2: /// Raise the specified PropertyChangedEventHandler for the sender
3: /// and property name, but only when the handler is not null
4: /// </summary>
5: /// <param name="sender"></param>
6: /// <param name="handler"></param>
7: /// <param name="propertyName"></param>
8: public static void RaisePropertyChanged(this INotifyPropertyChanged sender
9: , PropertyChangedEventHandler handler, string propertyName)
10: {
11: if (handler != null)
12: {
13: handler(sender, new PropertyChangedEventArgs(propertyName));
14: }
15: }
16:
17: /// <summary>
18: /// Raise the specified PropertyChangedEventHandler for the sender and
19: /// each of the property names, but only when the handler is not null
20: /// </summary>
21: /// <param name="sender"></param>
22: /// <param name="handler"></param>
23: /// <param name="propertyNames"></param>
24: public static void RaisePropertyChanged(this INotifyPropertyChanged sender
25: , PropertyChangedEventHandler handler, params string[] propertyNames)
26: {
27: if (handler != null)
28: {
29: foreach (string propertyName in propertyNames)
30: {
31: handler(sender, new PropertyChangedEventArgs(propertyName));
32: }
33: }
34: }
These can then be used like this:
1: public decimal Freight
2: {
3: get { return this._freight; }
4: set
5: {
6: if (this._freight != value)
7: {
8: this._freight = value;
9: this.RaisePropertyChanged(this.PropertyChanged, "Freight");
10: }
11: }
12: }
13:
14: public decimal Subtotal
15: {
16: get { return this._subtotal; }
17: set
18: {
19: if (this._subtotal != value)
20: {
21: this._subtotal = value;
22: this.RaisePropertyChanged(this.PropertyChanged, "Subtotal", "Tax", "Total");
23: }
24: }
25: }
This helps some. I don't like having to pass this.PropertyChanged into the extension method, but it's a necessary evil because the extension method cannot access the event in its handler form.
Another approach is to actually have an extension method directly on the PropertyChangedEventHandler, which you'll see just reverses the problem.
1: /// <summary>
2: /// Raise a PropertyChangedEventHandler but only when it's non-null
3: /// </summary>
4: /// <param name="handler"></param>
5: /// <param name="sender"></param>
6: /// <param name="propertyName"></param>
7: public static void Raise(this PropertyChangedEventHandler handler
8: , object sender, string propertyName)
9: {
10: if (handler != null)
11: {
12: handler(sender, new PropertyChangedEventArgs(propertyName));
13: }
14: }
15:
16: /// <summary>
17: /// Raise a PropertyChangedEventHandler for each property specified,
18: /// but only when the handler is non-null
19: /// </summary>
20: /// <param name="handler"></param>
21: /// <param name="sender"></param>
22: /// <param name="propertyNames"></param>
23: public static void Raise(this PropertyChangedEventHandler handler
24: , object sender, params string[] propertyNames)
25: {
26: if (handler != null)
27: {
28: foreach (string propertyName in propertyNames)
29: {
30: handler(sender, new PropertyChangedEventArgs(propertyName));
31: }
32: }
33: }
Here's the usage of that approach:
1: public decimal Freight
2: {
3: get { return this._freight; }
4: set
5: {
6: if (this._freight != value)
7: {
8: this._freight = value;
9: this.PropertyChanged.Raise(this, "Freight");
10: }
11: }
12: }
13:
14: public decimal Subtotal
15: {
16: get { return this._subtotal; }
17: set
18: {
19: if (this._subtotal != value)
20: {
21: this._subtotal = value;
22: this.PropertyChanged.Raise(this, "Subtotal", "Tax", "Total");
23: }
24: }
25: }
The syntax is more concise, but now we have to pass the object reference in, because there's no way to get the object that owns an event handler (that I can tell).
I don't know which of these two approaches I like better. They're both an improvement over putting the logic into every property setter though.