c# wpf MVVM Get ObservableList from ObservableDictionary



我在应用程序的速度方面遇到了一些问题,并做了一些性能分析。结果表明,我的应用程序中有很多时间花在 linq 查询上,尤其是模型的 ID。我的想法是创建一个可观察的字典,其中ID作为键,模型作为值。这工作得很好,并且比 linq 查询快得多

.Any(x => x.ID == id)

或 linq 查询

.First(x => x.ID == id)

作为可观察词典,我使用了此示例

http://blogs.microsoft.co.il/shimmy/2010/12/26/observabledictionarylttkey-tvaluegt-c/

现在的问题是我需要创建一个可以绑定到我的视图的可观察集合。我试图使用可观察值属性扩展可观察字典,但这不起作用

     public ObservableCollection<TValue> ObservableValues
    {
        get
        {
            if (observableValues == null)
            {
                lock (lockObject)
                {
                    if (observableValues == null)
                        observableValues = new ObservableCollection<TValue>(Dictionary.Values);
                }
            }
            return observableValues;
        }
    }

当我将模型添加到字典或更新模型时,绑定到视图的可观察集合将不会更新。

我能想到的最好的解决方案是在你的ObservableDictionnary中保留一个包含字典所有值的ObservableCollection

然后,您必须更改类,以便在 dicionnary 中插入/更新/删除值时,它在ObservableCollection中执行相同的操作,从而触发事件以更新视图。

感谢您的建议,但我尝试了一些 KeyedCollection 的实现,并决定将所有集合/字典切换到我的自定义 KeyedCollection 实现。性能与字典一样快,但不要使用KVP。这是我使用一些替换方法的可观察实现,它运行得非常快。

public class ObservableKeyedCollection<TKey, TItem> : KeyedCollection<TKey, TItem>, INotifyCollectionChanged
{
    private const string CountString = "Count";
    private readonly Func<TItem, TKey> _getKeyForItemDelegate;
    // Constructor now requires a delegate to get the key from the item
    public ObservableKeyedCollection(Func<TItem, TKey> getKeyForItemDelegate) : base()
    {
        if (getKeyForItemDelegate == null)
            throw new ArgumentNullException("Delegate passed can't be null!");
        _getKeyForItemDelegate = getKeyForItemDelegate;
    }
    protected override TKey GetKeyForItem(TItem item)
    {
        return _getKeyForItemDelegate(item);
    }
    /// <summary>
    /// Method to add a new object to the collection, or to replace an existing one if there is 
    /// already an object with the same key in the collection.
    /// </summary>
    public void AddOrReplace(TItem newObject)
    {
        int i = GetItemIndex(newObject);
        if (i != -1)
            SetItem(i, newObject);
        else
            Add(newObject);
    }

    /// <summary>
    /// Method to replace an existing object in the collection, i.e., an object with the same key. 
    /// An exception is thrown if there is no existing object with the same key.
    /// </summary>
    public void Replace(TItem newObject)
    {
        int i = GetItemIndex(newObject);
        if (i != -1)
            SetItem(i, newObject);
        else
            throw new Exception("Object to be replaced not found in collection.");
    }

    /// <summary>
    /// Method to get the index into the List{} in the base collection for an item that may or may 
    /// not be in the collection. Returns -1 if not found.
    /// </summary>
    private int GetItemIndex(TItem itemToFind)
    {
        TKey keyToFind = GetKeyForItem(itemToFind);
        if (this.Contains(keyToFind))
            return this.IndexOf(this[keyToFind]);
        else return -1;
    }
    // Overrides a lot of methods that can cause collection change
    protected override void SetItem(int index, TItem item)
    {
        var oldItem = base[index];
        base.SetItem(index, item);
        OnCollectionChanged(NotifyCollectionChangedAction.Replace, item, oldItem);
    }
    protected override void InsertItem(int index, TItem item)
    {
        base.InsertItem(index, item);
        OnCollectionChanged(NotifyCollectionChangedAction.Add, item);
    }
    protected override void ClearItems()
    {
        base.ClearItems();
        OnCollectionChanged();
    }
    protected override void RemoveItem(int index)
    {
        TItem item = this[index];
        base.RemoveItem(index);
        OnCollectionChanged(NotifyCollectionChangedAction.Remove, item);
    }
    private bool _deferNotifyCollectionChanged = false;
    public void AddRange(IEnumerable<TItem> items)
    {
        _deferNotifyCollectionChanged = true;
        foreach (var item in items)
            Add(item);
        _deferNotifyCollectionChanged = false;
        OnCollectionChanged(new NotifyCollectionChangedEventArgs(NotifyCollectionChangedAction.Reset));
    }
    protected virtual void OnCollectionChanged(NotifyCollectionChangedEventArgs e)
    {
        if (_deferNotifyCollectionChanged)
            return;
        if (CollectionChanged != null)
            CollectionChanged(this, e);
    }
    #region INotifyCollectionChanged Members
    public event PropertyChangedEventHandler PropertyChanged;
    public event NotifyCollectionChangedEventHandler CollectionChanged;
    private void OnPropertyChanged()
    {
        OnPropertyChanged(CountString);
    }

    protected virtual void OnPropertyChanged(string propertyName)
    {
        if (PropertyChanged != null) PropertyChanged(this, new PropertyChangedEventArgs(propertyName));
    }

    private void OnCollectionChanged()
    {
        if (_deferNotifyCollectionChanged)
            return;
        OnPropertyChanged();
        if (CollectionChanged != null) CollectionChanged(this, new NotifyCollectionChangedEventArgs(NotifyCollectionChangedAction.Reset));
    }

    private void OnCollectionChanged(NotifyCollectionChangedAction action, TItem changedItem)
    {
        if (_deferNotifyCollectionChanged)
            return;
        OnPropertyChanged();
        if (CollectionChanged != null) CollectionChanged(this, new NotifyCollectionChangedEventArgs(action, changedItem));
    }

    private void OnCollectionChanged(NotifyCollectionChangedAction action, TItem newItem, TItem oldItem)
    {
        if (_deferNotifyCollectionChanged)
            return;
        OnPropertyChanged();
        if (CollectionChanged != null) CollectionChanged(this, new NotifyCollectionChangedEventArgs(action, newItem, oldItem));
    }

    private void OnCollectionChanged(NotifyCollectionChangedAction action, IList newItems)
    {
        if (_deferNotifyCollectionChanged)
            return;
        OnPropertyChanged();
        if (CollectionChanged != null) CollectionChanged(this, new NotifyCollectionChangedEventArgs(action, newItems));
    }
    #endregion
}

最新更新