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

将无属性实体绑定到 Windows 窗体控件

starIconemptyStarIconemptyStarIconemptyStarIconemptyStarIcon

1.00/5 (4投票s)

2008年2月29日

CPOL

3分钟阅读

viewsIcon

26038

downloadIcon

210

如何避免实体对象的属性编码

引言

将没有属性的实体绑定到 Windows 窗体控件。

背景

大多数开发者不喜欢为实体的每个字段编写属性(尽管在我所知道的所有框架中,属性是强制性的)。 所有 Window Forms 控件的数据绑定机制都需要这些属性。 标准数据绑定也不支持绑定嵌入对象属性(例如 Person.address.City)。 因为我厌倦了编写冗余代码(属性和虚拟对象,以便能够绑定嵌入对象属性),所以诞生了这个小型框架的想法。

基础实体 BaseEntiy 提供了框架数据绑定机制所需的所有方法。 实体只定义私有或公共字段。 GenericEntityList<T>GenericEntityPropertyDescriptor 类是将所有属性调用转换为 BaseEntity 类的 GetValue<T>SetValue<T> 方法的唯一标准数据绑定类的扩展。

目前,该框架仅支持 DataGridView 的数据绑定。

Using the Code

让我们从使用框架的一个简单示例开始。 创建一个新项目(类库),设置对 *GenericEntity.dll* 的引用并创建一个类,例如 Person。 Person 类有五个私有成员:No、Name、Surname、Birthdate 和 Address。 为了支持框架的数据绑定机制,Person 类必须继承自 BaseEntity。 使用私有成员时,设置 PropertyVisibility 属性来控制可见性。

public class Person:BaseEntity
{
    [PropertyVisibility(true)]
    private Int32 no;
    [PropertyVisibility(true)]
    private String name;
    [PropertyVisibility(true)]
    private String surname;
    [PropertyVisibility(true)]
    private DateTime dateOfBirth;
    [PropertyVisibility(true)]
        private Address address;
}

现在创建第二个项目(Windows 应用程序),设置对 *GenericEntity.dll* 和 *SampleLib.dll* 的引用,创建一个 Form 并向 Form 添加一个 DataGridView 控件。

要将 PersonCollection 绑定到 DataGridView,请将 GenericEntityList<Person> 设置为 DataGridView 的数据源。

Person person = new Person();
person.SetValue<Int32>("no", 1);
person.SetValue<String>("name", "Donald");
person.SetValue<String>("surname", "Duck");
person.SetValue<DateTime>("dateOfBirth", new DateTime(1934, 6, 9));

Address address = new Address();
address.SetValue<String>("city", "Ducktown");
address.SetValue<String>("street", "Duck Str. 1");

person.SetValue<Address>("address", address);
                
GenericEntityList<Person> persons = new GenericEntityList<Person>();
persons.Add(person);
PersonsDataGridView.DataSource = persons;

Person 的所有公共或可见标记成员都会显示在 DataGridView 中。 还会显示嵌入的 BaseEntity 类的所有可见标记成员。 GenericEntityList<T> 支持添加新行和排序。

关注点

反射和泛型是 .NET 2.0 框架最有价值的特性。 我的小型数据绑定框架结合了两者,无需编写冗余属性即可处理数据绑定机制。 让我们看一下 GenericEntityList<T> 类。 GetItemProperties 方法将数据源实体的所有公共属性发布到绑定类。 为了支持绑定没有属性的实体,GenericEntityList<T> 收集实体的所有公共或可见标记的私有成员,并将此 PropertyDescriptorCollection 发布到绑定类。

public PropertyDescriptorCollection GetItemProperties(PropertyDescriptor[] listAccessors)
{
    T entity = items[0];
    IList<GenericEntityPropertyDescriptor> genericProperties = GetVisibleProperties(
        entity.GetType(), null);
    PropertyDescriptor[] propertyDescriptors =
        new PropertyDescriptor[genericProperties.Count];
    int index = 0;
    foreach (GenericEntityPropertyDescriptor genericProperty in genericProperties)
    {
        propertyDescriptors[index] = genericProperty;
        index++;
    }
    return new PropertyDescriptorCollection(propertyDescriptors);
}

