Lock / Unlock ObservableCollection<T>

邮差的信 提交于 2019-12-11 03:55:16

问题


I have a requirement to allow or prevent modification to an ObservableCollection<T> (or at least a type that implements INotifyCollectionChanged for WPF binding) as well as the objects it contains based on a business rule.

I can solve the issue of preventing modification to contained objects, but am not sure how to approach preventing change to the collection itself. One option would be to subscribe to the CollectionChanged event and undo changes after they happen, but that is not elegant and represents a confusing contract to the client.

What other approaches are there?


回答1:


You could declare your own class that implements ICollection (and friends) interfaces and have the underlying collection be a member of that class. You wouldn't want to inherit because then someone might simply cast object reference to the base class and you've lost your protection.

Under normal conditions, delegate all updates to the underlying class until the collection becomes locked. When it's locked, start throwing assertions instead of forwarding the calls below. And all read operations are always delegated.

This would essentially be a decorator pattern




回答2:


You could create your own collection type and inherit ObservableCollection<T>. Is that a potential option?

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



回答3:


I went with the suggestions to wrap an ObservableCollection<T>. Here's my implementation in case it helps anyone. One drawback of this approach is that ILockable.Locked on the contained object is public, so individual contained objects could be unlocked like: wrappedCollection.Item(0).Locked = false;. Also, I have not implemented an indexer because EF chokes on indexers. Feel free to add an indexer if you're not using 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"));
                }

            }
        }
    }
}

The contained object that implements ILockable must still enforce the Locked property, e.g. something like this:

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;
        }
    }
}


来源:https://stackoverflow.com/questions/9013060/lock-unlock-observablecollectiont

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!