【WPF】変更通知をIWeakEventListenerでリッスン!

  • 2008.11.24 Monday
  • 22:38
INotifyPropertyChanged - IWeakEventListener

Source and Project

カテゴリー分けが迷いますが、「WPF」とさせていただきます。
今回はINotifyPropertyChangedで発砲したイベントを
IWeakEventListenerでリッスンするという素朴な仕組みを紹介したいと思います。

INotifyPropertyChanged インターフェイス
プロパティ値が変更されたことをクライアントに通知します。
名前空間 : System.ComponentModel
アセンブリ : System (System.dll 内)

INotifyPropertyChanged インターフェイスは、プロパティ値が変更されたことをクライアント (通常はバインド元クライアント) に通知するために使用されます。

たとえば、FirstName というプロパティを持つ Person オブジェクトの例を説明します。汎用的なプロパティ変更通知を実現するには、Person 型で INotifyPropertyChanged インターフェイスを実装し、FirstName が変更された場合に PropertyChanged イベントを発生させます。データ ソースで INotifyPropertyChanged が実装されていて、非同期操作を実行している場合は、バックグラウンド スレッドでデータ ソースに対する変更を行わないようにする必要があります。代わりに、バックグラウンド スレッドでデータを読み取り、そのデータを UI スレッドでリストにマージするようにします。

バインドされたクライアントとデータ ソース間のバインディングで変更通知を発生させる場合、バインド型で次のいずれかを行う必要があります。

INotifyPropertyChanged インターフェイスを実装します (推奨)。

バインド型の各プロパティに変更イベントを提供します。

両方は行わないでください。

引用元
http://msdn.microsoft.com/ja-jp/library/system.componentmodel.inotifypropertychanged.aspx


IWeakEventListener インターフェイス
WeakEvent パターンと WeakEventManager を通じてイベントを受信するクラスにイベント リスニング サポートを提供します。

名前空間 : System.Windows
アセンブリ : WindowsBase (WindowsBase.dll 内)

WeakEventManager (ディスパッチャ) は、このインターフェイスを実装しているクラスのうち、事前に WeakEventManager のメソッドの呼び出しによってリスナとして追加されているクラスの ReceiveWeakEvent メソッドを呼び出してイベントを転送します。

WeakEvent パターンを使用する主な理由は、イベント ソースのオブジェクトの有効期間がイベント リスナに依存しない可能性があるためです。WeakEventManager の中央イベント ディスパッチを使用すると、ソース オブジェクトの有効期間がリスナの有効期間を超える場合でも、リスナのハンドラをガベージ コレクト (または手動削除) することができます。これに対し、+= またはこれに相当する言語固有のイベント構文を使用する通常のイベント フックアップでは、接続中のソースがハンドラへの強い参照を保持する可能性があります。これにより、リスナ参照を適切なタイミングでガベージ コレクトすることができなくなります。

ソースとリスナの関係によってこのパターンの使用が必要となる典型的な状況の 1 つとして、データ バインディング用のソースから送信された更新イベントを処理する場合が挙げられます。

WeakEvent パターンを使用すると、アプリケーション外部のイベントをリッスンすることができます。必要な作業は、イベントに対して WeakEventManager を定義し、リスニング動作を ReceiveWeakEvent で指定し、WeakEventManager を使用して += の代わりにハンドラ参照をアタッチすることだけです。

引用元
http://msdn.microsoft.com/ja-jp/library/system.windows.iweakeventlistener.aspx


と、まあ、MSDNに小難しいことが書かれています。
では、今回はこの二つのインターフェイスを連携させてみます。うひゃひゃ。


