winforms привязка данных лучшие практики - PullRequest
3 голосов
/ 16 марта 2010

Требования / проблемы:

  1. Я хотел бы связать несколько свойств объекта с элементами управления в форме. Некоторые из них только для чтения время от времени (согласно бизнес-логике). - Правка: Логика основана на связанном экземпляре, а не только на его типе.
  2. При использовании объекта, который реализует INotifyPropertyChanged в качестве DataSource, каждое уведомление об изменении обновляет все элементы управления, связанные с этим источником данных (легко проверить - просто свяжите два свойства с двумя элементами управления и вызовите уведомление об изменении на одном из них, вы увидите, что оба свойства были проверены и переоценены).
  3. Должно быть удобных уведомлений об ошибках (сущность реализует IDataErrorInfo). (возможно, используя ErrorProvider)

Использование объекта в качестве DataSource элементов управления приводит к проблемам с производительностью и усложняет жизнь, когда время для элемента управления доступно только для чтения.

Я думал о создании какой-то обертки, которая содержит сущность и определенное свойство, чтобы каждый элемент управления был связан с различным DataSource. Более того, эта оболочка может содержать индикатор ReadOnly для этого свойства, поэтому элемент управления будет напрямую связан с этим значением.

Оболочка может выглядеть так:

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

    bool IsReadOnly { get; }
}

Но это также означает разные ErrorProvider для каждого свойства (оболочки свойств)

Мне кажется, что я пытаюсь заново изобрести колесо ... Каков «правильный» способ справиться со сложными связующими требованиями, подобными этим?

Спасибо, вперед.

Ответы [ 2 ]

3 голосов
/ 16 марта 2010

Вы можете написать оболочку для вашей сущности, которая реализует 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 голосов
/ 16 марта 2010

Я бы не оборачивал каждое свойство индивидуально ... Я бы обернул объект корневого домена. Там я бы реализовал логику только для чтения ... и установил бы значение для объекта реального домена, только если для флага только для чтения установлено значение false.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...