c# 绑定(bind)到嵌套对象上的字段

标签 c# .net data-binding .net-4.0 2-way-object-databinding

我似乎找不到关于如何使用数据绑定(bind)将 WinForms 应用程序中的控件绑定(bind)到嵌套对象的简单、具体的解释。例如:

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));
        }
    }
}

现在想象一个只有两个文本框的表单,第一个用于 Name,第二个用于 Inner.SomeValue。我可以很容易地绑定(bind)到 Name,但 Inner.SomeValue 是不稳定的。如果我填充对象然后设置绑定(bind),它会在文本框中显示 Inner.SomeValue 但我无法编辑它。如果我在没有初始化 Inner 的情况下从一个新对象开始,我似乎无法将数据粘贴到 Inner.SomeValue 中。

我已经检查了整个 MSDN、整个 StackOverflow 以及许多使用不同关键字的搜索。每个人都想谈论绑定(bind)到数据库或 DataGrid,大多数示例都是用 XAML 编写的。

更新:我尝试了 Marc 的完整测试工具并取得了部分成功。如果我点击“全部改变!”按钮,我似乎可以写回内部对象。但是,从 MyObject.Inner null 开始,它不知道如何创建内部对象。我想现在,我可以通过确保我的内部引用始终设置为有效对象来解决它。尽管如此,我还是忍不住觉得自己错过了什么:)

最佳答案

嗯 - 一个很好的问题;我对对象做了很多数据绑定(bind),我会发誓您所做的应该有效;但确实很不愿意注意到内部对象的变化。我设法让它工作:

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);
        }
    }

}

关于c# 绑定(bind)到嵌套对象上的字段,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/3974627/

相关文章:

c# - .net 中未使用的用途是否会影响性能?

android - TextView textColor 中的数据绑定(bind)选择器

c# - 如果网格较小,则调整 DataGridView 的列以填充可用空间,如果网格大于可用空间,则使用滚动

c# - 获取最近添加的记录的主键并作为外键插入到另一个表中

c# - 将数据从行保存到数据库

c++ - .NET 4、C++、if...else 和 switch() 对性能的影响

c# - 将 DataGrid 列宽绑定(bind)到另一个 DataGrid 的两列

c# - MvvmCross 自定义绑定(bind)通过 INotifyPropertyChanged

c# - 将数据表导出到 Excel asp

c# - 如何使 C# Create.Delegate 支持继承?