Friday, March 25, 2011

I... I... I think I may be in love with Observable Collections

     Oh Observable Collection
     You satisfy my selection

     Like a lonely flower, a desert rose
     When there is a new one, everyone knows

     If only we could use properties accessed internal
     Using properties accessed public may lead to an inferno

     Oh Observable Collection, I can't stay mad at you.
     Throughout my meandering code, your notifications are pure and true.

Observable collections are a handy way to get data transferred across multiple windows and XAML documents. I have found them versatile and useful:

XAML Code:
. . . <TextBlock Height="36" Margin="0,6,56,0" Name="textTimeBlock" Width="200" Text="{Binding Path=TimeCollection[0], RelativeSource={RelativeSource FindAncestor, AncestorType={x:Type Window}}}" FontSize="24" TextAlignment="Center" /> . . .

Corresponding C#.

Code:
. . . private readonly ObservableCollection<string> timeCollection; . . . /// <summary> /// Gets the updating time /// </summary> public ObservableCollection<string> TimeCollection { get { return this.timeCollection; } }
Just set up a simple DispatcherTimer tick to update the collection every second...
Code:
this.dispatcherTimer.Tick += new EventHandler(delegate(object s, EventArgs a) { if (this.timeCollection.Count == 1) { this.timeCollection.RemoveAt(0); } this.timeCollection.Add(DateTime.Now.ToLongTimeString()); });
... and away you go.

Now you may have noticed a slight peculiarity, that the private field 'timeCollection' is readonly. This is necessary because I don't want anybody who comes along and grabs the exe to be able to just assign their own collection over my collection(!). Since the property must be public for the notifications to make it to the XAML code, this is the only way. You still expose, but atleast no one can overwrite. Hopefully in some future version of .NET, Microsoft will allow this notifications process to work with internal accessed properties as well. I can't see any obvious reason why they shouldn't.

Note that despite the fact that your collection is readonly, you will still have access to all of the methods of that collection and, perhaps counter intuitively, the ability to modify the collection outside of the constructor or initialization. The are two ways around this:

1. Use a ReadOnlyObservableCollection, though this does have the issue of a protected CollectionChanged event. I had several issues trying to get this to work with Xaml code. Most notoriously "A TwoWay or OneWayToSource binding cannot work on the read-only property." I tried everything I could find in the top 30 or 40 Google results. No love.

2. Extend the ObservableCollection class and hide all of the methods that can actually modify the collection. Since you can't make methods of the base class less accessible, what I did was just have the public methods perform no operation, and put in the comments that the collection is readonly. I then created equivalent internal methods with a different signature, which then called the respective base methods. This way, nobody can overwrite the collection or overwrite the actual values of the collection. The class itself is marked public.

No comments:

Post a Comment