c# wpf MVVM 从 ObservableDictionary 获取 ObservableList

问题描述 投票:0回答:2

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

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

或 linq 查询

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

作为 ObservableDictionary 我使用了这个示例

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

现在的问题是我需要创建一个可以绑定到我的视图的 ObservableCollection。我尝试使用 ObservableValue 属性扩展 ObservableDictionary,但这不起作用

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

当我将模型添加到字典或更新模型时,绑定到视图的 ObservableCollection 将不会更新。

c# wpf linq mvvm observablecollection
2个回答
0
投票

我能想到的最好的解决方案是在你的

ObservableDictionnary
中保留一个包含字典所有值的
ObservableCollection

然后,您必须更改您的类,以便当您在字典中插入/更新/删除一个值时,它在

ObservableCollection
中执行相同的操作,从而触发事件来更新视图。


0
投票

感谢您的建议,但我尝试了 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
}
© www.soinside.com 2019 - 2024. All rights reserved.