即使使用INotifyPropertyChanged对象,.NET中的双向数据绑定也不起作用

时间:2012-03-28 16:23:19

标签: .net winforms data-binding

我正在使用Windows窗体应用程序,当然这涉及到对象的数据绑定。

从我收集到的信息来看,双向数据绑定不能与.NET一起开箱即用。

我的问题是我无法找到有关实施它的一致信息:

  • 根据“简而言之的.NET WinForms”,我必须为每个属性创建一个事件,名为< propertyName> Changed,并在相关属性的setter中触发它。

  • 根据在线资源(包括MSDN),我只需在我的类中实现INotifyPropertyChanged,并在所有setter中触发PropertyChanged事件,传入属性的名称。

但是,假设我有一个简单的UI,文本框数据绑定到类的属性(实现上述任一方法),如果我在代码中设置数据绑定属性的值,则UI不会反映更改,使用任何一种方法。

那我在这里错过了什么?

修改

我应该提到INotifyPropertyChanged没有在相关类的声明中实现,而是通过动态代码生成大致相当于DynamicProxy。我正在为该主题创建一个新问题,因为答案符合实际问题。

另一个编辑 INotifyPropertyChanged由代码生成工具正确实现,我已经通过将代理对象的实例强制转换为INotifyPropertyChanged来检查该点,并注册了事件的处理程序,当属性的值发生变化时。

数据绑定的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 ......哪种方式违背了双向绑定IMHO的目的。

终于!!

问题来自Designer。在设计时,我无法访问业务对象代理的实际类型,这是代理生成的运行时类型(这是函数,我对该部分毫不怀疑)。所以BindingSource由设计者使用业务对象的已知数据类型进行初始化,而不是代理的数据类型(这是继承自BO类的类)

因此,需要使用代理对象的运行时类型手动初始化数据源,以使双向绑定正常工作。

工作版本如下(在伪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 ! :)
    }
}

感谢大家的建议和帮助,你刚刚结束了我的一天。我在这个人的头上碰到了我的头!

2 个答案:

答案 0 :(得分:2)

实施INotifyPropertyChanged就足够了。只需在属性窗口中将Data Source Update Mode的{​​{1}}从TextBoxes更改为OnValidation - > (DataBindings) - > (高级) - > [...]对话框。


更新

这就是OnPropertyChanged的实施方式。为您的所有属性执行此操作

INotifyPropertyChanged

您的课程需要实现界面

private string _city;
public string City
{
    get { return _city; }
    set
    {
        if (_city != value) {
            _city = value;
            OnPropertyChanged("City");
        }
    }
}

答案 1 :(得分:2)

我会检查动态代码生成是否在加载表单之前实际添加了INotifyChanged 。然后我会检查您通过代码更改的对象的 public 属性是否实际上是与当前显示在屏幕上的对象相同的对象(它是否为列表还是单个物体?)。确保数据绑定设置为OnPropertyChanged而不是OnValidation。然后我会检查它是否相反:编辑数据绑定文本框值并查看对象是否在选择另一个控件后获取新值。使用这样的代码(伪代码)更改属性时要小心。

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
}

<强>更新
你说数据绑定不是开箱即用的,但我可以确保你这样做。看到你提供的代码后,我怀疑它与动态代理生成/ CreateProxy() - 方法有关。由于ResetBindings工作,它可能与CreateProxy返回一个不同的类,然后MyClass的事实有关。关于类型,数据绑定非常挑剔。例如。如果您使用接口(例如List作为数据源),Databinding可能会出错,因为它们的类型不同。这就是你需要使用新的BindingList()作为数据源的原因。只是给你一些可能指向正确方向的背景信息。在您的情况下,由于动态代码生成,元数据可能会更改,请查看ResetBindings()上的msdn文档。作为测试尝试这个:

mySource.Datasource = new BindingList<MyClass>(new List<MyClass>(){this.localobj});