65.9K
CodeProject 正在变化。 阅读更多。
Home

复杂数据绑定:实现IBindingList和ITypedList的集合

starIconstarIconstarIconstarIcon
emptyStarIcon
starIcon

4.60/5 (22投票s)

2003年3月27日

2分钟阅读

viewsIcon

197188

downloadIcon

3802

复杂数据绑定:实现IBindingList和ITypedList的集合。

Sample Image - ComplexDataBinding.jpg

引言

数据绑定可能很棘手 - 尤其是当您无法使用 System.Data 对象时。我试图使用数据绑定的所有功能来向您展示它们。它很小,但要做到这一点需要很长时间。

该示例展示了一个自定义集合,其中包含具有属性的对象,其中一些属性又是集合,其中包含对象 - 即分层数据。也许某些属性无法直接编辑,因为网格不够智能 - 例如 SqlTypes

问题

我们有一个需要放入 Windows DataGrid 的对象集合。它们可能具有也可能没有作为集合的属性。

解决方案

我们需要创建以下对象

  • 一个实现 ITypedListIBindingList 的类(您的集合)
  • 一个作为您数据的类(大量属性等)
  • 一个实现 IComparer 的类(用于排序)
  • 一个实现 PropertyDescriptor 的类(用于使用非网格兼容类型,例如 SqlTypes
  • 一个实现 Attribute 的类(以便我们知道集合中有什么)

对于那些尝试过但没有成功的人,我将讨论几个出现的问题。

ITypedList.GetItemProperties(PropertyDescriptor[] listAccessors) 

这是项目中界面最棘手的方法。问题与 listAccessors 有关。如果参数不是 null,则应该只处理最后一个。我不得不反编译 System.Data.DataView.ITypedList.GetItemProperties 才能找到它。然后我需要检测我们的自定义集合并返回基础数据的属性。

SqlPropertyDescriptor : PropertyDescriptor

这个虽然实现起来很简单,但在调用 GetItemProperties 时有点棘手,并且我们希望使用此 PropertyDesciptor 而不是默认值,我们需要在集合中替换它。我写了这个方法来做到这一点

protected PropertyDescriptorCollection 
    GetPropertyDescriptorCollection( ArrayList properties )
{
    if ( properties == null || properties.Count == 0 )
        return new PropertyDescriptorCollection(null);

    ArrayList output = new ArrayList();

    foreach ( PropertyDescriptor p in properties )
    {
        if ( p.Attributes.Matches(new Attribute[]
            {new BindableAttribute(false)}) ) continue;

        if ( p.PropertyType.Namespace == "System.Data.SqlTypes" )
        {
            // create the base type property descriptor
            output.Add(SqlPropertyDescriptor.
                GetProperty( p.Name, p.PropertyType ) );
        }
        else
        {
            output.Add(p);
        }
    }
    return new PropertyDescriptorCollection((PropertyDescriptor[])
        output.ToArray(typeof(PropertyDescriptor)));
}

在那之后,一切都很顺利。

PropertyDescriptorSetValueGetValue 提供了编辑 SqlTypes(如 SqlDateTime)的方法。在检测到数据类型的命名空间后,我使用 SqlPropertDescriptor,它实现了 GetValue()SetValue()。这些方法试图为自己解决所有问题

public override void SetValue(object component,object value)
{
    try
    {
        PropertyInfo pi = component.GetType().GetProperty(this.Name);
        Object o;
        if ( value == DBNull.Value )
        {
            o = component.GetType().GetField("Null", 
                BindingFlags.Static | BindingFlags.Public | 
                BindingFlags.GetField).GetValue(component);
                
        }
        else
        {
            o = pi.PropertyType.GetConstructor(new Type[]
                {BaseType}).Invoke(new Object[]{value});
        }
        pi.SetValue(component,o, null);
    }
    catch(Exception ex)
    {
        Debug.WriteLine(ex);
    }
}

public override object GetValue(object component)
{
    try
    {
        object Property = component.GetType().GetProperty
            (this.Name).GetValue(component,null);

        // handle nulls
        if ( (bool)Property.GetType().GetProperty
            ("IsNull").GetValue(Property,null) ) 
            return DBNull.Value;

        object Value = Property.GetType().GetProperty
            ("Value").GetValue(Property,null);
        return Value;
    }
    catch(Exception ex)
    {
        Debug.WriteLine(ex);
    }
    return null;
}

许可证

本文未附加明确的许可证,但可能在文章文本或下载文件本身中包含使用条款。如有疑问,请通过下面的讨论区联系作者。

作者可能使用的许可证列表可以在此处找到。

© . All rights reserved.