I’ve seen a number of forum posts lately expressing a desire for an observable dictionary. Here are links to a couple of them:
Thread 1: Observable Dictionary, problem with Remove
Thread 2: Bind observable dictionary
As promised in my response to the latter thread, I am now providing a sample demonstrating how one might implement an observable dictionary. You can download the sample here.
The Observable Dictionary Sample
In this sample, I demonstrate how to bind to a dictionary of button styles. The styles are sorted based on their Key values in the observable dictionary. (Clearly, this should be treated as a nonsensical illustration for demonstration purposes only.)
On the left side of the window is a ListView that is bound to the observable dictionary. If you click an odd-indexed button in the ListView, its corresponding entry is removed from the dictionary. If you click an even-indexed button, its entry is duplicated in the dictionary.
On the right side of the window is a ComboBox whose items are bound to the same dictionary. Beneath the ComboBox is a Button whose style is bound to the selected value of the ComboBox. If you click this button, the dictionary will be cleared and reloaded.
As you click around, you will notice that the bound controls respond appropriately to any changes in the observable dictionary.
So why would someone want to do this?
Dictionaries are easy to work with in code. Perhaps you’ve already written an application that leverages a dictionary to store and retrieve in-memory data. You may now want to provide a view of the dictionary entries in your user interface.
Unfortunately, to provide a dynamic view of your dictionary data, you would need to do a lot of extra work. Namely, you would need to write code to synchronize some observable collection with your dictionary whenever you modify it.
Wouldn’t it be nice if the dictionary, itself, were observable?
The Observable Dictionary Class
What we clearly need is an “observable” dictionary class. You will find an example of such a class in the ObservableDictionary.cs file in the provided sample. What does it mean for a dictionary to be observable? In WPF, an observable class is simply a collection class that provides change notifications whenever its collection changes. It does this by implementing the INotifyPropertyChanged and INotifyCollectionChanged interfaces. This allows it to serve as the “ItemsSource” of a collection control (like a ListBox, ComboBox, or any other ItemsControl). These controls know how to monitor the collection for changes.
For a detailed explanation of how binding to a collection works, see the MSDN documentation. Here is a good starting point.
So how do you implement an observable dictionary?
Dictionaries basically work by aggregating an internal collection of key-value pairs. People often start by trying to derive a class from Dictionary<TKey, TValue> or by trying to implement IDictionary<TKey, TValue> in a class that aggregates a dictionary. The idea is that they could then add the requisite INotifyPropertyChanged and INotifyCollectionChanged interfaces and have an observable dictionary.
There’s a major problem with this approach. Observable collections are indexed by integer. In order to fire the necessary collection change notifications, you must know the index of an entry when it changes. Unfortunately, none of the interfaces exposed by the Dictionary<TKey, TValue> class are indexed by integer. And if you derive directly from Dictionary<TKey, TValue>, you do not have access to the underlying collection. As a result, an attempt to create an observable dictionary by deriving from or aggregating a dictionary won’t work.
To truly create an observable dictionary, you need to aggregate your own collection (usually of type KeyedCollection<TKey, TValue>). Then you must implement a whole slew of interfaces on top of the collection, as shown in the following class declaration:
public class ObservableDictionary <TKey, TValue> :
IDictionary<TKey, TValue>,
ICollection<KeyValuePair<TKey, TValue>>,
IEnumerable<KeyValuePair<TKey, TValue>>,
IDictionary,
ICollection,
IEnumerable,
ISerializable,
IDeserializationCallback,
INotifyCollectionChanged,
INotifyPropertyChanged
By implementing all of the above interfaces, you ensure that your dictionary class can be used interchangeably with the CLR’s Dictionary class. You should only need to change the declaration from Dictionary<TKey, TValue> to ObservableDictionary<TKey, TValue> in your code.
Pros and Cons
The benefit to using an observable dictionary, of course, is that the dictionary can serve as the ItemsSource for a databound control and you can still access the dictionary in code the same way you access any other dictionary. It is truly an indexed dictionary of objects.
There are certainly some limitations inherent in the very idea of making a dictionary observable. Dictionaries are built for speed. When you impose the behaviors of an observable collection on a dictionary so that the framework can bind to it, you add overhead.
Also, a dictionary exposes its Values and Keys collections through separate properties of the same name. These collections are of types Dictionary<TKey, TValue>.ValueCollection and Dictionary<TKey, TValue>.KeyCollection, respectively. These CLR-defined collections are not observable. As such, you cannot bind to the Values collection or to the Keys collection directly and expect to receive dynamic collection change notifications. You must instead bind directly to the observable dictionary.
The Observable Sorted Dictionary Class
Another thing to note is that a dictionary doesn’t typically store its data in a deterministic order. When binding to an observable dictionary, you cannot simply apply a sort order via a CollectionView because the exposed interfaces do not support sorting. For this reason, I include an ObservableSortedDictionary class in the attached sample.
Binding to an Observable Dictionary
When you bind to an observable dictionary, you are really binding to a collection of KeyValuePair objects. A KeyValuePair is an object which makes the entry’s key available via an appropriately named Key property and its value available via a Value property. Your item templates must take this into consideration and provide the appropriate binding paths. The provided sample illustrates how to do this.
All the Standard Caveats Apply
As always, this sample is provided “as is”. It has not been heavily tested and there are probably lots of improvements that could be made. Use it and modify it as you see fit. Let me know how it works for you, what improvements you make, what bugs you find, etc.
Cheers,
Dr. WPF
bug in the IDictionary index property… recursive calls == stack overflow
this was my simple fix:
object IDictionary.this[object key])this)[(TKey)key]; }
{
get { return ((IDictionary
set { DoSetEntry((TKey)key, (TValue)value); }
}
Great catch, Nathan! I have added the fix to the sample. Much thanks! -dw
I was planning to create by myself but a brief search in google take me here. Thanks for the code I will try it and come back if I find some suggestion or problem
thank you for sharing
Hi,
I’m having trouble sorting the ObservableSortedDictionary. I create a CollectionViewSource bound to the ObservableSortedDictionary, and create a ListBox which binds to the CollectionViewSource. This works fine, and all the items in the collection are displayed as expected. However when I add some sort descriptions as follows:
<CollectionViewSource.SortDescriptions>
<scm:SortDescription PropertyName=”Key” Direction=”Ascending” />
</CollectionViewSource.SortDescriptions>
I get an InvalidOperationException (‘System.Windows.Data.CollectionView’ view does not support sorting.) What am I doing wrong?
Any help would be much appreciated! Thanks,
Rod
Hi Rod,
Sorry. I really should have been more clear in my description of the sorted dictionary class.
Just like the standard dictionary, this class does not support sorting via a CollectionView. Rather, it provides a dictionary that stores its items already sorted. You must supply an IComparer instance when constructing the class. Clearly, this adds some overhead to the dictionary.
You would only use a sorted dictionary if you knew that you would be binding to it and you needed the bound items to appear in a certain order. This is no where near as convenient as the CollectionView approach where you are really only sorting a view of the items. Again, a dictionary does not lend itself nicely to such an approach, since it is really built for speed.
Cheers,
-dw
Hi Dr.Wpf,
you’re class is really interesting and helpful.
I found only a small bug in it but I am not really sure how to solve it.
Maybe you can help me…
The problem happens when I try to iterate in the ObservableDictionary collection.
For example the following code:
obsDictionary.Last().Value
return me the following expcetion:
Unable to cast object of type ‘Enumerator[System.Collections.DictionaryEntry]’ to type ‘System.Collections.Generic.IEnumerator`1[System.Collections.Generic.KeyValuePair`2[System.String,System.Int32]]’.
locate in
IEnumerator<KeyValuePair<TKey, TValue>> IEnumerable<KeyValuePair<TKey, TValue>>.GetEnumerator()
{
return (IEnumerator<KeyValuePair<TKey, TValue>>)GetEnumerator();
}
do you have any idea. If you prefer to not use the blog as support I can make the question in the MSDN forum 😉
Thank you
Thx Marco. Yes, that’s a bug… I was a bit too hasty in completing some of the interfaces. That line should read as follows:
return (IEnumerator<KeyValuePair<TKey, TValue>>)TrueDictionary.GetEnumerator();
I’ve updated the code in the sample. Keep me posted if you encounter any more such issues. 🙂
Thanks again!
-dw
Thanks a lot for your help, this class if very useful. Unfortunately for me I discover yesterday that I cannot use it in my current project.
The problem I discovered is that I cannot bind in TwoWay mode the Key and Value properties of ObservableDictionary items because they are, of course (but I didn’t know before yesterday 🙂 ) ReadOnly because part of structure KeyValuePair.
In any case this is indeed a very nice class that I will use in other projects.
Keep up the good work you are doing in sharing WPF code and hints.
This is probably highlighting my lack of low down knowlage on the subject, but, when binding an Infragistics xamDataGrid control to your ObservableDictionary only the type of the value is displayed where as using a standard Dictionary object the value (in this case a List (Of T) object) is fully enumerated. Is this one of the limitations of making the Dictionary observable?
Many thanks
Hi Paul,
This class should behave in the same manner as a native dictionary when used as the source for a collection binding (unless the control is designed to behave differently when bound to a dictionary).
If you can send me a simple repro that demonstrates the difference you’re seeing, I’d be willing to take a look.
Cheers,
-dw
Hi Paul,
Thanks so much for sending the sample application! You were absolutely correct! The default enumerator for the native Dictionary class returns KeyValuePair objects, whereas my enumerator was returning DictionaryEntry objects.
I have updated the code to include a custom enumerator so that it behaves just like the native class. Please give this a try and let me know if it works better for you.
Thanks again for helping me make this code better!
-dw
I am also trying to bind a combo box control to an ObservableDictionary. Everything shows fine based on the code below. I am coding everything in the code behind because my XAML developer uses Expression Blend and we have trouble going back and forth between Visual Studio and Expression Blend. When I change the selection in the combo box, the OnPropertyChanged(string name) is called but the string "name" is always null. Also, when I add a new KeyValue to the collection, the combo box doesn’t show the new entry even though it is successfully added to the dictionary. I am pretty sure I am not binding to the Dictionary properly (as you can see from the my commented code I am trying several different methods). I am wondering if you have any suggestions for doing all this binding in the code behind.Thanks,Nicole
Binding binding1 = new Binding("UserTypeName");
binding1.Source = UserTypes.Values;
binding1.UpdateSourceTrigger = UpdateSourceTrigger.PropertyChanged;
binding1.Mode = BindingMode.TwoWay;
//_cmbUserTypes.SetBinding(ItemsControl.ItemsSourceProperty, binding1);
//_cmbUserTypes.SetBinding(ItemsControl.DisplayMemberPathProperty, "UserTypeName");
//_cmbUserTypes.SetBinding(System.Windows.Controls.Primitives.Selector.SelectedValuePathProperty, "UserTypeID");
_cmbUserTypes.ItemsSource = UserTypes.Values;
_cmbUserTypes.DisplayMemberPath = "UserTypeName";
_cmbUserTypes.SelectedValuePath = "UserTypeID";
//_cmbUserTypes.SetBinding(System.Windows.Controls.Primitives.Selector.SelectedItemProperty, binding1);
Hi Nicole,
I’m not sure I fully understand what you’re trying to do in the code snippet, but I do notice that you’re setting the source of the binding to be the Values collection. As I mentioned in the article, the Values collection is not observable. The source must be the ObservableDictionary itself. binding1.Source = UserTypes;
I don’t think you want a two-way binding. There’s no way to add entries to the dictionary automatically if entries are added to the ComboBox.
You probably want to set the binding via your first example: _cmbUserTypes.SetBinding(ItemsControl.ItemsSourceProperty, binding1);
I would need to see a full repro to really understand your scenario.
Hello Dr. WPF !
First of all, thank you for your ObservableDictionary class. I really like it since it helps to keep my code clean! In my project, I bind a WPF TreeView control to the ObservableDictionary and it works perfectly, except for one operation : Remove.
It crashes on this line :
OnCollectionChanged(new NotifyCollectionChangedEventArgs(NotifyCollectionChangedAction.Remove, entry, index));
With this exception/stack trace :
System.InvalidOperationException was unhandled
Message="Added item does not appear at given index ‘4’."
Source="PresentationFramework"
StackTrace:
at MS.Internal.Data.EnumerableCollectionView.ProcessCollectionChanged(NotifyCollectionChangedEventArgs args)
at System.Windows.Data.CollectionView.OnCollectionChanged(Object sender, NotifyCollectionChangedEventArgs args)
at Collections.ObservableDictionary`2.OnCollectionChanged(NotifyCollectionChangedEventArgs args) in D:ProjetsCollectionsObservableDictionary.cs:line 199
at Collections.ObservableDictionary`2.FireEntryRemovedNotifications(DictionaryEntry entry, Int32 index) in D:ProjetsCollectionsObservableDictionary.cs:line 311
at Collections.ObservableDictionary`2.DoRemoveEntry(TKey key) in D:ProjetsCollectionsObservableDictionary.cs:line 267
at Collections.ObservableDictionary`2.Remove(TKey key) in D:ProjetsCollectionsObservableDictionary.cs:line 152
at Configuration.Snippets.RemoveSnippet(Snippet s) in D:ProjetsConfigurationSnippets.cs:line 64
at TextExpander.Window1.btnRemoveSnippet_Click(Object sender, RoutedEventArgs e) in D:ProjetsMXG.Window1.xaml.cs:line 126
at System.Windows.RoutedEventHandlerInfo.InvokeHandler(Object target, RoutedEventArgs routedEventArgs)
at System.Windows.EventRoute.InvokeHandlersImpl(Object source, RoutedEventArgs args, Boolean reRaised)
at System.Windows.UIElement.RaiseEventImpl(DependencyObject sender, RoutedEventArgs args)
at System.Windows.UIElement.RaiseEvent(RoutedEventArgs e)
at System.Windows.Controls.Primitives.ButtonBase.OnClick()
at System.Windows.Controls.Button.OnClick()
[…]
Do you have an idea of what is going on?
Thank you,
Martin
Hi Dr. WPF!
I am having exactly the same problem as Martin. When removing an entry from the dictionary, I get the InvalidOperationException.
Did you find a solution to this one already? Would be great!
Thanks a lot!
Andreas
Hi Andreas,
Martin was kind enough to send me a stack trace, but I would still love to get a repro that I could actively debug. If you have something handy (and not too complicated ;-)), I’d love to get it and track this issue down.
Thanks!
-dw
Hi again,
unfortunately I cannot send you the code, and actually it is quite complicated as well. 🙂 But I just replaced the ObservableDictionary with the standard ObservableCollection from the .Net framework and it also throws an exception at exactly the same line of code. It seems like something is wrong in the binding because the entry is successfully removed from the collection, but it is not removed from the ListView. If I just continue the debugging after the exception, the entry is still in the ListView, and upon selection I get another exception because the entry is no longer available…
It looks a little weird to me, but I will try to find the problem and post it here. If you or anyone has an idea, do not hesitate to post it here! At least the problem does not seem to be related to your ObservableDictionary.
Andreas
Thanks for the follow up, Andreas. Sounds like a tough one, so best of luck. I would love to know the resolution when you figure it out. 🙂
Hey,
I think I figured it out… I had bound several CollectionViews to the same data source, and each view had another filter applied to it. Now, the exception was thrown when I removed an item from View1 which was not visible in View2 due to the filter. It seems like View2 tried to remove the item from the list although it was not even in there. Therefore the provided index was out of range because View2 displayed far less items than View1.
Now I’m struggling with the next exception, but that does not seem to be related to the previous problem. 😉
Andreas
G’day
I am trying to bind an ObservableDictionary to a DataSource component (without any luck so far…)
– Plan A: to inherit ObservableDictionary from BindingList<KeyValuePair<TKey,TValue>>;
– Plan B: manually implement the following interffaces:
IBindingList
ICancelAddNew
IRaiseItemChangeevents
Any ideas?
Hi Igor,
The ObservableDictionary class is designed specifically for WPF (or other frameworks that listen to INotifyCollectionChanged and INotifyPropertyChanged). If you wish to use it with other platforms (like WinForms), then yes, you will need to add the additional required interfaces. I probably would not try to derive from BindingList<T>, since that will require a pretty large rearchitecture.
Cheers,
-dw
Has anyone really solved this? I constantly am getting this if the dictionary is populated immediately on application start up. If it is populated later and modified nothing goes wrong and the UI and dictionary update as expected.
Otherwise you get the error and the ListView or ListBox never updates again and the ObservableDictionary throws exceptions constantly in the background when an item in the dictionary has changed. This seems to happen on SetEntry when you have a new update and you remove the old value and populate the new. The exception seems to happen immediately after the NotificationCollectionChange event is fired.
Any help is welcomed. 🙂
Sent you a fully working example that breaks the dictionary.
BTW, if you are updating the Visual Element Via TCP WCF or some other transport.. then you will want to check the this.IsLoaded on your Window or Framework Element before you update the ObservableDictionary. I don’t know if this is a bug or something you should do for all ObservableCollections.
Hope this helps.
Thanks so much for the repro, Nick! That was exactly what I needed.
It turns out that I missed a couple of things in the change that I made for Paul back in April. This fully explains the problem that Martin reported (and probably others too).
I have posted an updated sample that contains the simple 2-line fix. Please grab it and verify that it works for you.
Wrt asynchronously accessing the collection, there are definitely some issues that you must address when accessing any observable collection. As long as the work is done on the same dispatcher that was used to create the collection, everything should be copacetic. You certainly shouldn’t see exceptions based on whether the elements have loaded. This would also be a good thing to test with the updated ObservableDictionary class.
Thanks again for the repro! Someday I hope to have extra time to really optimize a few things in the dictionary, but at least the current version should be more solid now. 🙂
Cheers,
-dw
I am checking out your class and I noticed a potential bug that VS 2008 SP1 points out. In ObservableDictionary.cs, method GetIndexAndEntryForKey, line 220. “The out parameter ‘entry’ must be assigned to before control leaves the current method”.
While the compiler marks it as an error, it appears to be treating it as a warning and letting the compile and link finish.
Normally, I would suggest that the output parameter be set to null, but since it is a struct and not a class, I am not sure what would be the best value.
Thanks,
Jim
Hi Jim,
That’s not a compilation error… it’s the code editor being over dilligent with its intellisense errors. The compiler realizes that the out parameter is a struct and therefore has no real need of construction, but the code editor does not.
I’ll try to remember to initialize it in the next update, just to avoid the confusion. 🙂
Cheers,
-dw
Hi,
I’m working on a project and I was using ObservableColections and Dictionary until I found out this link, this ObservableDictionary makes life much easier, thank you.
But(there is always a “but”),I tested the application that comes with the dictionary and it seems that if you delete or add more than one item the application crashes(System.InvalidOperationException in ValidateVersion()). There are any other updates to the class or what
Hi John,
I cannot reproduce the problem you describe. I would need a simple repro to understand what is happening.
Regards,
-dw
I’m not sure if this is working, or if its not meant to work in this way, but…
I am trying to bind a DependencyProperty on a FrameworkElement, of type Person to an index within an ObservableCollection. So, if it is bound to element 1, whenever element 1 is replaced with a new Person object, the binding should fire and the DependencyProperty should be updated with the new Person.
As I couldn’t use index’s within property paths in Silverlight, I switched the ObservableCollection to your ObservableDictionary, and simply keyed the entry on Person.PersonID. However, this is still not working. I see that in your example, you only bind to the whole collection not an index within the collection. Should this be possible using you ObservableDictionary?
Hi Mark,
I’m afraid that scenario is not supported via WPF’s binding framework. You would need code logic to insert/replace the item at a specific index.
Regards,
-dw
Hi dw,
I don’t have the before and after to diff against for your 2-line fix. What did you do to fix this? I’m having a similar bug in some code I’ve written.
Hi Daniel,
My fix was pretty specific to my class, as detailed below.
Line 326 was:
OnCollectionChanged(new NotifyCollectionChangedEventArgs(NotifyCollectionChangedAction.Add, entry, index));
became:
OnCollectionChanged(new NotifyCollectionChangedEventArgs(NotifyCollectionChangedAction.Add, new KeyValuePair<TKey, TValue>((TKey)entry.Key, (TValue)entry.Value), index));
Line 338 was:
OnCollectionChanged(new NotifyCollectionChangedEventArgs(NotifyCollectionChangedAction.Remove, entry, index));
became:
OnCollectionChanged(new NotifyCollectionChangedEventArgs(NotifyCollectionChangedAction.Remove, new KeyValuePair<TKey, TValue>((TKey)entry.Key, (TValue)entry.Value), index));
Hope this helps!
-dw
Hello Doctor,
I have a test program that I have bound three TextBoxes to three different dictionary keys. If I update the value in the text box, the dictionary get’s updated. If I programmaticly update the dictionary the text box does not get the updated value. My Mode is set to BindingMode.TwoWay.
Is this something that should be doable with your dictionary?
Thanks,
Jim
Property update is missing! When i bind to an individual item in wpf, a property item changed event is not fired. Need to add this line to the DoSetEntry() at the very end inside the if statement:
OnPropertyChanged(“Item[]”);
This is described very well in http://blogs.msdn.com/xtof/archive/2007/09/28/binding-to-indexers.aspx
Hi Jim,
The two-way binding in your scenario will work if and only if the source object provides property change notifications. If your key values are objects that implement INotifyPropertyChanged, then it should work.
Cheers,
-dw
Another thing: there is a significant number of extra unneeded code that becomes apparent if you use Resharper – subclasses do not need generic parameters – they can use their parent’s; many interfaces have already been declared as part of other interfaces, etc. I have been cleaning it up and can send you a copy – if you provide your email addr. (You can contact me at yuriastrakhan at gmail). One last thing – thanks to your code and resharper, I found a bug in the C# compiler: https://connect.microsoft.com/VisualStudio/feedback/ViewFeedback.aspx?FeedbackID=388839
Hi dw–Thanks for this great class…
Hi Yurik–Thanks for that bug fix. That is EXACTLY what was missing. Personally, I think it should go in "FirePropertyChangedNotifications" outside the if statmentment. I put it there since an item can change without the Count changing and since this method gets called from other places. If an item is added or removed wouldn’t the Item[] also be updated???
Should "OnPropertyChanged("Values");" be outside the if too?
Thanks,
Jim
Thanks for the suggested improvements, Yurik and Jim.
There are actually lots of optimizations that could be made to this class, but I’ve not had the bandwidth to properly test such changes, and I continue to procrastinate there. 🙁 I took a lot of shortcuts (like editing an item by simply removing the old and adding the new) when I first put the class together.
I’m happy to post any suggested improvements that have been tested. You can reach me at ask[at]drwpf.com. My response time depends entirely on my workload. 🙂
This is great. Well done!
I’d like to register to get the change events directly. The following code is what I am attempting, but the collectionChanged method is protected. What is the proper way to register interest?
ObservableDictionary aDict = new ObservableDictionary();
aDict.CollectionChanged += OnCollectionChanged;
Thank for the great work!
Chip
Hi Chip,
You should be able to cast the dictionary to INotifyCollectionChanged to add the handler:
(aDict as INotifyCollectionChanged).CollectionChanged += …
Cheers,
-dw
hi Dr.WPF,
wow since 2007 (when you wrote it) has now more than 2 years passed by and I can even now after so many years use your great class. I am trying to use your Observabledictionary under silverlight 3. MS didn’t make any improvements in this field since ver.2. I’ve noticed that the ISerializable and IDeserializable are not supported in SL. So your serialization part doesn’t work in SL.
My question is. If I replace not working Ser/DeSer code with IXmlSerializable and methods like write-/readXML would it be enough?
Thanks for your great work
Rafal
Hi Rafal,
My implementation of the ObservableDictionary class includes all of the interfaces implemented by the native Dictionary class in the full .NET framework. Silverlight uses a compact version of the .NET framework. The CoreCLR for Silverlight defines the Dictionary class as follows:
Note that the serialization interfaces simply don’t exist in Silverlight’s CoreCLR. This means you can create an ObservableDictionary for Silverlight that is fully analogous to the native Dictionary class without supporting serialization.
Of course, if you actually need to serialize the dictionary (for storage, remoting, etc), then I see no problem with using the XML serialization interfaces. 🙂
Cheers,
-dw
Dr. WPF,
Thanks for the class! Saved me a lot of time. One thing, I’m using it in VS 2010 B2 and get 3 CS0693 warnings. These look like BS warnings to me, but apperently Microsoft decided you can’t use the same names for inner template params as you do for the outer ones. The warnings are because you named all the collection params as TKey and TValue.
Hi,
thx for the great work.
With VS 2010RC1 I got a Stackoverflow in Line 445. Replacing the Line with
get { return ((IDictionary<TKey, TValue>)this)[(TKey)key]; }
helped.
Marc
[…] community has come up with a few implementations of an ObservableDictionary. This link is to one of the good ones, by Dr. WPF. Here is another ObservableDictionary on codeplex. Unfortunately, the first works only for […]
I’ve updated the dictionary to fix a couple of bugs:
First, there’s the infinitely recursive call that Marc pointed out in his comment above. (Who writes this crap?)
Second, I fixed the issue that Pete found with not raising the change notification for “Item[]” when a set occurs (see pingback comment above). This was actually a regression from an earlier fix. Doh! 😮 (Seriously, doesn’t anyone unit test these things!?)
Big thanks to Pete and Marc! 😀