需求/问题:
- 我想将实体的多个属性绑定(bind)到表单中的控件。 其中一些有时是只读的(根据业务逻辑)。 - 编辑:逻辑基于绑定(bind)实例,而不仅仅是其类型。
- 当使用实现
INotifyPropertyChanged
的实体作为DataSource
时,每个更改通知都会刷新绑定(bind)到该数据源的所有控件(易于验证 - 只需将两个属性绑定(bind)到两个控件并在其中一个上调用更改通知,您将看到两个属性都被命中并重新计算)。 - 应该有用户友好的错误通知(实体实现
IDataErrorInfo
)。 (可能使用ErrorProvider
)
将实体用作控件的 DataSource
会导致性能问题,并且在控件为只读状态时使工作变得更加困难。
我想创建某种包装器来保存实体和特定属性,以便每个控件都绑定(bind)到不同的 DataSource
。此外,该包装器可以保存该属性的 ReadOnly
指示符,因此控件将直接绑定(bind)到该值。
包装器看起来像这样:
interface IPropertyWrapper : INotifyPropertyChanged, IDataErrorInfo
{
object Value { get; set; }
bool IsReadOnly { get; }
}
但这也意味着每个属性(属性包装器)的不同 ErrorProvider
我觉得我正在重新发明轮子……处理这些复杂绑定(bind)需求的“正确”方法是什么?
提前致谢。
最佳答案
您可以为实现 ICustomTypeDescriptor
的实体编写一个包装器。这样,您就可以决定哪些属性是只读的或不是……但对于一个不那么复杂的场景来说,这是相当多的工作。
当您希望属性为只读时,一个更简单的解决方案是将绑定(bind)的 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);
关于c# - Winforms 数据绑定(bind)最佳实践,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/2457481/