久々にWindows Formsなプログラムを作っております。

(どのぐらい久々かというと、最後にの記憶がVB6辺りという。。。)

Windows Formsで必須のテクニックとしてデータバインディングがあります。

そこで必ず登場するのがINotifyPropertyChanged。

このインターフェース、とっても単純ですが素直に実装するととても面倒なことになります。

先人の皆様がいろんな実装を考えておられます。

C# 6.0時代の変更通知プロパティの書き方

とか

INotifyPropertyChangedを実装する(2)

とか、すごいっす。

すごすぎて何やってるかわかんないので、泥臭く実装してみました。

Notifier クラスのインスタンス(!)を介して代入するとPropertyChangedイベントが発行されるだけです。

 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
/// <summary>
    /// 通知付代入クラス
    /// </summary>
    public class Notifier {
        //private event PropertyChangedEventHandler PropertyChanged;
        INotifyPropertyChanged mEventSource;
        Func<PropertyChangedEventHandler> mGetPropertyChanged;
        public Notifier(INotifyPropertyChanged aEventSource, Func<PropertyChangedEventHandler> aGetPropertyChanged){
            this.mEventSource = aEventSource;
            this.mGetPropertyChanged = aGetPropertyChanged;
        }

        protected void OnPropertyChanged([CallerMemberName] string propertyName = null) {
            var handler = mGetPropertyChanged();
            handler?.Invoke(this.mEventSource, new PropertyChangedEventArgs(propertyName));
        }
        /// <summary>
        /// 値変更チェックと変更通知付の代入を行う
        /// </summary>
        /// <typeparam name="T"></typeparam>
        /// <param name="field">変更される変数</param>
        /// <param name="value">新しい値</param>
        /// <param name="propertyName">通知するプロパティ名</param>
        /// <returns>更新したらTrue</returns>
        public bool Set<T>(ref T field, T value, [CallerMemberName] string propertyName = null) {
            if (EqualityComparer<T>.Default.Equals(field, value)) return false;
            field = value;
            OnPropertyChanged(propertyName);
            return true;
        }
        /// <summary>
        /// 変更通知を発行
        /// </summary>
        /// <param name="propertyName"></param>
        public void Rise([CallerMemberName] string propertyName = null) {
            OnPropertyChanged(propertyName);
        }
    }

使う側はコンストラクタでNotifierを作って、プロパティ内で使います。

 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
public class TimeRange: INotifyPropertyChanged
    {
        public event PropertyChangedEventHandler PropertyChanged;   //INotifyPropertyChangedなメンバー
        protected Notifier notify;

        public TimeRange() {
            this.notify = new Notifier(this, () => this.PropertyChanged);  //代入はこれを使う
        }

        private double mFrom;
        public double From {
            get { return mFrom; }
            set {
                this.notify.Set(ref this.mFrom, value);  //こんな感じで代入します
            }
        }

        private double mTo;
        public double To {
            get { return mTo; }
            set {
                this.notify.Set(ref this.mTo, value);
            }
        }
    }

バグがないことを祈る!