DataBinding/WPF C# 的通用可观察字典类

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/1098663/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-08-06 08:11:28  来源:igfitidea点击:

General Observable Dictionary Class for DataBinding/WPF C#

c#wpfdata-binding

提问by Joseph jun. Melettukunnel

I'm trying to create a Observable Dictionary Class for WPF DataBinding in C#. I found a nice example from Andy here: Two Way Data Binding With a Dictionary in WPF

我正在尝试在 C# 中为 WPF 数据绑定创建一个 Observable 字典类。我在这里找到了 Andy 的一个很好的例子:在 WPF 中使用字典进行双向数据绑定

According to that, I tried to change the code to following:

据此,我尝试将代码更改为以下内容:

class ObservableDictionary : ViewModelBase
{
    public ObservableDictionary(Dictionary<TKey, TValue> dictionary)
    {
        _data = dictionary;
    }

    private Dictionary<TKey, TValue> _data;

    public Dictionary<TKey, TValue> Data
    {
        get { return this._data; }
    }

    private KeyValuePair<TKey, TValue>? _selectedKey = null;
    public KeyValuePair<TKey, TValue>? SelectedKey
    {
        get { return _selectedKey; }
        set
        {
            _selectedKey = value;
            RaisePropertyChanged("SelectedKey");
            RaisePropertyChanged("SelectedValue");
        }
    }

    public TValue SelectedValue
    {
        get
        {
            return _data[SelectedKey.Value.Key];
        }
        set
        {
            _data[SelectedKey.Value.Key] = value;
            RaisePropertyChanged("SelectedValue");
        }
    }
}

}

}

Unfortunately I still don't know how to pass "general" Dictionary Objects.. any ideas?

不幸的是,我仍然不知道如何传递“通用”字典对象……有什么想法吗?

Thank you!

谢谢!

Cheers

干杯

采纳答案by Andy

If you really want to make an ObservableDictionary, I'd suggest creating a class that implements both IDictionaryand INotifyCollectionChanged. You can always use a Dictionaryinternally to implement the methods of IDictionaryso that you won't have to reimplement that yourself.

如果您真的想要制作ObservableDictionary,我建议您创建一个同时实现IDictionary和的类INotifyCollectionChanged。您始终可以在Dictionary内部使用 a来实现 的方法,IDictionary这样您就不必自己重新实现它。

Since you have full knowledge of when the internal Dictionarychanges, you can use that knowledge to implement INotifyCollectionChanged.

由于您完全了解内部何时Dictionary发生变化,因此您可以使用该知识来实施INotifyCollectionChanged.

回答by reinierpost

You can't write something that will make somebody else's Dictionary, let alone IDictionary, observable without using some form of reflection. The trouble is that the Dictionary may be a subclass with additional mutators (say, Sort, or Filter, or whatever) that don't invoke Add and Remove and bypass your events as a result.

如果不使用某种形式的反射,您就无法编写可以使其他人的字典(更不用说 IDictionary)可观察的内容。问题是 Dictionary 可能是一个子类,带有额外的 mutator(比如,Sort 或 Filter 或其他),它们不会调用 Add 和 Remove 并因此绕过您的事件。

I believe code generation frameworks exist that allow you do do stuff like this but I'm not familiar with them.

我相信存在代码生成框架可以让你做这样的事情,但我不熟悉它们。

回答by JoanComasFdz

I suggest the following article, where is explanied how to implement an observable dictionary and the source code is available with a sample:

我建议阅读以下文章,其中解释了如何实现可观察字典,并且可以通过示例获得源代码:

http://drwpf.com/blog/2007/09/16/can-i-bind-my-itemscontrol-to-a-dictionary/

http://drwpf.com/blog/2007/09/16/can-i-bind-my-itemscontrol-to-a-dictionary/

回答by Tony Wall

For historical purposes and to put people on the "current" path... It's important to know that Microsoft now solve this requirement in their Windows Store "Basic Page" template in Visual Studio 2012. In order to support the LayoutAwarePage they generate a private ObservableDictionary class.

出于历史目的并让人们走上“当前”路径......重要的是要知道 Microsoft 现在在 Visual Studio 2012 中的 Windows 应用商店“基本页面”模板中解决了这个要求。为了支持 LayoutAwarePage,他们生成了一个私有的ObservableDictionary 类。

However they implement a new IObservableMap interface rather than IDictionary directly. This interface adds a MapChanged event and MapChangedEventHandler, defined in the Windows.Foundation.Collections namespace.

然而,他们直接实现了一个新的 IObservableMap 接口而不是 IDictionary。该接口添加了一个 MapChanged 事件和 MapChangedEventHandler,在 Windows.Foundation.Collections 命名空间中定义。

The snippet below is just the ObservableDictionary class from the LayoutAwarePage.cs generated in the "Common" folder of your project:

