使CustomCollection允许EditItem(自定义CollectionView?)

时间:2017-09-15 15:48:30

标签: c# wpf collectionviewsource icollectionview

我正在编写一个自定义可观察集合来满足我的一些需求,我希望它在WPF EditItem中使用时允许DataGrid

(请参阅问题末尾的代码集,以避免污染问题的阅读)

你会看到我想到了3个解决方案。但我现在不能让他们中的任何人工作。

解决方案1 ​​

根据此question,如果我希望ObservableDictionary允许EditItem,则必须实施非通用IList。但是当我使用IDictionary作为支持字段来存储元素时,无法正确实现IList接口(因为它基于有序索引)。

除非有人找到方法?

解决方案2

接下来的想法,不是让系统选择CollectionView实现,而是强迫它使用我自己的,如下所示:

<CollectionViewSource 
    x:Key="FooesSource" 
    Source="{Binding Fooes}" 
    CollectionViewType="local:MyCollectionView" />

为此,我尝试覆盖一个现有的CollectionView,一个允许EditItem,以满足我的需求。例如:

class ObservableDictionaryCollectionView : ListCollectionView
{
    public ObservableDictionaryCollectionView(IDictionary dictionary) 
        : base(dictionary.Values)
    {
    }
}

但它不起作用,因为dictionary.ValuesICollectionICollection没有实现IList(它是相反的-_-)。

是否有其他内置的CollectionView可以满足我的需求?

解决方案3