まずはINotifyPropertyChangedをサポートするクラスを作成します。
一応、プロパティ間に依存関係がるように、プロパティが変更される度に
LastUpDateTimeプロパティが変更されるようにしてみました。

 class NotifyObject : INotifyPropertyChanged
 {
  private string _text;
  public string Text
  {
   get { return _text; }
   set
   {
    if (_text != value)
    {
     _text = value;
     InvokePropertyChanged(new PropertyChangedEventArgs("Text"));
     LastUpDateTime = DateTime.Now;
    }
   }
  }

  private int _number;
  public int Number
  {
   get { return _number; }
   set
   {
    if (_number != value)
    {
     _number = value;
     InvokePropertyChanged(new PropertyChangedEventArgs("Number"));
     LastUpDateTime = DateTime.Now;
    }
   }
  }

  private DateTime _lastUpDateTime;
  public DateTime LastUpDateTime
  {
   get { return _lastUpDateTime; }
   set
   {
    _lastUpDateTime = value;
    InvokePropertyChanged(new PropertyChangedEventArgs("LastUpDateTime"));
   }
  }

  public event PropertyChangedEventHandler PropertyChanged;

  private void InvokePropertyChanged(PropertyChangedEventArgs e)
  {
   PropertyChangedEventHandler Handler = PropertyChanged;
   if (Handler != null)
    Handler(this, e);
  }
 }


次にLintner側の実装です。

 class LisnerObject : IWeakEventListener
 {
  public bool ReceiveWeakEvent(Type managerType, object sender, EventArgs e)
  {
   if (managerType == typeof(PropertyChangedEventManager) && e is PropertyChangedEventArgs)
   {
    PropertyChanged(sender, (PropertyChangedEventArgs)e);
   }
   else
   {
    return false;
   }
   return true;
  }

  public event PropertyChangedEventHandler PropertyChanged;
 }

IWeakEventListenerは登録されたlistnerをすべて一手に引き受けて処理するので、
分別する必要があります。
今回はプロパティの変更通知に対しては、
PropertyChangedというイベントハンドラーを用いて処理することにします。




では最後に、この二つの関連付けと実際のテストコードを紹介します。

 class Program
 {
  static void Main()
  {
   var notify = new NotifyObject();
   var listner = new LisnerObject();
   listner.PropertyChanged += OnListnerPropertyChanged;
   PropertyChangedEventManager.AddListener(notify, listner, string.Empty);

   notify.Text = "同情するな金をくれ!";
   notify.Number = 100;
   notify.Text = "ありがとう!";
  }

  static void OnListnerPropertyChanged(object sender, PropertyChangedEventArgs e)
  {
   var notify = (NotifyObject) sender;
   Type type = notify.GetType();
   PropertyInfo propertyInfo = type.GetProperty(e.PropertyName);
   object value = propertyInfo.GetValue(notify, null);

   Console.WriteLine("NotifyObject.{0}: {1}", e.PropertyName, value);
  }
 }

PropertyChangedEventManager.AddListener(notify, listner, string.Empty);

で変更通知のソースとそれを受け取るリスナーの関連付けを行います。
第三引数にstring.Emptyと指定することにより、
notify内で変更通知をサポートするすべてのプロパティの変更通知を
受け取れるようになります。
逆に、プロパティ単位で受け取る受け取らないというのを実装したい場合は
string.Emptyではなく、プロパティ名を指定することで、実装可能です。

今回のテストコードは

notify.Text = "同情するな金をくれ!";
notify.Number = 100;
notify.Text = "ありがとう!";

とプロパティを変更します。
このプロパティが変更される度に

static void OnListnerPropertyChanged(object sender, PropertyChangedEventArgs e)
{
 var notify = (NotifyObject) sender;
 Type type = notify.GetType();
 PropertyInfo propertyInfo = type.GetProperty(e.PropertyName);
 object value = propertyInfo.GetValue(notify, null);

 Console.WriteLine("NotifyObject.{0}: {1}", e.PropertyName, value);
}

リフレクションを用いて変更された値を呼び出すようにします。


では、実際に動かしてみます。

INotifyPropertyChanged - IWeakEventListener





では、さらに実験、


