【问题标题】:c# binding to fields on a nested objectc# 绑定到嵌套对象上的字段
【发布时间】:2011-04-27 20:10:06
【问题描述】:

对于如何使用数据绑定将 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));
        }
    }
}

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

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

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

【问题讨论】:

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


    【解决方案1】:

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

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

    而且在讨价还价中,它修复了空异常(竞争条件)的(微小)机会。


    完整的测试台,以消除扭结(来自 cmets):

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

    【讨论】:

    • 嘿,谢谢马克。我稍后会添加 SafeInvoke 代码,但希望首先让数据绑定工作。不幸的是,这似乎并没有什么不同。关于我可能做错的任何其他想法?
    • @Matt - 我会将我的测试设备添加到我的答案中 - 你能看看这是否有效吗?可能是 .NET 版本问题...
    • 谢谢马克。我用你的测试台的最新结果更新了这个问题。似乎只要我从 Inner 的对象开始,一切都会奏效。如果 Inner 为空,我将无法从控件中获取任何内容。无论如何,我将问题标记为已回答,非常感谢您的帮助!
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2017-05-09
    • 2019-04-01
    • 2017-08-30
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多