下面的代码片段只是在项目的“Common”文件夹中生成的 LayoutAwarePage.cs 中的 ObservableDictionary 类:

    /// <summary>
    /// Implementation of IObservableMap that supports reentrancy for use as a default view
    /// model.
    /// </summary>
    private class ObservableDictionary<K, V> : IObservableMap<K, V>
    {
        private class ObservableDictionaryChangedEventArgs : IMapChangedEventArgs<K>
        {
            public ObservableDictionaryChangedEventArgs(CollectionChange change, K key)
            {
                CollectionChange = change;
                Key = key;
            }

            public CollectionChange CollectionChange { get; private set; }
            public K Key { get; private set; }
        }

        private Dictionary<K, V> _dictionary = new Dictionary<K, V>();
        public event MapChangedEventHandler<K, V> MapChanged;

        private void InvokeMapChanged(CollectionChange change, K key)
        {
            var eventHandler = MapChanged;
            if (eventHandler != null)
            {
                eventHandler(this, new ObservableDictionaryChangedEventArgs(change, key));
            }
        }

        public void Add(K key, V value)
        {
            _dictionary.Add(key, value);
            InvokeMapChanged(CollectionChange.ItemInserted, key);
        }

        public void Add(KeyValuePair<K, V> item)
        {
            Add(item.Key, item.Value);
        }

        public bool Remove(K key)
        {
            if (_dictionary.Remove(key))
            {
                InvokeMapChanged(CollectionChange.ItemRemoved, key);
                return true;
            }
            return false;
        }

        public bool Remove(KeyValuePair<K, V> item)
        {
            V currentValue;
            if (_dictionary.TryGetValue(item.Key, out currentValue) &&
                Object.Equals(item.Value, currentValue) && _dictionary.Remove(item.Key))
            {
                InvokeMapChanged(CollectionChange.ItemRemoved, item.Key);
                return true;
            }
            return false;
        }

        public V this[K key]
        {
            get
            {
                return _dictionary[key];
            }
            set
            {
                _dictionary[key] = value;
                InvokeMapChanged(CollectionChange.ItemChanged, key);
            }
        }

        public void Clear()
        {
            var priorKeys = _dictionary.Keys.ToArray();
            _dictionary.Clear();
            foreach (var key in priorKeys)
            {
                InvokeMapChanged(CollectionChange.ItemRemoved, key);
            }
        }

        public ICollection<K> Keys
        {
            get { return _dictionary.Keys; }
        }

        public bool ContainsKey(K key)
        {
            return _dictionary.ContainsKey(key);
        }

        public bool TryGetValue(K key, out V value)
        {
            return _dictionary.TryGetValue(key, out value);
        }

        public ICollection<V> Values
        {
            get { return _dictionary.Values; }
        }

        public bool Contains(KeyValuePair<K, V> item)
        {
            return _dictionary.Contains(item);
        }

        public int Count
        {
            get { return _dictionary.Count; }
        }

        public bool IsReadOnly
        {
            get { return false; }
        }

        public IEnumerator<KeyValuePair<K, V>> GetEnumerator()
        {
            return _dictionary.GetEnumerator();
        }

        System.Collections.IEnumerator System.Collections.IEnumerable.GetEnumerator()
        {
            return _dictionary.GetEnumerator();
        }

        public void CopyTo(KeyValuePair<K, V>[] array, int arrayIndex)
        {
            if (array == null) throw new ArgumentNullException("array");
            int arraySize = array.Length;
            foreach (var pair in _dictionary)
            {
                if (arrayIndex >= arraySize) break;
                array[arrayIndex++] = pair;
            }
        }
    }

Further examination of the new Windows.Foundation.Collections namespace shows a load of new interfaces defined, but only one PropertySetclass implemented. Actually this seems like a pretty good ObservableDictionary itself. But there must be a reason why MS still generate a private ObservableDictionary. So further examination is required here to identify the pros and cons.

对新的 Windows.Foundation.Collections 命名空间的进一步检查显示了定义的新接口负载,但只实现了一个PropertySet类。实际上,这似乎是一个非常好的 ObservableDictionary 本身。但是MS仍然生成私有ObservableDictionary肯定是有原因的。所以这里需要进一步检查以确定利弊。

In short, either the PropertySet or your own IObservableMap based ObservableDictionary should solve immediate requirements for "current" Windows 8 and Phone 8 projects. However for older frameworks (WPF 4 and Phone 7.5) there is still more work to do.

简而言之,PropertySet 或您自己的基于 IObservableMap 的 ObservableDictionary 应该可以解决“当前”Windows 8 和 Phone 8 项目的直接需求。但是,对于较旧的框架(WPF 4 和 Phone 7.5),还有更多工作要做。

回答by User

Microsoft has an implementation of an observable dictionary in the MSFT.ParallelExtensionsExtraspackage available via Nuget: https://www.nuget.org/packages/ParallelExtensionsExtras/

Microsoft 在MSFT.ParallelExtensionsExtrasNuget 提供的包中实现了可观察字典:https://www.nuget.org/packages/ParallelExtensionsExtras/

ObservableConcurrentDictionary<TKey, TValue>

ObservableConcurrentDictionary<TKey, TValue>