PropertyChangedEventManager.AddListener(notify, listner, "Text");
PropertyChangedEventManager.AddListener(notify, listner, "Number");

リッスンするプロパティをTextとNumberに絞ってみました。

実際に動かしてみると...

INotifyPropertyChanged - IWeakEventListener

と、まあ期待も裏切られる事もなく、
TextプロパティとNumberプロパティの値の変更通知を受け取ることができました。

今回はここまで...

Source and Project
コメント
コメントする








    
この記事のトラックバックURL
トラックバック

calendar

S M T W T F S
   1234
567891011
12131415161718
19202122232425
262728293031 
<< March 2017 >>

あわせて読みたい

あわせて読みたいブログパーツ

selected entries

categories

archives

recent comment

  • 【キーボード】6年前のRealForceを復活させることはできる!?その3
    art55 (05/22)
  • 【キーボード】6年前のRealForceを復活させることはできる!?その3
    分解大好き (05/18)
  • 【.NET Framework 4.5】 IListがIReadOnlyListを継承してない理由。
    art55 (02/04)
  • 【.NET Framework 4.5】 IListがIReadOnlyListを継承してない理由。
    Gen (02/04)
  • 【キーボード】RealForce が壊れて帰ってきた。
    art55 (04/29)
  • 【.NET Framework 4.5】 IListがIReadOnlyListを継承してない理由。
    art55 (02/23)
  • 【.NET Framework 4.5】 IListがIReadOnlyListを継承してない理由。
    かるあ (02/22)
  • 【C#】Dictionaryの実装・データ構造・アルゴリズムを観察する。
    art55 (01/16)
  • 【C#】Dictionaryの実装・データ構造・アルゴリズムを観察する。
    karuakun (01/16)
  • 【NetOffice】【Excel】死なないExcelプロセスをKillする。
    art55 (12/05)

recent trackback

recommend

recommend

recommend

C#プログラマのための.NETアプリケーション最適化技法 (Programmer's SELECTION)
C#プログラマのための.NETアプリケーション最適化技法 (Programmer's SELECTION) (JUGEMレビュー »)
Sasha Goldshtein,Dima Zurbalev,Ido Flatow,サシャ・ゴルドシュタイン,ディマ・ズルバレフ,イド・フラトー

recommend

ろんりと集合
ろんりと集合 (JUGEMレビュー »)
中内 伸光
とてもわかりやすいです。

recommend

recommend

シャノン・ノイマン・ディジタル世界
シャノン・ノイマン・ディジタル世界 (JUGEMレビュー »)
市川 忠男
4章がリレーショナルデータベースな内容になってます。ページ数があまりありませんが、ポイントがものすごく的確にまとまっていて、感動します。

recommend

東プレ Realforce91UBK-S 静音キーボード 静電容量無接点方式 変荷重 ブラック NG01BS
東プレ Realforce91UBK-S 静音キーボード 静電容量無接点方式 変荷重 ブラック NG01BS (JUGEMレビュー »)

テンキーレス、静音のRealForce91UBK-S。スコスコ感がたまらなく気持ちいいです。家と会社で2台持ってます。

recommend

recommend

プログラミング.NET Framework 第4版 (プログラミングシリーズ)
プログラミング.NET Framework 第4版 (プログラミングシリーズ) (JUGEMレビュー »)
Jeffrey Richter
発売予定美 2013年10月10日。.NET Frameworkとお付き合いする人のバイブルですね。

recommend

recommend

キャット・シッターの君に。
キャット・シッターの君に。 (JUGEMレビュー »)
喜多嶋 隆
私のイラストレータデビュー本です。

recommend

Essential .NET ― 共通言語ランタイムの本質
Essential .NET ― 共通言語ランタイムの本質 (JUGEMレビュー »)
ドン・ボックス,クリス・セルズ,Don Box,Chris Sells,吉松 史彰

links

profile

search this site.

others

mobile

qrcode

powered

無料ブログ作成サービス JUGEM