internal IList<GenericEntityPropertyDescriptor> GetVisibleProperties(Type entityType,
    String rootPropertyName)
{
    FieldInfo[] fields = GetFieldInfos(entityType);
    IList<GenericEntityPropertyDescriptor> propertyList =
        new List<GenericEntityPropertyDescriptor>();
    foreach (FieldInfo fieldInfo in fields)
    {
        if (FieldIsVisible(fieldInfo))
        {
            String propertyName = fieldInfo.Name;
            if (rootPropertyName != null)
                propertyName = rootPropertyName + "." + fieldInfo.Name;
            if (fieldInfo.FieldType.BaseType.Equals(typeof(BaseEntity)))
            {
                Type subEntityType = fieldInfo.FieldType;
                foreach (
                    GenericEntityPropertyDescriptor propertyDescriptor in
                        GetVisibleProperties(subEntityType, propertyName))
                {
                    propertyList.Add(propertyDescriptor);
                }
            }
            else
            {
                propertyList.Add(
                    new GenericEntityPropertyDescriptor(propertyName, items[0]));
            }
        }
    }
    return propertyList;
}
        
internal Boolean FieldIsVisible(FieldInfo info)
{
    if (!info.IsPublic)
    {
        PropertyVisibilityAttribute[] attributes =
            (PropertyVisibilityAttribute[])info.GetCustomAttributes(typeof(
            PropertyVisibilityAttribute), true);
        if (attributes == null)
            return false;
        if (!attributes[0].IsVisible)
            return false;
    }
    return true;
}

为了支持排序,需要自定义 Comparer。 在此框架中,Comparer 是一个泛型类 GenericEntityComparer<T>,其中 T 必须继承自 BaseEntityConstructor 期望两个参数,String fieldNameType fieldType。 虽然 GenericEntityList<T> 必须创建 GenericEntityComparer<T> 的实例,但必须通过反射来完成创建。 要创建泛型类的实例,需要通过方法 MakeGenericTypeTypes 列表传递给泛型类型的泛型类型参数。 GenericEntityComparer<T> 类型的 GetConstructor 方法也需要 Constructor 参数的 Types。 要创建实例,请使用 Constructor 参数的值调用 ConstructorInfoInvoke 方法。

internal IComparer<T> GetComparer(PropertyDescriptor property)
{
    Type genericType = typeof(GenericEntityComparer<>);
    genericType = genericType.MakeGenericType(new Type[1]{typeof(T)});
    ConstructorInfo constructorInfo = genericType.GetConstructor(
        new Type[2]{typeof(String), typeof(Type)});
    object[] parameters = new object[2];
    parameters[0] = property.Name;
    parameters[1] = items[0].GetFieldType(property.Name);
    return (IComparer<T>)constructorInfo.Invoke(parameters);
}

IComparer<T>Compare 实现使用 BaseEntity 的泛型方法 GetValue<T>。 由于该方法是泛型的,因此使用反射来调用它。

public int Compare(T x, T y)
{
    MethodInfo methodInfo = x.GetType().GetMethod("GetValue");
    MethodInfo genericMethodInfo = methodInfo.MakeGenericMethod(
        x.GetFieldType(fieldName));
    object[] parameters = new object[1];
    parameters[0] = fieldName;
    IComparable xComparable = ((IComparable)genericMethodInfo.Invoke(x, parameters));
    IComparable yComparable = ((IComparable)genericMethodInfo.Invoke(y, parameters));

    return xComparable.CompareTo(yComparable);
}

该实现假设 IComparable Type 以一种不雅的方式进行比较,但我还没有找到避免容易出错的强制转换的方法。

© . All rights reserved.