c#wpf MVVM从ObservableDictionary获取ObservableList

时间:2017-04-12 13:39:17

标签: c# wpf linq mvvm observablecollection

我的应用程序速度有些问题,并进行了一些性能分析。结果表明,在我的应用程序中有很多时间用linq查询,特别是模型的ID。我的想法是创建一个可观察的字典,其中ID作为键,模型作为值。这非常好,并且比使用

的linq查询要快得多
.Any(x => x.ID == id)

或带

的linq查询
.First(x => x.ID == id)

As 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将不会更新。

2 个答案:

答案 0 :(得分:0)

我能想到的最佳解决方案是保留ObservableDictionnary ObservableCollection包含字典的所有值。

然后你必须更改你的类,这样当你在dicionnary中插入/更新/删除一个值时,它会在ObservableCollection中执行相同的操作,从而触发事件来更新视图。

答案 1 :(得分:0)

感谢您的建议,但我尝试了一些KeyedCollection的实现,并决定将所有Collections / Dictionarys切换到我的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
}