c# - Winforms 数据绑定(bind)最佳实践

标签 c# winforms data-binding

需求/问题:

  1. 我想将实体的多个属性绑定(bind)到表单中的控件。 其中一些有时是只读的(根据业务逻辑)。 - 编辑:逻辑基于绑定(bind)实例,而不仅仅是其类型。
  2. 当使用实现 INotifyPropertyChanged 的实体作为 DataSource 时,每个更改通知都会刷新绑定(bind)到该数据源的所有控件(易于验证 - 只需将两个属性绑定(bind)到两个控件并在其中一个上调用更改通知,您将看到两个属性都被命中并重新计算)。
  3. 应该有用户友好的错误通知(实体实现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/

相关文章:

c# - 创建新记录时,如何使用过滤器中的值填充某些字段?

c# - 如何在 .net 后端使用 ChannelUri 向特定用户发送通知

c# - 在 C# 中,如何防止在特定代码完成之前多次按下 PreviewKeyDown 事件?

c# - 查看表单中的所有组件

c# - WPF 双向绑定(bind)不起作用

c# - 无法转换 double ?加倍

c# - 从外部 Web 服务自行更新 POCO 模型类

C# Winforms 透明控件允许点击

c# - ASP.NET 中继器交替行突出显示没有完全成熟 <alternatingitemtemplate/>

silverlight - 如何从 PagedCollectionView 中的当前页面获取项目?