基于下一个想法,我可以尝试从头开始编写自己的CollectionView。但在此之前,我想知道:

  1. 如果有人有更好的主意?
  2. 如果有可能?
  3. 可能是一些字典限制使这个不可能?我应该实现哪些接口? (IEditableCollectionViewIEditableCollectionViewAddNewItemIEditableCollectionViewICollectionViewLiveShaping等)

    正如所承诺的,这是集合的代码:

    public class ObservableDictionary<TKey, TValue> : IDictionary<TKey, TValue>, IEnumerable<TValue>, INotifyCollectionChanged
    {
        #region fields
    
        private IDictionary<TKey, TValue> _innerDictionary;
    
        #endregion
    
        #region properties
    
        public int Count { get { return _innerDictionary.Count; } }
    
        public ICollection<TKey> Keys { get { return _innerDictionary.Keys; } }
    
        public ICollection<TValue> Values { get { return _innerDictionary.Values; } }
    
        public bool IsReadOnly { get { return false; } }
    
        #endregion
    
        #region indexors
    
        public TValue this[TKey key]
        {
            get { return _innerDictionary[key]; }
            set { this.InternalAdd(new KeyValuePair<TKey, TValue>(key, value)); }
        }
    
        #endregion
    
        #region events
    
        public event NotifyCollectionChangedEventHandler CollectionChanged;
    
        #endregion
    
        #region constructors
    
        public ObservableDictionary()
        {
            _innerDictionary = new Dictionary<TKey, TValue>();
        }
    
        public ObservableDictionary(int capacity)
        {
            _innerDictionary = new Dictionary<TKey, TValue>(capacity);
        }
    
        public ObservableDictionary(IEqualityComparer<TKey> comparer)
        {
            _innerDictionary = new Dictionary<TKey, TValue>(comparer);
        }
    
        public ObservableDictionary(IDictionary<TKey, TValue> dictionary)
        {
            _innerDictionary = new Dictionary<TKey, TValue>(dictionary);
        }
    
        public ObservableDictionary(int capacity, IEqualityComparer<TKey> comparer)
        {
            _innerDictionary = new Dictionary<TKey, TValue>(capacity, comparer);
        }
    
        public ObservableDictionary(IDictionary<TKey, TValue> dictionary, IEqualityComparer<TKey> comparer)
        {
            _innerDictionary = new Dictionary<TKey, TValue>(dictionary, comparer);
        }
    
        #endregion
    
        #region public methods
    
        public bool ContainsKey(TKey key)
        {
            return _innerDictionary.ContainsKey(key);
        }
    
        public bool Contains(KeyValuePair<TKey, TValue> item)
        {
            return _innerDictionary.Contains(item);
        }
    
        public void Add(TKey key, TValue value)
        {
            this.InternalAdd(new KeyValuePair<TKey, TValue>(key, value));
        }
    
        public void AddRange(IEnumerable<KeyValuePair<TKey, TValue>> items)
        {
            if (!items.Any())
            {
                return;
            }
    
            var added = new List<TValue>();
            var removed = new List<TValue>();
    
            foreach (var item in items)
            {
                TValue value;
                if (_innerDictionary.TryGetValue(item.Key, out value))
                {
                    removed.Add(value);
                }
    
                added.Add(item.Value);
                _innerDictionary[item.Key] = item.Value;
            }
    
            this.CollectionChanged?.Invoke(this, new NotifyCollectionChangedEventArgs(NotifyCollectionChangedAction.Add, added, null));
    
            if (removed.Count > 0)
            {
                this.CollectionChanged?.Invoke(this, new NotifyCollectionChangedEventArgs(NotifyCollectionChangedAction.Remove, null, removed));
            }
        }
    
        public void Add(KeyValuePair<TKey, TValue> item)
        {
            this.InternalAdd(item);
        }
    
        public bool TryGetValue(TKey key, out TValue value)
        {
            return _innerDictionary.TryGetValue(key, out value);
        }
    
        public bool Remove(TKey key)
        {
            return this.InternalRemove(key);
        }
    
        public bool Remove(KeyValuePair<TKey, TValue> item)
        {
            return this.InternalRemove(item.Key);
        }
    
        public void Clear()
        {
            _innerDictionary.Clear();
            this.CollectionChanged?.Invoke(this, new NotifyCollectionChangedEventArgs(NotifyCollectionChangedAction.Reset));
        }
    
        public void CopyTo(KeyValuePair<TKey, TValue>[] array, int arrayIndex)
        {
            _innerDictionary.CopyTo(array, arrayIndex);
        }
    
        public IEnumerator<TValue> GetEnumerator()
        {
            return Values.GetEnumerator();
        }
    
        IEnumerator IEnumerable.GetEnumerator()
        {
            return this.GetEnumerator();
        }
    
        IEnumerator<KeyValuePair<TKey, TValue>> IEnumerable<KeyValuePair<TKey, TValue>>.GetEnumerator()
        {
            return _innerDictionary.GetEnumerator();
        }
    
        #endregion
    
        #region private methods
    
        /// <summary>
        /// Adds the specified value to the internal dictionary and indicates whether the element has actually been added. Fires the CollectionChanged event accordingly.
        /// </summary>
        /// <param name="key"></param>
        /// <param name="value"></param>
        private void InternalAdd(KeyValuePair<TKey, TValue> item)
        {
            IList added = new TValue[] { item.Value };
    
            TValue value;
            if (_innerDictionary.TryGetValue(item.Key, out value))
            {
                this.CollectionChanged?.Invoke(this, new NotifyCollectionChangedEventArgs(NotifyCollectionChangedAction.Remove, null, new TValue[] { value }));
            }
    
            _innerDictionary[item.Key] = item.Value;
            this.CollectionChanged?.Invoke(this, new NotifyCollectionChangedEventArgs(NotifyCollectionChangedAction.Add, added, null));
        }
    
        /// <summary>
        /// Remove the specified key from the internal dictionary and indicates whether the element has actually been removed. Fires the CollectionChanged event accordingly.
        /// </summary>
        /// <param name="key"></param>
        /// <param name="value"></param>
        private bool InternalRemove(TKey key)
        {
            TValue value;
            if (_innerDictionary.TryGetValue(key, out value))
            {
                _innerDictionary.Remove(key);
                this.CollectionChanged?.Invoke(this, new NotifyCollectionChangedEventArgs(NotifyCollectionChangedAction.Remove, null, new TValue[] { value }));
            }
    
            return value != null;
        }
    
        #endregion
    }
    

1 个答案:

答案 0 :(得分:0)

您可以通过将插入的数据双重存储到内部列表和字典中来构建一个实现Dictionary和List的ObservableCollection。 Dictionary提供了字典的优点,List为绑定操作提供了更好的支持。

缺点包括加倍内存使用量,并损害您的项目删除时间*

*插入**和访问具有与常规字典相同的效率,但删除也必须在内部列表中找到该项

**我想在需要数组调整大小的情况下插入也会更加昂贵。