2010-03-16 64 views
3

需求/问题:的WinForms数据绑定最佳做法

  1. 我想将一个实体的多个属性绑定到控件的形式。 其中一些只能不时读取(根据业务逻辑)。 - 编辑:逻辑基于绑定实例,而不仅仅是它的类型。
  2. 当使用实现INotifyPropertyChanged作为DataSource的实体,每一个变化通知刷新绑定到数据源的所有控制(容易验证 - 只是结合两个属性两个控制和对它们中的一个调用改变通知,你会看到这两个属性都被击中和重新评估)。
  3. 应该有用户友好的错误通知(该实体执行IDataErrorInfo)。 (可能使用ErrorProvider

使用实体作为对照的DataSource导致性能问题,使生活困难时,为只读它的控制时间。

我想创建一些包装的实体和一个特定的属性,使每个控件将绑定到不同的DataSource。而且,该包装器可以容纳该属性的ReadOnly指示器,因此控件将直接绑定到该值。

的包装看起来是这样的:

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

    bool IsReadOnly { get; } 
} 

但是,这也意味着不同的ErrorProvider每个属性(属性包装)

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

提前感谢。

回答

0

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

+0

嘿请注意,每个属性都具有不同的只读逻辑。你将如何包装域对象来启用它? 关于将相同的数据源绑定到许多控件上的性能问题呢? – 2010-03-16 19:46:05

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); 
+0

“ICustomTypeDescriptor”接口允许您描述TYPE而不是单个实体,因此只读逻辑将按类型而非每个实例。设置'DataSourceUpdateMode.Never'仍然让用户键入/更改控件值,并且控件将不会显示为只读... – 2010-03-16 20:05:23

+0

“所以只读逻辑将是每个类型,而不是每个实例”:不是必需的,请参阅我的更新回答 – 2010-03-16 21:42:02

+0

不幸的是,我已经试过走过这条路,从我的经验来看,这个解决方案并不是最优的。 当考虑属性描述符*的责任时,然后使其实现'INotityPropertyChanged'(因为我们希望显示更改)并让EntityWrapper成员,但将组件传递到打包的'_property' ...好像它只是似乎错了(并与示例/测试实施不会工作)。此外 - 这迫使我们把我们的控制绑定到两个不同的数据源(这个实体 - 值和描述符 - 只读指示) – 2010-03-16 23:15:26