General Observable Dictionary Class for DataBinding/WPF C#
Asked Answered
B

6

26

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

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

Bekki answered 8/7, 2009 at 15:7 Comment(3)
Could you give a little more detail as to what you are trying to do? Can you show an example of the code that you would like to be able to write when you say "pass a general dictionary..."Toxicogenic
I have different Dictionaries for e.g. a Postal Code and a City. What I'm trying to do is: - Bind the Data (Model/the dictionary) to my WPF ItemsControl, so the user can e.g. change the city of the postal code and the model gets automaticly updated. Unfortunately only OneWay-Binding is possible with the "normal" Dictionary, because I'd need INotifyPropertyChanged. - Create a ObservableDictionary, which implements INotifyPropertyChanged and also contains a dictionaryBekki
The solution is there: https://mcmap.net/q/418394/-net-observabledictionaryTruditrudie
A
35

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

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

Antwanantwerp answered 8/7, 2009 at 18:0 Comment(3)
any chance you could elaborate with code? you will be helping a lot of noobs (like me) trawling stackOverflow for answers - 23k views already. chrsFlump
@BKSpurgeon There is a sample already available: blogs.microsoft.co.il/shimmy/2010/12/26/… (via https://mcmap.net/q/418394/-net-observabledictionary, linked in the comments of the question).Antwanantwerp
install Microsoft ParallelExtensionsExtras Now available via Nuget: nuget.org/packages/MSFT.ParallelExtensionsExtras the library implements ObservableConcurrentDictionary, I tried and it works =]Hefner
B
18
Blare answered 19/11, 2009 at 14:42 Comment(4)
I can't seem to implement INotifyCollectionChanged. It says missing assembly. I imported all the assemblies at the top of your post (C#), and I have .NET 3.5, but it cannot find it. Any ideas?Etan
You imported the namespaces, but perhaps there are missing references to crucial assemblies. Make sure the assembly System.dll is referenced in your project. See example here.Blare
Looks like the links are broken.Ansermet
The links are dead.Zo
S
6

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.

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.

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

    /// <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 PropertySet class 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.

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.

Sissy answered 6/10, 2012 at 20:39 Comment(0)
D
3

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

ObservableConcurrentDictionary<TKey, TValue>

Dessau answered 7/1, 2018 at 18:32 Comment(0)
A
1

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/

Ahrendt answered 1/4, 2011 at 13:49 Comment(0)
I
-1

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.

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

Informative answered 30/9, 2009 at 14:4 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.