One of the great things of Silverlight – from my point of view – is the introduction of converters to format the content if a control, e.g. a text box. Well, the idea itself isn’t new, but it is the first time I saw it built in a Microsoft UI technology.
But I think MS should spend some time to optimize this feature. Here is the scenario:
I created a concatenation converter. This converter receives via the ConverterParameter
attribute the names of the properties it should concatenate (simple string concat with blank separator). The properties are taken from the object the control is bound to. This converter is used e.g. for display-only text boxes.
Now I had the case that one of the properties, that is concatenated by the converter, was changed by the application during runtime (not by user input). But the control wasn’t updated, means it was still showing the old value.
After some web searching I understood why: In case a property of an object is changed – and the object implements the INotifyPropertyChanged
– the control that is bound to that property receives the PropertyChanged
event and updates itself. You doesn’t have to implement the event handler yourself. It is part of Silverlight’s magic. As soon as you use the binding, the event handler is there.
In my case, the TextBox
wasn’t bound to a property, but directly to the DataContext
. This is done by just declaring Text="{Binding}"
. And of course in my case it looks like this: Text="{Binding Converter={StaticResource ConcatConverter}, ConverterParameter=Property1,Property2}"
. So the control doesn’t have a property it can listen to (or not the right one). Silverlight does not know that the values of ConverterParameter
are property names it should use to listen to PropertyChanged
events.
And – of course – I hadn’t found a way how to ‘attach’ the properties to the control.
My first approach was to fire the PropertyChanged
event by calling OnPropertyChanged
of the control via reflection. The Silverlight security prevents this. An exception was thrown when I called the method via refection.
Then I implemented a kind of reset to the DataContext
of the control the TextBox was embedded in. Reset is not just DataContext = DataContext
. Siverlight recognizes that the context does not change. One has to set the DataContext
to null first and then reassign the old value.
In theory, first all the controls are cleared and then the values are displayed. In fact, the graphic engine and / or the human eye (at least my eyes) are too slow to recognize an flickering.
Later on, I learned that the reset approach might lead to some confusion (as it did in my project). One of the users told me that, after she used the functionality where the DataContext is re-set, the control always shows the value that was set by the re-set.
It took some time to figure out what was going on. I did some debugging, but the context of the control’s parent was always set to the correct value. And this is the point: the context of the parent.
The control was nested in (was part of) another control. The nested control’s DataContext wasn’t set explicitly, so the control I have the focus on “derived” the context from its parent (one thing I really like about Silverlight: I don’t have to set the context for each child control).
As soon as the context of the child was re-set, something inside of Silverlight seems to cut the context connection between the parent and the child. And since the control is only created once during the lifetime of the application, from that moment on, it doesn’t point to the parent’s DataContext anymore. And therefor, the control does not reflect any changes. Even setting child.DataContext = parent.DataContext did not help. Odd thing!
The only workaround I found was to add a SetDataContext
method by the nesting control, which has to be called every time a refresh is required. 🙁