WPF の MVVM フレームワーク Stylet 開発ドキュメント 16. INotifyPropertyChanged インターフェイスのリッスン

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として設定され、メソッドを表すように設定されますMyClassMethodMethodInfoPropertyChangedHandler

その後、そのインスタンスが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__DisplayClassTarget

これは、MyClassコンストラクターが戻るとき、<>c__DisplayClass1インスタンスへの参照はデリゲートだけであることを意味します。<>c__DisplayClass1インスタンスのライフサイクルはMyClassインスタンスから完全に独立しました。

弱いイベントを実装する方法は、何らかの方法でTargetデリゲートのプロパティを に設定することですWeakReference。通常は、それを「実際の」ターゲットへの参照を持つ中間クラスにポイントすることによって行われますWeakReferenceこれは、デリゲートがターゲットを保持しないことを意味します。

このターゲットがコンパイラによって生成された内部クラスである場合、作成したWeakReferenceもの以外にその参照を保持するものはありません。これは、この内部クラスがすぐにリサイクルされるため、デリゲートが呼び出されないことを意味します。

したがって、 に渡されたデリゲートにpropertyBindWeakがある場合、例外がスローされます。CompilerGeneratedTarget

プロジェクトの元のアドレス: https://github.com/canton7/Stylet
現在のドキュメントの元のアドレス: https://github.com/canton7/Stylet/wiki/The-ViewManager

前へ: WPF MVVM フレームワーク スタイレット開発ドキュメント 15. ViewManager
次へ: WPF MVVM フレームワーク スタイレット開発ドキュメント 17. デザイン モードのサポート

おすすめ

転載: blog.csdn.net/qq_39427511/article/details/130280765