C# 动态对象双向数据绑定

C# 动态对象双向数据绑定,c#,winforms,dynamic,data-binding,dynamicobject,C#,Winforms,Dynamic,Data Binding,Dynamicobject,我试图构建一个动态数据容器,允许(一些)动态添加的属性绑定到WinForm元素。到目前为止,当我绑定常规对象属性时,绑定工作正常 样本: public class CompileTimePropertiesDataContainer { public string TestString = "Hello World"; } 然后在表单中绑定就可以了: var component = new CompileTimePropertiesDataContainer(); lblTestStr

我试图构建一个动态数据容器,允许(一些)动态添加的属性绑定到WinForm元素。到目前为止,当我绑定常规对象属性时,绑定工作正常

样本:

public class CompileTimePropertiesDataContainer {
    public string TestString = "Hello World";
}
然后在表单中绑定就可以了:

var component = new CompileTimePropertiesDataContainer();
lblTestString.DataBinding.Add(
    "Text", component, "TestString", false, DataSourceUpdateMode.OnPropertyChanged);
// >>> lblTestString.Text == "Hello World"
component.TestString = "Another Sample";
// >>> lblTestString.Text == "Another Sample";
此时,上面的示例起作用,并假设对象属性的更新是在UI线程上完成的。因此,现在我需要实现一个具有动态属性的对象(用于在整个项目和其他项目中实现可恢复性)

因此,我实现了以下类(替换上面的CompileTimePropertiesDataContainer):

在上面的代码中,我实现了INotifyPropertyChanged以满足我所理解的绑定到winforms控件的要求,并为DataContainer及其提供的动态属性定义了属性描述符

现在回到示例实现,我将对象调整为“动态”,现在绑定看起来不会“粘滞”

dynamic component = new DataContainer();
// *EDIT* forgot to initialize component.TestString in original post
component.TestString  = "Hello World";
lblTestString.DataBinding.Add(
    "Text", component, "TestString", false, DataSourceUpdateMode.OnPropertyChanged);
// >>> lblTestString.Text == "Hello World"
component.TestString = "Another Sample";
// >>> lblTestString.Text == "Hello World";
另一个注意事项是,DataContainer对象中的“event PropertyChangedEventHandler PropertyChanged”为null,事件正在触发(通过调试确认),但由于PropertyChanged为null(不侦听事件),因此它不会更新


我觉得问题在于我在DataContainer或DynamicPropertyDescriptor中实现的ICustomTypeDescriptor。

设置与属性的数据绑定时,框架调用该属性的
PropertyDescriptor
AddValueChanged
方法。要提供双向数据绑定,属性描述符应覆盖该方法,并订阅组件的
PropertyChanged
事件,并调用属性描述符的
OnValueChanged
方法:

void PropertyChanged(object sender, EventArgs e)
{
    OnValueChanged(sender, e);
}
public override void AddValueChanged(object component, EventHandler handler)
{
    base.AddValueChanged(component, handler);
    ((INotifyPropertyChanged)component).PropertyChanged += PropertyChanged;
}
public override void RemoveValueChanged(object component, EventHandler handler)
{
    base.RemoveValueChanged(component, handler);
    ((INotifyPropertyChanged)component).PropertyChanged -= PropertyChanged;
}
示例

您可以在以下存储库中找到一个可用的实现:


数据容器的目的是什么?基于上一个代码示例,您将如何使用它-您根本不需要这个。绑定将与动态绑定一起使用-您可以使用任何类型的任何实例作为“组件”,如果所有实例都具有属性
TestString
,则在设置到属性的数据绑定时,框架将调用该属性的PropertyDescriptor的AddValueChanged方法。您的属性描述符应该重写该方法并订阅组件的PropertyChanged事件,并调用属性描述符的OnValueChanged方法。@fabio是的,这不是它的使用方法,这只是一个简单的重新创建问题的示例。@Aaron Murray,是否可以将动态组件绑定到WPF控件中?这几乎肯定是我错过的一部分,但是,在实现和测试上述内容时,我现在遇到了无法“绑定到数据源上的属性'TestString'的”错误,这意味着我在课堂上做了一些改变,导致出现了这个错误。我已经确认PropertyDescriptor(用于“TestString”)在绑定之前确实存在,我已经测试了解决方案并得到了预期的结果。我在GitHub存储库中共享了这个示例。没错,实现“ICustomTypeDescriptor”似乎是无法绑定到属性异常的关键,我已经在代码中包含了属性异常(这就是为什么它最初可以正常工作的原因,但是在我过去2天多次尝试让类正常工作的过程中,我删除了它,忘记了它的必要性…现在一切都很好,你的解决方案无疑是我所缺少的关键。太好了!我看到你有实现,但没有看到你忘记了o在实现列表中声明接口!@RezaAghaei,在测试您的GitHub实现时,我在调试控制台中发现了几个绑定异常。
dynamic component = new DataContainer();
// *EDIT* forgot to initialize component.TestString in original post
component.TestString  = "Hello World";
lblTestString.DataBinding.Add(
    "Text", component, "TestString", false, DataSourceUpdateMode.OnPropertyChanged);
// >>> lblTestString.Text == "Hello World"
component.TestString = "Another Sample";
// >>> lblTestString.Text == "Hello World";
void PropertyChanged(object sender, EventArgs e)
{
    OnValueChanged(sender, e);
}
public override void AddValueChanged(object component, EventHandler handler)
{
    base.AddValueChanged(component, handler);
    ((INotifyPropertyChanged)component).PropertyChanged += PropertyChanged;
}
public override void RemoveValueChanged(object component, EventHandler handler)
{
    base.RemoveValueChanged(component, handler);
    ((INotifyPropertyChanged)component).PropertyChanged -= PropertyChanged;
}