Winforms databinding best practices

Requirements / Problems:

  • I would like to bind several properties of the object to the controls in the form. Some of them are only read from time to time (in accordance with business logic). - Edit: . Logic is based on a linked instance, not just its type.
  • When using an object that implements INotifyPropertyChangedhow DataSource, each change notification updates all the controls bound to this data source (it’s easy to check - just bind two properties to two controls and trigger a change notification on one of them, you will see that both properties removed and revalued).
  • There should be friendly error notifications (the object implements IDataErrorInfo). (possibly using ErrorProvider)

Using an object as a DataSourcecontrol leads to performance problems and complicates life when the time for monitoring is read-only.

I was thinking of creating some kind of wrapper that contains an object and a specific property so that each control is bound to another DataSource. Moreover, this shell may contain an indicator ReadOnlyfor this property, so the control will be bound directly to this value.

The wrapper might look like this:

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

    bool IsReadOnly { get; }
}

But it also means different ErrorProviderfor each property (property wrapper)

, ... "" , ?

.

+3
2

, ICustomTypeDescriptor. , , ... .

DataSourceUpdateMode Never, , .


UPDATE: 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);
        }
    }
}

, read-only :

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

... . readonly... objet, readonly false.

0

Source: https://habr.com/ru/post/1737214/


All Articles