与复杂/嵌套对象的数据绑定 (C#)

     2023-02-26     15

关键词:

【中文标题】与复杂/嵌套对象的数据绑定 (C#)【英文标题】:Data Binding with Complex / Nested Objects (C#) 【发布时间】:2014-02-02 19:35:52 【问题描述】:

由于我很难找到有关此主题的通用信息,因此我在发布此内容时会尽可能多地进行解释,并希望与 SO 社区分享我的发现。

在 C# 中,数据绑定到复杂对象的集合通常不允许从类中的嵌套对象中读取数据。这方面的一个例子是class A 实例的成员是class B 的对象。如果您在将集合/绑定源用作数据源时需要内部对象的属性(在本例中为 B),那么如果没有额外的工作或访问原始类进行修改,那么您就很不走运了。

问题是“如何在数据绑定到 UI 对象时使用内部类中的数据,而无需修改原始类?”

【问题讨论】:

【参考方案1】:

内部类中的数据绝对可以用于数据绑定映射,但默认情况下不能。处理此问题的最佳方法是设置PropertyDescriptorsTypeDescriptors。我将在下面解释的方式是大部分通用实现,但将允许对内部对象进行数据绑定访问,而无需对原始类或扩展进行任何修改来实现接口。如果您不是您正在使用的类的作者,或者您正在使用 ORM 映射类,这将非常有用。

实现此解决方案有 4 个部分:

    PropertyDescriptor 类的扩展以访问内部对象 CustomTypeDescriptor 实现 TypeDescriptonProvider 实现 将新创建的提供程序附加到我们需要访问数据的类型。

第 1 部分 - 扩展 PropertyDescriptor 类:

为了访问内部组件,我们需要获取它们的PropertyDescriptors,它们本质上是用于访问类的公共属性的元数据。这可以通过扩展PropertyDescriptor 来访问子属性来完成。此外,您还可以在此处实现如何读取和回写这些对象,或将它们设置为只读(就像我所做的那样)。

class SubPropertyDescriptor : PropertyDescriptor

    private PropertyDescriptor _parent;
    private PropertyDescriptor _child;

    public SubPropertyDescriptor(PropertyDescriptor parent, PropertyDescriptor child, string propertyDescriptorName)
        : base(propertyDescriptorName, null)
    
        _child = child;
        _parent = parent;
    
    //in this example I have made this read-only, but you can set this to false to allow two-way data-binding
    public override bool IsReadOnly get  return true;  
    public override void ResetValue(object component)   
    public override bool CanResetValue(object component) return false; 
    public override bool ShouldSerializeValue(object component) return true;
    public override Type ComponentType get  return _parent.ComponentType;  
    public override Type PropertyType get  return _child.PropertyType;  
    //this is how the value for the property 'described' is accessed
    public override object GetValue(object component)
    
        return _child.GetValue(_parent.GetValue(component));
    
    /*My example has the read-only value set to true, so a full implementation of the SetValue() function is not necessary.  
    However, for two-day binding this must be fully implemented similar to the above method. */
    public override void SetValue(object component, object value)
    
        //READ ONLY
        /*Example:  _child.SetValue(_parent.GetValue(component), value);
          Add any event fires or other additional functions here to handle a data update*/
    

第 2 部分 - 实现 CustomTypeDescriptor

CustomTypeDesciptor 创建元数据标签以允许绑定来自内部对象的数据。本质上,我们将创建“描述符字符串”,链接到内部对象的类型属性,然后将它们添加到父对象中。用于内部对象的格式如下"className_property",其中类名是内部对象的Type

class MyClassTypeDescriptors : CustomTypeDescriptor

    Type typeProp;

    public MyClassTypeDescriptors(ICustomTypeDescriptor parent, Type type)
        : base(parent)
    
        typeProp = type;
    
    //This method will add the additional properties to the object.  
    //It helps to think of the various PropertyDescriptors are columns in a database table
    public override PropertyDescriptorCollection GetProperties(Attribute[] attributes)
    
        PropertyDescriptorCollection cols = base.GetProperties(attributes);
        string propName = ""; //empty string to be populated later
        //find the matching property in the type being called.
        foreach (PropertyDescriptor col in cols)
        
            if (col.PropertyType.Name == typeProp.Name)
                propName = col.Name;
        
        PropertyDescriptor pd = cols[propName];
        PropertyDescriptorCollection children = pd.GetChildProperties(); //expand the child object

        PropertyDescriptor[] propDescripts = new PropertyDescriptor[cols.Count + children.Count];
        int count = cols.Count; //start adding at the last index of the array
        cols.CopyTo(propDescripts, 0);
        //creation of the 'descriptor strings'
        foreach (PropertyDescriptor cpd in children)
        
            propDescripts[count] = new SubPropertyDescriptor(pd, cpd, pd.Name + "_" + cpd.Name);
            count++;
        

        PropertyDescriptorCollection newCols = new PropertyDescriptorCollection(propDescripts);
        return newCols;
    

此时,我们现在有了用于设置与 innre 对象的绑定的“描述符字符串”。 MyClass 的内部属性可以像"MyOtherClass_Property1" 一样调用,其他属性可以像往常一样使用它们的变量名"Property1" 调用

第 3 部分 - 实现 TypeDescriptonProvider

这是我们需要创建的最后一个自定义部分。 TypeDescriptionProvider 是数据绑定对象用来确定对象属性的部分,并且在需要描述符时用于实际调用我们的 CustomTypeDescriptor 类。这也是一个使用泛型的类,但实际上不是泛型类,因为我们必须将它连接到我们的外部对象(也就是正在使用的集合的数据类型)。

class MyClassTypeDescProvider<T> : TypeDescriptionProvider

    private ICustomTypeDescriptor td;

    public DigiRecordBindingTypeDescProvider()
        : this(TypeDescriptor.GetProvider(typeof(MyClass)))
     

    public MyClassTypeDescProvider(TypeDescriptionProvider parent)
        : base(parent)
     

    public override ICustomTypeDescriptor GetTypeDescriptor(Type objectType, object instance)
    
        if (td == null)
        
            td = base.GetTypeDescriptor(objectType, instance);
            td = new MyClassTypeDescriptors(td, typeof(T));
        
        return td;
    

通用类“T”用于指定我们需要链接到父对象的内部对象属性的Type。您将在下一步中看到这是如何工作的。

第 4 部分 - 将我们的 Provider 附加到父类型:

现在我们已经创建了访问存储在内部属性中的数据的基础架构,我们必须告诉系统在查找我们的TypeDescriptors 时使用我们自定义的提供程序。这是使用静态方法完成的:

TypeDescriptor.AddProvider(provider,type)

这应该为每个内部Type 完成,我们需要访问内部作为属性。应在将数据绑定到绑定对象之前添加提供程序,例如设置 UI 对象的DataSource 属性时。

IQueryable<MyClass> myData = PopulateCollectionWithData();
TypeDescriptor.AddProvider(new MyClassTypeDescProvider<MyOtherClass>(), typeof(MyClass));
TypeDescriptor.AddProvider(new MyClassTypeDescProvider<MyThirdClass>(), typeof(MyClass));
DataGridView1.DataSource = myData; //don't bind directly to a collection if you are doing two-way binding.  Use a BindingSource instead!

最后,如果由于某种原因您需要删除此提供程序并恢复为默认值,您可以反向执行完全相同的操作:

TypeDescriptor.RemoveProvider(new MyClassTypeDescProvider<MyOtherClass>(), typeof(MyClass));
TypeDescriptor.RemoveProvider(new MyClassTypeDescProvider<MyThirdClass>(), typeof(MyClass));

请参阅TypeDescriptor Class - MSDN 或The MSDN blog that put me on the right track 了解更多信息。此外,在我对此进行研究期间,我偶然发现了this SO 问题,这促使我发布了一个完整的解释,因为它实际上只是要求这个答案的第 4 部分。我希望这对某人有所帮助,这样他们就不需要像我不必要的那样深入研究System.ComponentModel 库!

【讨论】:

LINQ C# 复杂的嵌套结构

】LINQC#复杂的嵌套结构【英文标题】:LINQC#complexnestingstructure【发布时间】:2017-11-2017:22:39【问题描述】:我设法从对象的复杂结构中做出选择,但只有在foreach的帮助下,我怎样才能避免这种foreach并解决我的问题,只使用LINQ?v... 查看详情

Winforms DataGridView 数据绑定到复杂类型/嵌套属性

】WinformsDataGridView数据绑定到复杂类型/嵌套属性【英文标题】:WinformsDataGridViewdatabindtocomplextype/nestedproperty【发布时间】:2010-10-1514:09:27【问题描述】:我正在尝试将DataGridView数据绑定到包含具有以下结构的类的列表:MyClass.SubCl... 查看详情

c#教程之通过数据绑定修改数据

...库。  26.2.1更新现有数据 使用一个ObjectContext对象获取数据时,根据数据创建的对象位于应用程序的内存缓存中。为了更改缓存中的对象的值,采取的方式和修改任何普通对象中的值一样——设置它们 查看详情

在 C# 中解析嵌套的复杂 JSON 响应

】在C#中解析嵌套的复杂JSON响应【英文标题】:ParsinganestedandcomplexJSONresponseinC#【发布时间】:2017-05-2105:59:55【问题描述】:解析复杂JSON对象的最佳做法是什么,在这种情况下被发布到控制器操作?预先创建一个模型类很脆弱,... 查看详情

GridView 与嵌套类的属性绑定

】GridView与嵌套类的属性绑定【英文标题】:GridViewboundwithPropertiesofnestedclass【发布时间】:2010-11-1022:46:38【问题描述】:我有一个类似于下面列出的对象映射。当我尝试在GridView中绑定NestedClass的属性时,出现错误:“在所选数... 查看详情

与复杂的 .Net 通用对象(例如嵌套的 List<...> 等)一起使用的 ORM?

】与复杂的.Net通用对象(例如嵌套的List<...>等)一起使用的ORM?【英文标题】:ORMsthatworkwithcomplex.Netgenericobjects(e.g.nestedList<...>,etc)?【发布时间】:2009-10-0620:05:52【问题描述】:我刚刚开始为一个新项目建模数据,它必... 查看详情

将配置绑定到复杂对象

】将配置绑定到复杂对象【英文标题】:BindingConfigurationstoaComplexObject【发布时间】:2021-10-0609:11:09【问题描述】:在ASP.NETCore2.2中,我尝试将JSON对象数组绑定到匹配的C#对象,但它没有正确绑定成员。(剧透:Test1有效,Test2无... 查看详情

如何在 C# 中使用嵌套的 json 对象

】如何在C#中使用嵌套的json对象【英文标题】:howtoworkwithnestedjsonobjectinc#【发布时间】:2021-12-1808:08:57【问题描述】:api返回嵌套的json,我不知道从中检索数据并将其映射到我的模型。我想从嵌套中检索数据,但不确定如何使... 查看详情

复杂和嵌套的 json 数据集如何与 pyspark 一起使用

】复杂和嵌套的json数据集如何与pyspark一起使用【英文标题】:Howcomplexandnestedjsondatasetworkswithpyspark【发布时间】:2020-08-3003:25:27【问题描述】:我有一个非常复杂的数据,并在scala中的databricks中处理。我想将该scala转换为python,... 查看详情

模型绑定不适用于嵌套对象

】模型绑定不适用于嵌套对象【英文标题】:Modelbindingnotworkingfornestedobject【发布时间】:2019-08-2711:26:34【问题描述】:我正在尝试在我的模型中绑定从邮递员发布的模型中的数据:publicclassVariantModelpublicintIdget;set;publicList<Subvar... 查看详情

一个复杂查询与多个简单查询

】一个复杂查询与多个简单查询【英文标题】:OnecomplexqueryvsMultiplesimplequeries【发布时间】:2010-10-1515:50:52【问题描述】:实际上什么更好?让具有复杂查询的类负责加载例如嵌套对象?还是具有简单查询的类负责加载简单对象... 查看详情

使用 C# 反序列化复杂的嵌套 JSON

】使用C#反序列化复杂的嵌套JSON【英文标题】:DeserializecomplexnestedJSONwithC#【发布时间】:2021-12-2011:19:19【问题描述】:我想在C#中读取类似这样的JSON。但我不知道如何从JSON中提取特定的块。"took":32,"timed_out":false,"_shards":"total":3,"... 查看详情

c#设计模式之建造者模式

...过程分开,关注如何一步步创建一个包含多个组成部分的复杂对象,用户只需要指定复杂对象的类型即可得到该对象,而无须知道其内部的具体构造细节。建造者模式:将一个复杂对象的构建与它的表示分离,使得同样的构建过... 查看详情

C# 使用字符串数组中的嵌套对象动态创建 JSON

】C#使用字符串数组中的嵌套对象动态创建JSON【英文标题】:C#DynamicallycreateJSONwithnestedobjectsfromarrayofstrings【发布时间】:2021-05-0314:23:24【问题描述】:所以我有一个动态的字符串数组,例如:["a","b","c,"d"]或["x","y","z"]数组是动态... 查看详情

无法在 Knockout.js 中绑定具有复杂对象的视图

】无法在Knockout.js中绑定具有复杂对象的视图【英文标题】:UnabletobindaviewwithcomplexobjectinKnockout.js【发布时间】:2014-06-1717:04:45【问题描述】:我正在尝试使用KnockOut.js在视图上绑定复杂对象。在不使用overservable()和observableArray()... 查看详情

带有stickit.js的backbone.js嵌套对象属性

】带有stickit.js的backbone.js嵌套对象属性【英文标题】:backbone.jsnestedobjectattributewithstickit.js【发布时间】:2013-12-0512:01:27【问题描述】:我正在使用stickit.js来实现双向数据绑定。我很高兴知道如何将stickit与嵌套对象属性绑定。例... 查看详情

函数对象函数嵌套名称空间与作用域闭包函数装饰器

...作为函数的返回值4.函数可以作为容器类型的元素二.函数嵌套1.函数嵌套调用:在函数内又调用了其他函数2.函数嵌套定义:在函数内又定义了其他函数(只能在其函数内部调用,在其函数外部访问不到)三.名称空间名称空间:... 查看详情

officaltutorial

...表达式数据对象绑定数据事件处理方法引用监听绑定避免复杂的监听布局细节importvariable自定义绑定类的名称include表达式一般特性缺失的操作符null的合并操作符属性引用避免NullPointerException集合字符字面值资源数据对象Observa 查看详情