プロパティ変更とエラー情報の通知 (実装編)

XAML 系テクノロジにおけるデータ バインディングは、
プレゼンテーション層とモデル層の間のデータ同期を自動化するプログラミング モデルを実現するための仕組みです。
バインディング ソース (主にモデル層のデータ) では、オブジェクト内で変化が起こった場合にそのことを通知します。

これは Observer パターンの活用例の 1 つです。
Observer パターンについては、プロパティ変更とエラー情報の通知 (概念編) に書きました。

以下では、主に MSDN ライブラリの資料をもとに、バインディング ソースを実装する際の注意点をまとめました。
大きく、プロパティ変更通知とエラー情報通知に分けられます。

 

■ プロパティ変更通知

オブジェクトのプロパティの値が変更される・されたことを通知するには、
INotifyPropertyChanging インターフェイス (プロパティ変更前) および
INotifyPropertyChanged インターフェイス (プロパティ変更後) を実装します。
INotifyPropertyChanging インターフェイスの実装は省略されることもあります。

これらのインターフェイスでは PropertyChanging イベントおよび PropertyChanged イベントを実装することになりますが、
イベント引数の PropertyName には、変更されたプロパティの名前を指定します。
ここで null または空文字列を指定すれば、オブジェクトのすべてのプロパティが変更されたという意味になります。
ただし、実際には null はあまり使わず、空文字列を使うことが多いようです。

以前は、プロパティ内でこれを実装するためにプロパティ名の文字列をコードに埋め込んでいましたが、
.NET Framework 4.5 または Windows ストアでは CallerMemberName 属性を利用することで回避できます。

public class Person : INotifyPropertyChanged
{
    private string name;

    public string Name
    {
        get { return name; }
        set
        {
            if (name == value) return;
            name = value;
            NotifyPropertyChanged();
        }
    }

    public event PropertyChangedEventHandler PropertyChanged = (o, e) => { };

    public void NotifyPropertyChanged([CallerMemberName]string propertyName = "")
    {
        PropertyChanged(this, new PropertyChangedEventArgs(propertyName));
    }
}

 

⋄ コレクション変更通知

コレクションが変更されたことを通知するには、INotifyCollectionChanged インターフェイスを実装します。
自分で実装しなくても、ObservableCollection<T> クラスを利用すればほぼ十分です。

 

■ エラー情報通知

オブジェクトの中でエラーが発生したことを通知するには、INotifyDataErrorInfo インターフェイスを実装します。
主に、ユーザーからの入力値を検証する場合などに利用されます。
INotifyDataErrorInfo インターフェイスは Silverlight で先に登場しており、解説もこちらのほうが詳しく記載されています。

なお、以前から IDataErrorInfo インターフェイスがありましたが、
これは古い形式で、通知イベントが発生する形式にはなっていません。今後は使われなくなるでしょう。

さて、以下は INotifyDataErrorInfo インターフェイスの実装方法です。

内部でエラーが発生したら、例えば変数などにエラー情報をキャッシュしておき、ErrorsChanged イベントを発生させます。
プロパティ変更通知のときと同様、ErrorsChanged イベントの引数にはプロパティ名を渡します。
ここで null または空文字列を指定すれば、オブジェクトのレベルでエラーが発生したという意味になります。

そして GetErrors メソッドで、実際のエラーのコレクションを取得できるようにします。

HasErrors プロパティでは、エンティティ レベルにもプロパティ レベルにもエラーが存在しない場合に false を返します。
ただし、標準のバインディング エンジンに利用されることはないようです。
通常は、プロパティ名を指定できる ErrorsChanged イベントと GetErrors メソッドを使います。

INotifyDataErrorInfo.ErrorsChanged イベントの説明には、ErrorsChanged イベントを UI スレッドで発生させる、
と書かれていますが、Binding オブジェクトでデータ バインディングを設定してある場合には
UI スレッドでデータ ソースの値が取得されるため、UI スレッドでなくてもとくに問題はなさそうです。

以下は、その他の注意点です。

・GetErrors メソッドで INotifyCollectionChanged が実装されたコレクションを返す場合であっても、
    ErrorsChanged イベントを発生させる。
・GetErrors の戻り値の各アイテムに対して ToString メソッドを呼び出したらエラー メッセージを取得できる。
    例えば、ValidationResult オブジェクトを利用する。

 

INotifyDataErrorInfo インターフェイスの実装方法については以上ですが、
標準のバインディング エンジンの中核である Binding オブジェクトの設定の注意についても記述しておきます。

・ValidatesOnNotifyDataErrors プロパティ : INotifyDataErrorInfo のエラー検知をする場合。
・ValidatesOnDataErrors プロパティ : IDataErrorInfo のエラー検知をする場合。通常は使いません。
・ValidatesOnExceptions プロパティ : 例外を検知する場合。
・NotifyOnValidationError プロパティ : エラーを検知したときに、WPF の場合は UI 要素の Validation.Error 添付イベントを、
    Silverlight の場合は UI 要素の BindingValidationError イベントを発生させる。

これらのプロパティの既定値は、ValidatesOnNotifyDataErrors のみ true で、その他はすべて false です。
Silverlight の ValidationSummary などの一部のコントロールを利用する場合、
NotifyOnValidationError プロパティを true にします。

 

プラットフォーム
.NET Framework
Silverlight
Windows Phone
Windows ストア
Portable Class Library

参照
プロパティ変更とエラー情報の通知 (概念編)
INotifyPropertyChanged インターフェイス
INotifyCollectionChanged インターフェイス
INotifyDataErrorInfo インターフェイス
弱いイベント パターン

2件のフィードバック to “プロパティ変更とエラー情報の通知 (実装編)”

  1. プロパティ変更とエラー情報の通知 (概念編) | Do Design Space Says:

    […] 先にプロパティ変更とエラー情報の通知 (実装編) を投稿しましたが、 今回は通知のプログラミング手法について概念的に考えてみます。 […]

  2. C# のコードでデータ バインディング (1) | Do Design Space Says:

    […] 参照 データ バインディングの概要 プロパティ変更とエラー情報の通知 (実装編) […]


コメントを残す