锁定/解锁 ObservableCollection

发布于 2024-12-29 04:45:16 字数 267 浏览 0 评论 0原文

我需要允许或阻止修改 ObservableCollection(或至少是为 WPF 绑定实现 INotifyCollectionChanged 的类型)及其包含的对象在商业规则上。

我可以解决防止修改所包含对象的问题,但不确定如何防止更改集合本身。一种选择是订阅 CollectionChanged 事件并在发生更改后撤消更改,但这并不优雅,并且会给客户端带来令人困惑的契约。

还有哪些其他方法?

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?

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(3

合约呢 2025-01-05 04:45:16

您可以声明自己的类来实现 ICollection(及其朋友)接口,并使底层集合成为该类的成员。您不想继承,因为有人可能会简单地将对象引用强制转换为基类,而您就失去了保护。

在正常情况下,将所有更新委托给基础类,直到集合被锁定。当它被锁定时,开始抛出断言而不是转发下面的调用。并且所有读取操作始终被委托。

这本质上是装饰器模式

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

情感失落者 2025-01-05 04:45:16

您可以创建自己的集合类型并继承 ObservableCollection。这是一个潜在的选择吗?

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

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
}
江心雾 2025-01-05 04:45:16

我采纳了包装 ObservableCollection 的建议。这是我的实现,以防它对任何人有帮助。此方法的一个缺点是所包含对象上的 ILockable.Locked 是公共的,因此可以解锁各个所包含对象,如:wrappedCollection.Item(0).Locked = false;。另外,我还没有实现索引器,因为 EF 索引器上的阻塞< /a>.如果您不使用 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;
        }
    }
}

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;
        }
    }
}
~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文