winforms数据绑定最佳实践

时间:2010-03-16 19:35:32

标签: c# winforms data-binding

需求/问题:

  1. 我想将实体的多个属性绑定到表单中的控件。 其中一些只是不时读取(根据业务逻辑)。 - 编辑:逻辑基于绑定实例,而不仅仅基于其类型。
  2. 当使用实现INotifyPropertyChanged的实体作为DataSource时,每个更改通知刷新绑定到该数据源的所有控件(易于验证 - 只需绑定两个属性对于两个控件并在其中一个上调用更改通知,您将看到两个属性都被命中并重新评估。)
  3. 应该有用户友好的错误通知(实体实现IDataErrorInfo)。 (可能使用ErrorProvider
  4. 将实体用作控件的DataSource会导致性能问题,并且当控件只能读取时,会使生活变得更加困难。

    我想过创建一种包含实体和特定属性的包装器,以便每个控件都绑定到不同的DataSource。此外,该包装器可以保存该属性的ReadOnly指示符,因此控件将直接绑定到该值。

    包装器可能如下所示:

    interface IPropertyWrapper : INotifyPropertyChanged, IDataErrorInfo
    {
        object Value { get; set; }
    
        bool IsReadOnly { get; }
    }
    

    但这对每个属性(属性包装器)

    也意味着不同的ErrorProvider

    我觉得我正在尝试重新发明轮子......处理这些复杂的绑定需求的“正确”方法是什么?

    非常感谢。

2 个答案:

答案 0 :(得分:3)

您可以为实现ICustomTypeDescriptor的实体编写包装器。这样,您可以决定哪些属性是只读的......但对于不太复杂的情况,这是非常多的工作。

当您希望该属性为只读时,更简单的解决方案是将绑定的DataSourceUpdateMode更改为Never


更新:这是实现ICustomTypeDescriptor的基本包装器:

class EntityWrapper<T> : CustomTypeDescriptor
{
    public EntityWrapper(T entity)
    {
        this.Entity = entity;
        var properties = TypeDescriptor.GetProperties(typeof(T))
                    .Cast<PropertyDescriptor>()
                    .ToArray();
        ReadOnly = properties.ToDictionary(p => p.Name, p => p.IsReadOnly);
        _properties = new PropertyDescriptorCollection(properties
                            .Select(p => new WrapperPropertyDescriptor(p, this))
                            .ToArray());
    }

    public T Entity { get; private set; }
    public Dictionary<string, bool> ReadOnly { get; private set; }

    public override PropertyDescriptorCollection GetProperties()
    {
        return _properties;
    }

    public override PropertyDescriptorCollection GetProperties(Attribute[] attributes)
    {
        return _properties;
    }

    private PropertyDescriptorCollection _properties;
    private class WrapperPropertyDescriptor : PropertyDescriptor
    {
        private EntityWrapper<T> _entityWrapper;
        private PropertyDescriptor _property;

        public WrapperPropertyDescriptor(PropertyDescriptor property, EntityWrapper<T> entityWrapper)
            : base(property)
        {
            _property = property;
            _entityWrapper = entityWrapper;
        }

        public override bool CanResetValue(object component)
        {
            return _property.CanResetValue(component);
        }

        public override Type ComponentType
        {
            get { return _property.ComponentType; }
        }

        public override object GetValue(object component)
        {
            return _property.GetValue(component);
        }

        public override bool IsReadOnly
        {
            get
            {
                return _entityWrapper.ReadOnly[this.Name];
            }
        }

        public override Type PropertyType
        {
            get { return _property.PropertyType; }
        }

        public override void ResetValue(object component)
        {
            _property.ResetValue(component);
        }

        public override void SetValue(object component, object value)
        {
            _property.SetValue(component, value);
        }

        public override bool ShouldSerializeValue(object component)
        {
            return _property.ShouldSerializeValue(component);
        }
    }
}

正如您所看到的,完全可以将属性设置为只读一个实例:

        MyEntity a = new MyEntity { Foo = "hello", Bar = 42 };
        MyEntity b = new MyEntity { Foo = "world", Bar = 5 };
        EntityWrapper<MyEntity> wa = new EntityWrapper<MyEntity>(a);
        EntityWrapper<MyEntity> wb = new EntityWrapper<MyEntity>(b);

        var fooA = wa.GetProperties()["Foo"];
        var fooB = wb.GetProperties()["Foo"];

        wa.ReadOnly["Foo"] = false;
        wb.ReadOnly["Foo"] = true;

        Console.WriteLine("Property Foo of object a is read-only : {0}", fooA.IsReadOnly);
        Console.WriteLine("Property Foo of object b is read-only : {0}", fooB.IsReadOnly);

答案 1 :(得分:0)

我不会单独包装每个属性...我会包装根域对象。在那里,我将实现readonly逻辑...并且只在readonly标志设置为false时才在真实域objet上设置值。