ネストされたオブジェクトのフィールドへのバインディング
-
09-10-2019 - |
質問
Winformsアプリでコントロールをバインドする方法について、データバインディングを使用してネストされたオブジェクトにバインドする方法について、シンプルで具体的な説明を見つけることができないようです。例えば:
class MyObject : INotifyPropertyChanged
{
private string _Name;
public string Name
{
get { return _Name; }
set
{
_Name = value;
OnPropertyChanged("Name");
}
}
private MyInner _Inner;
public MyInner Inner
{
get { return _Inner; }
set
{
_Inner = value;
OnPropertyChanged("Inner");
}
}
public event PropertyChangedEventHandler PropertyChanged;
private void OnPropertyChanged(string propertyName)
{
if (PropertyChanged != null)
{
PropertyChanged(this, new PropertyChangedEventArgs(propertyName));
}
}
}
class MyInner : INotifyPropertyChanged
{
private string _SomeValue;
public string SomeValue
{
get { return _SomeValue; }
set
{
_SomeValue = value;
OnPropertyChanged("SomeValue");
}
}
public event PropertyChangedEventHandler PropertyChanged;
private void OnPropertyChanged(string propertyName)
{
if (PropertyChanged != null)
{
PropertyChanged(this, new PropertyChangedEventArgs(propertyName));
}
}
}
次に、2つのテキストボックスを持つフォームを想像してください。最初は名前が、2番目のテキストボックスがあります。私は簡単に名前に対して仕事に拘束されることができますが、内側。オブジェクトに入力してからバインディングを設定すると、TextBoxに内側.someValueが表示されますが、編集できません。内側を初期化することなく新鮮なオブジェクトから始めると、データを内側に固定することができないようです。
MSDN全体で、StackOverFlow全体でチェックし、さまざまなキーワードで数十の検索をチェックしました。誰もがデータベースやデータグリッドへの拘束力について話したいと思っており、ほとんどの例はXAMLで書かれています。
更新:Marcの完全なテストハーネスを試してみましたが、部分的に成功しました。 「All Change!」にヒットした場合ボタン、私は内側のオブジェクトに書き戻すことができるようです。ただし、myobject.inner nullから始めて、内側のオブジェクトを作成する方法がわかりません。今のところ、私の内側の参照が常に有効なオブジェクトに設定されていることを確認することで、それを回避できると思います。それでも、私は何かが足りないように感じられません:)
解決
うーん - 素晴らしい質問。私はオブジェクトにたくさんのデータバインディングをしました、そして私は持っているでしょう 誓った あなたがしていることは機能するはずです。しかし、確かに、内側のオブジェクトの変化に気付くことは非常に消極的です。私はそれをうまく機能させることができました:
var outer = new BindingSource { DataSource = myObject };
var inner = new BindingSource(outer, "Inner");
txtName.DataBindings.Add("Text", outer, "Name");
txtSomeValue.DataBindings.Add("Text", inner, "SomeValue");
理想的ではありませんが、機能します。ところで;次のユーティリティメソッドが役立つ場合があります。
public static class EventUtils {
public static void SafeInvoke(this EventHandler handler, object sender) {
if(handler != null) handler(sender, EventArgs.Empty);
}
public static void SafeInvoke(this PropertyChangedEventHandler handler,
object sender, string propertyName) {
if(handler != null) handler(sender,
new PropertyChangedEventArgs(propertyName));
}
}
その後、あなたは持つことができます:
class MyObject : INotifyPropertyChanged
{
private string _Name;
public string Name { get { return _Name; } set {
_Name = value; PropertyChanged.SafeInvoke(this,"Name"); } }
private MyInner _Inner;
public MyInner Inner { get { return _Inner; } set {
_Inner = value; PropertyChanged.SafeInvoke(this,"Inner"); } }
public event PropertyChangedEventHandler PropertyChanged;
}
class MyInner : INotifyPropertyChanged
{
private string _SomeValue;
public string SomeValue { get { return _SomeValue; } set {
_SomeValue = value; PropertyChanged.SafeInvoke(this, "SomeValue"); } }
public event PropertyChangedEventHandler PropertyChanged;
}
そして、掘り出し物では、ヌルエクシセント(人種条件)の(スリムな)チャンスを修正します。
フルテストリグ、キンクをアイロンをかける(コメントから):
using System;
using System.ComponentModel;
using System.Windows.Forms;
public static class EventUtils {
public static void SafeInvoke(this PropertyChangedEventHandler handler, object sender, string propertyName) {
if(handler != null) handler(sender, new PropertyChangedEventArgs(propertyName));
}
}
class MyObject : INotifyPropertyChanged
{
private string _Name;
public string Name { get { return _Name; } set { _Name = value; PropertyChanged.SafeInvoke(this,"Name"); } }
private MyInner _Inner;
public MyInner Inner { get { return _Inner; } set { _Inner = value; PropertyChanged.SafeInvoke(this,"Inner"); } }
public event PropertyChangedEventHandler PropertyChanged;
}
class MyInner : INotifyPropertyChanged
{
private string _SomeValue;
public string SomeValue { get { return _SomeValue; } set { _SomeValue = value; PropertyChanged.SafeInvoke(this, "SomeValue"); } }
public event PropertyChangedEventHandler PropertyChanged;
}
static class Program
{
[STAThread]
public static void Main() {
var myObject = new MyObject();
myObject.Name = "old name";
// optionally start with a default
//myObject.Inner = new MyInner();
//myObject.Inner.SomeValue = "old inner value";
Application.EnableVisualStyles();
using (Form form = new Form())
using (TextBox txtName = new TextBox())
using (TextBox txtSomeValue = new TextBox())
using (Button btnInit = new Button())
{
var outer = new BindingSource { DataSource = myObject };
var inner = new BindingSource(outer, "Inner");
txtName.DataBindings.Add("Text", outer, "Name");
txtSomeValue.DataBindings.Add("Text", inner, "SomeValue");
btnInit.Text = "all change!";
btnInit.Click += delegate
{
myObject.Name = "new name";
var newInner = new MyInner();
newInner.SomeValue = "new inner value";
myObject.Inner = newInner;
};
txtName.Dock = txtSomeValue.Dock = btnInit.Dock = DockStyle.Top;
form.Controls.AddRange(new Control[] { btnInit, txtSomeValue, txtName });
Application.Run(form);
}
}
}