Mat*_*per 8 .net c# data-binding .net-4.0 2-way-object-databinding
我似乎无法找到一个简单,具体的解释,说明如何使用数据绑定将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));
}
}
}
Run Code Online (Sandbox Code Playgroud)
现在想象一个只有两个文本框的表单,第一个用于Name,第二个用于Inner.SomeValue.我可以很容易地绑定对Name工作,但Inner.SomeValue是片状的.如果我填充对象然后设置绑定,它会在文本框中显示Inner.SomeValue但我无法编辑它.如果我在没有初始化Inner的情况下从一个新对象开始,我似乎无法将数据保留在Inner.SomeValue中.
我已经检查了整个MSDN,遍布StackOverflow,以及使用不同关键字的数十个搜索.每个人都想谈论绑定到数据库或DataGrids,大多数示例都是用XAML编写的.
更新:我已经尝试过Marc的全部测试工具并取得了部分成功.如果我点击"全部改变!" 按钮,我似乎能够回写内部对象.但是,从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");
Run Code Online (Sandbox Code Playgroud)
不理想,但它有效。顺便提一句; 您可能会发现以下实用方法很有用:
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));
}
}
Run Code Online (Sandbox Code Playgroud)
然后你可以有:
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;
}
Run Code Online (Sandbox Code Playgroud)
并且在讨价还价中,它修复了空异常(竞争条件)的(微薄)机会。
完整的测试装置,以消除扭结(来自评论):
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);
}
}
}
Run Code Online (Sandbox Code Playgroud)