Блокировка / разблокировка ObservableCollection <T> - PullRequest
2 голосов
/ 26 января 2012

У меня есть требование разрешить или запретить изменение ObservableCollection<T> (или, по крайней мере, типа, который реализует INotifyCollectionChanged для привязки WPF), а также объектов, которые оно содержит на основе бизнес-правила.

Я могу решить проблему предотвращения изменения содержащихся объектов, но не знаю, как подойти к предотвращению изменения самой коллекции.Один из вариантов - подписаться на событие CollectionChanged и отменить изменения после того, как они произошли, но это не изящно и представляет собой запутанный контракт для клиента.

Какие существуют другие подходы?

Ответы [ 3 ]

3 голосов
/ 26 января 2012

Вы можете объявить свой собственный класс, который реализует интерфейсы ICollection (и друзей) и иметь базовую коллекцию, являющуюся членом этого класса. Вы не захотите наследовать, потому что тогда кто-то может просто привести ссылку на объект к базовому классу, и вы потеряете свою защиту.

В обычных условиях делегируйте все обновления базовому классу, пока коллекция не станет заблокированной. Когда он заблокирован, начните выдавать утверждения вместо переадресации вызовов ниже. И все операции чтения всегда делегируются.

По сути, это будет шаблон декоратора

2 голосов
/ 26 января 2012

Я пошел с предложениями обернуть ObservableCollection<T>.Вот моя реализация на случай, если она кому-нибудь поможет.Одним из недостатков этого подхода является то, что ILockable.Locked для содержащегося объекта является открытым, поэтому отдельные содержащиеся объекты могут быть разблокированы, например: wrappedCollection.Item(0).Locked = false;.Кроме того, я не реализовал индексатор, потому что EF душит индексаторы .Не стесняйтесь добавлять индексатор, если вы не используете EF.

public interface ILockable
{
    bool Locked { get; set; }
}

public class LockableObservableCollection<T> : ICollection<T>, INotifyCollectionChanged, INotifyPropertyChanged, ILockable
    where T: ILockable
{
    protected ObservableCollection<T> Collection { get; set; }

    public LockableObservableCollection()
    {
        Collection = new ObservableCollection<T>();
        Collection.CollectionChanged += new NotifyCollectionChangedEventHandler(Collection_CollectionChanged);
        ((INotifyPropertyChanged)Collection).PropertyChanged += 
            new PropertyChangedEventHandler(LockableObservableCollection_PropertyChanged);
    }

    void LockableObservableCollection_PropertyChanged(object sender, PropertyChangedEventArgs e)
    {
        if (PropertyChanged != null) PropertyChanged(this, e);
    }

    void Collection_CollectionChanged(object sender, NotifyCollectionChangedEventArgs e)
    {
        if (CollectionChanged != null) CollectionChanged(this, e);
    }

    public T Item(int index)
    {
        return Collection[index];
    }

    #region ICollection<T>

    public void Add(T item)
    {
        if (Locked) throw new Exception("Collection is locked.");

        Collection.Add(item);
    }

    public void Clear()
    {
        if (Locked) throw new Exception("Collection is locked.");

        Collection.Clear();
    }

    public bool Contains(T item)
    {
        return Collection.Contains(item);
    }

    public void CopyTo(T[] array, int arrayIndex)
    {
        Collection.CopyTo(array, arrayIndex);
    }

    public int Count
    {
        get { return Collection.Count; }
    }

    public bool IsReadOnly
    {
        get { return Locked; }
    }

    public bool Remove(T item)
    {
        if (Locked) throw new Exception("Collection is locked.");

        bool result = Collection.Remove(item);
        return result;
    }

    public IEnumerator<T> GetEnumerator()
    {
        return Collection.GetEnumerator();
    }

    System.Collections.IEnumerator System.Collections.IEnumerable.GetEnumerator()
    {
        return GetEnumerator();
    }
    #endregion

    #region INotifyCollectionChanged
    public event NotifyCollectionChangedEventHandler CollectionChanged;
    #endregion

    #region IPropertyChanged
    public event PropertyChangedEventHandler PropertyChanged;
    #endregion

    private bool locked;
    public bool Locked
    {
        get
        {
            return locked;
        }
        set
        {
            if (locked != value)
            {
                locked = value;
                foreach (T t in Collection)
                {
                    t.Locked = value;
                }
                if (PropertyChanged != null)
                {
                    PropertyChanged(this, new PropertyChangedEventArgs("Locked"));
                }

            }
        }
    }
}

Содержащийся объект, который реализует ILockable, должен по-прежнему применять свойство Locked, например что-то вроде этого:

private string text;

public string Text
{
    get { return text; }
    set
    {
        if (text != value)
        {
            if (Locked) throw new Exception("This item is locked to prevent changes.");
            text = value;
        }
    }
}
2 голосов
/ 26 января 2012

Вы можете создать свой собственный тип коллекции и наследовать ObservableCollection<T>. Это потенциальный вариант?

public class ReadOnlyObservableCollection<T> : ObservableCollection<T>
{
    // method overrides with conditional logic to allow/deny changes
}
...