16. INotifyPropertyChanged インターフェイスをリッスンします
インターフェイスの実装は簡単ですが、インターフェイスを実装するオブジェクトの通知を監視するのは少し難しいことがよくあります。イベント ハンドラーを登録し、プロパティ名をチェックしてそれが期待するものであるかどうかを確認し、終了したらイベントの登録を解除する必要があります。INotifyPropertyChanged
完了PropertyChanged
ハンドラー。
INotifyPropertyChanged.Bind
これは PropertyChanged イベントをサブスクライブする最も簡単な方法であり、(通常のイベントと同様に) サブスクライバーへの強い参照を使用します。つまり、購読しているコンテンツがまだ生きている間に割り当てを解除する場合は、忘れずに購読を解除する必要があります。
使い方はとても簡単です。次の形式のオブジェクトがあるとします。
// 可以是INotifyPropertyChanged的任何实现-我使用PropertyChangedBase,因为它使示例更短
class Model : PropertyChangedBase
{
private string _stringProperty;
public string StringProperty
{
get {
return this._stringProperty; }
set {
SetAndNotify(ref this._stringProperty, value); }
}
}
そして、StringProperty
プロパティが変更されるたびに通知を受け取りたいと考えています。次のようにします。
var model = new Model();
// ...
model.Bind(x => x.StringProperty, (sender, eventArgs) => Debug.WriteLine(String.Format("New value for property {0} on {1} is {2}", eventArgs.PropertyName, sender, eventArgs.NewValue)));
x => x.StringProperty
セクションは、タイプセーフな方法で観察するプロパティを指定する方法です。x には任意の値を入力できます。それを入力するとx => x.
、Intellisense によってプロパティのリストの入力が求められます。
(propertyName, newValue) => Debug.WriteLine(String.Format("New value is {0}", newValue))
part は、そのプロパティが変更されるたびに呼び出されるものです。
Bind
メソッドは実際にはメソッドIEventBinding
を 1 つだけ持つ実装を返しますUnbind
。バインディングを削除するには、このメソッドを呼び出します。例:
var model = new Model();
// ...
var binding = model.Bind(x => x.StringProperty, (sender, eventArgs) => Debug.WriteLine(String.Format("New value for property {0} on {1} is {2}", eventArgs.PropertyName, sender, eventArgs.NewValue)));
// ...
binding.Unbind();
INotifyPropertyChanged.BindWeak
通常、イベントをサブスクライブすると、イベント通知を受信するモノは、少なくともイベントを発行したモノと同じくらい存在します。これは、イベントを発行するモノが最終的にイベント通知を受け取るモノを参照するためです。
これは通常は望ましくないことです。たとえば、依存するサービスの PropertyChanged イベントを監視したい ViewModel があるとします。
Stylet は、INotifyPropertyChanged と呼ばれる拡張メソッドを提供します。BindWeak
これは、Bind
弱いバインディングを作成する点を除けば、INotifyPropertyChanged とよく似ています。構文は上記と同じなのでBind
、ここでは繰り返しません。
すべてのデリゲートを弱くバインドすることはできないことに注意してください。ローカル変数をキャプチャするデリゲートは通常失敗します。これについては、以下でさらに詳しく説明します。
テクニック: 弱いイベントのサブスクリプション
デリゲートの詳細についてはいくつか説明しますが、基本的には、イベントをサブスクライブするときに、デリゲートの新しいインスタンスを作成し、それをイベントを所有するものに渡します。デリゲートは (本質的に) 2 つの部分で構成されます: 呼び出すメソッド (属性)Method
と、メソッドを呼び出すインスタンス (属性) ですTarget
。
クラスのインスタンス メソッドを指すデリゲートを作成すると、すべてがうまくいき、簡単になります。
class MyClass
{
public MyClass(Model model)
{
model.PropertyChanged += new PropertyChangedEventHandler(this.PropertyChangedHandler);
// or, more concisely
model.PropertyChanged += this.PropertyChangedHandler;
{
public void PropertyChangedHandler(object sender, PropertyChangedEventArgs e)
{
// ...
}
}
この場合、新しいデリゲートが作成され、インスタンスTarget
として設定され、メソッドを表すように設定されます。MyClass
Method
MethodInfo
PropertyChangedHandler
その後、そのインスタンスがmodel
そのデリゲートの所有者になります。model
これは、インスタンスにはインスタンスを所有するデリゲートがあることを意味しますMyClass
。つまり、MyClass
インスタンスが解放されるまでインスタンスを解放することはできませんmodel
。
たとえば、匿名のデリゲート/ラムダが機能すると、状況は少し複雑になり始めます。
class MyClass
{
public MyClass(Model model)
{
model.PropertyChanged += delegate {
Debug.WriteLine("Hi"); };
// 或者,使用lambas(首选)
model.PropertyChanged += (o, e) => Debug.WriteLine("Hi");
}
}
ここで、C# コンパイラは次のようにクラスに新しい特別なメソッドを作成する必要があります。
class MyClass
{
public MyClass(Model model)
{
model.PropertyChanged += new PropertyChangedEventHandler(this.<.ctor>b__0);
}
[CompilerGenerated]
private void <.ctor>b__0(object sender, PropertyChangedEventArgs e)
{
Debug.WriteLine("Hi");
}
}
(「説明できない」メソッド名の使用に注意してください。文字 (<
と>
) を含むメソッド名は C# では無効ですが、CLR では有効です)。
ローカル変数をキャプチャする匿名のデリゲート/ラムダがある場合、これはさらに複雑になります。ここで、C# コンパイラは、変数への参照を保持するまったく新しい組み込みクラスを生成する必要があります。例えば:
class MyClass
{
public MyClass(Model model)
{
string test = "test";
model.PropertyChanged += (o, e) => Debug.WriteLine(test);
}
}
は次のようにコンパイルされます。
class MyClass
{
public MyClass(Model model)
{
MyClass.<>c__DisplayClass1 c__DisplayClass1 = new MyClass.<>c__DisplayClass1();
c__DisplayClass1.test = "test";
model.PropertyChanged += new PropertyChangedEventHandler(c__DisplayClass1.<.ctor>b__0);
}
[CompilerGenerated]
private sealed class <>c__DisplayClass1
{
public string test;
public void <.ctor>b__0(object sender, PropertyChangedEventArgs e)
{
Debug.WriteLine(this.test);
}
}
ここで、作成されたデリゲートは、そのプロパティの値として のインスタンスPropertyChangedEventHandler
を持ちます。<>c__DisplayClass
Target
これは、MyClass
コンストラクターが戻るとき、<>c__DisplayClass1
インスタンスへの参照はデリゲートだけであることを意味します。<>c__DisplayClass1
インスタンスのライフサイクルはMyClass
インスタンスから完全に独立しました。
弱いイベントを実装する方法は、何らかの方法でTarget
デリゲートのプロパティを に設定することですWeakReference
。通常は、それを「実際の」ターゲットへの参照を持つ中間クラスにポイントすることによって行われますWeakReference
。これは、デリゲートがターゲットを保持しないことを意味します。
このターゲットがコンパイラによって生成された内部クラスである場合、作成したWeakReference
もの以外にその参照を保持するものはありません。これは、この内部クラスがすぐにリサイクルされるため、デリゲートが呼び出されないことを意味します。
したがって、 に渡されたデリゲートにpropertyBindWeak
がある場合、例外がスローされます。CompilerGenerated
Target
プロジェクトの元のアドレス: https://github.com/canton7/Stylet
現在のドキュメントの元のアドレス: https://github.com/canton7/Stylet/wiki/The-ViewManager
前へ: WPF MVVM フレームワーク スタイレット開発ドキュメント 15. ViewManager
次へ: WPF MVVM フレームワーク スタイレット開発ドキュメント 17. デザイン モードのサポート