我正在开发一个 Windows 窗体应用程序,这当然涉及到对象的数据绑定(bind)。
据我所知,双向数据绑定(bind)不能在 .NET 中开箱即用。
我的问题是我找不到关于实现它的一致信息:
根据“.NET WinForms 简而言之”,我必须为每个属性创建一个名为
Changed 的事件,并在关联属性的 setter 中触发它。 根据在线资源(包括 MSDN),我只需要在我的类中实现 INotifyPropertyChanged,并在所有 setter 中触发 PropertyChanged 事件,并传入属性名称。
但是,假设我有一个简单的 UI,其中文本框数据绑定(bind)到类的属性(实现上述方法之一),如果我在代码中设置数据绑定(bind)属性的值,UI 不会反射(reflect)更改,使用任一方法。
那么我在这里缺少什么?
编辑
我应该提到,INotifyPropertyChanged 不是在相关类的声明中实现的,而是通过动态代码生成实现的,大致相当于 DynamicProxy。我正在为该主题创建一个新问题,因为答案符合实际问题。
另一个编辑 INotifyPropertyChanged 已由代码生成工具正确实现,我已通过将代理对象的实例强制转换为 INotifyPropertyChanged 来检查这一点,并为该事件注册了一个处理程序,该事件确实会引发当属性的值改变时。
数据绑定(bind)的 UpdateMode 设置为 NotifyPropertyChanged,关联控件上的 CausesValidation 设置为 false。
所以伪代码如下所示:
class MyForm {
MyClass localObj;
BindingSource mySource = new BindingSource();
MyForm(MyClass obj) {
localObj = obj.CreateProxy() // Ensures localObj is a proxy instance
mySource.Datasource = localObj // After this, UI is updated accordingly
localObj.someProperty = "I changed the value" // Here, the NotifyPropertyChanged event does get fired, but the UI does not update. Hence my question...
Console.WriteLine(localObj.someProperty) // Output is 'I changed the value'
}
}
因此,更新 UI 的唯一方法是在 BindingSource 上调用 ResetBindings...这违背了双向绑定(bind)恕我直言的目的。
终于!!
问题来自于设计者。在设计时,我无法访问业务对象代理的真实类型,这是代理生成的运行时类型(这是功能性的,我对此部分没有任何疑问)。因此,设计者使用业务对象的已知数据类型而不是代理的数据类型(继承自 BO 的类)来初始化 BindingSource
因此,需要使用代理对象的运行时类型手动初始化数据源,以使双向绑定(bind)正常工作。
工作版本如下(伪C#):
class MyForm {
Object localObj;
BindingSource mySource;
MyForm(MyClass obj) {
localObj = obj.CreateProxy() // Ensures localObj is a proxy instance
((ISupportInitialize) mySource).BeginInit()
mySource.Datasource = localObj.GetType()
((ISupportInitialize) mySource).EndInit()
mySource.Datasource = localObj // After this, UI is updated accordingly
localObj.someProperty = "I changed the value" // UI updates ! :)
}
}
感谢大家的建议和帮助,你们救了我的命。我当时就用头撞墙了!
最佳答案
我会在加载表单之前检查动态代码生成是否确实添加了 INotifyChanged。然后我会检查您通过代码更改的对象的 public 属性实际上是否与当前在屏幕上显示的对象相同(它是一个列表吗?还是单个对象?)。确保数据绑定(bind)设置为 OnPropertyChanged 而不是 OnValidation。然后我会检查它是否以相反的方式工作:编辑数据绑定(bind)文本框值并查看在您选择另一个控件后该对象是否获得新值。使用这样的代码(伪代码)更改属性时要小心。
ctor(MyClass objParm){
this.localobj = objParm;
bindingsource1.datasource = objParm; // or this.localobj
}
SomeMethod(){
this.localobj = new MyClass();
this.localobj.MyProperty = 'new value';
}
这将不起作用,除非您更改为
SomeMethod(){
this.localobj = new MyClass();
this.localobj.MyProperty = 'new value';
bindingsource1.datasource = this.localobj
}
更新:
您说数据绑定(bind)不能开箱即用,但我可以向您保证它可以。看到您提供的代码后,我怀疑它与动态代理生成/CreateProxy() 方法有关。由于 ResetBindings 有效,这可能与 CreateProxy 返回与 MyClass 不同的类有关。数据绑定(bind)对于类型非常挑剔。例如。如果您使用接口(interface)(例如 List)作为数据源,数据绑定(bind)可能会出错,因为它们不是相同的类型。这就是为什么您需要使用 new BindingList() 作为数据源。只是向您提供一些可能为您指明正确方向的背景信息。在您的情况下,元数据可能会由于动态代码生成而发生变化,请查看有关 ResetBindings() 的 msdn 文档。作为测试,请尝试以下操作:
mySource.Datasource = new BindingList<MyClass>(new List<MyClass>(){this.localobj});
关于.net - .NET 中的双向数据绑定(bind)不起作用,即使使用 INotifyPropertyChanged 对象也是如此,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/9911757/