C# のコードでデータ バインディング (1) の中で Binding Source の条件について書きましたが、
INotifyPropertyChanged でも DependencyObject でもないオブジェクトの場合は少し難解な動作をします。
というのも、プロパティ変更通知ができないはずのオブジェクトに対して、
内部で PropertyDescriptor を経由して双方向のバインディングができるように試みるためです。
POCO である Person0 クラスを利用したコードを以下に示します。
using System; | |
using System.ComponentModel; | |
using System.Windows.Controls; | |
using System.Windows.Data; | |
namespace BindingConsole | |
{ | |
class Program | |
{ | |
[STAThread] | |
static void Main(string[] args) | |
{ | |
// Binding Source (Any object). | |
var person = new Person0 { Id = 123, Name = "Taro" }; | |
// Binding Target (DependencyObject). | |
var textBox = new TextBox { Text = "Default" }; | |
Console.WriteLine(textBox.Text); // Default | |
// Binds target to source. | |
var binding = new Binding(nameof(person.Name)) { Source = person, UpdateSourceTrigger = UpdateSourceTrigger.PropertyChanged }; | |
textBox.SetBinding(TextBox.TextProperty, binding); | |
Console.WriteLine(textBox.Text); // Taro | |
// Changes source value. | |
// Notification does not work in usual property setting. | |
//person.Name = "Jiro"; | |
var properties = TypeDescriptor.GetProperties(person); | |
properties[nameof(person.Name)].SetValue(person, "Jiro"); | |
Console.WriteLine(textBox.Text); // Jiro | |
// Changes target value. | |
textBox.Text = "Saburo"; | |
Console.WriteLine(person.Name); // Saburo | |
} | |
} | |
} |
Binding Source 側を変更しようとするとき、通常の
person.Name = "Jiro";
というコードでは変更は通知されません。
変更を通知するには PropertyDescriptor.SetValue メソッドを使います。
なお、変更時のイベントハンドラーを登録するには PropertyDescriptor.AddValueChanged メソッドを使います。
Binding オブジェクトの内部では OneTime バインディングの場合を除き、
PropertyDescriptor.SetValue メソッドおよび PropertyDescriptor.AddValueChanged メソッドを利用して、
プロパティ変更通知を試みます。
このように、オブジェクトへの操作は PropertyDescriptor を経由しなければならなくなるため、
WPF などのアプリケーションでこの方法でコードを書くのは大変です。
また、アプリケーションのコードで person への参照を削除しても、
PropertyDescriptor の中で person への参照を持ち続けるため、メモリリークの原因となるようです。
したがって、Binding Source として使うオブジェクトには INotifyPropertyChanged インターフェイスを実装させるのがよいでしょう。
補足として、PropertyDescriptor の挙動を確認するためのコードを以下に示します。
using System; | |
using System.ComponentModel; | |
namespace BindingConsole | |
{ | |
class Program | |
{ | |
[STAThread] | |
static void Main(string[] args) | |
{ | |
var person = new Person0 { Id = 123, Name = "Taro" }; | |
var properties = TypeDescriptor.GetProperties(person); | |
var nameProp = properties[nameof(person.Name)]; | |
nameProp.AddValueChanged(person, (o, e) => Console.WriteLine(person.Name)); | |
nameProp.SetValue(person, "Jiro"); // 出力: Jiro | |
} | |
} | |
} |
作成したサンプル
BindingConsole (GitHub)
PocoBindingWpf (GitHub)
バージョン情報
C# 6.0
.NET Framework 4.5
参照
【WPF】変更通知をサポートしないCLRプロパティの変更通知
【WPF】PropertyDescriptorを取得 / 利用する。