Simulating IsSynchronizedWithCurrentItem in Silverlight (part 2)

This is part 2 of a two posts series about the property IsSynchronizedWithCurrentItem. In the previous post, we saw what it does in Windows Presentation Foundation. In this post, we will see that this property is missing in Silverlight, and propose a way to simulate it.

Like we mentioned in the previous post, the property IsSynchronizedWithCurrentItem is very handy to keep a List-Details view in synchronization. However, this handy property is not implemented in Silverlight 2. When the team at Microsoft implemented this new technology, they made it a subset of WPF, and had to decide which feature to keep and which to let go. This was the only way to get the framework to fit in less than 5 MB, which is a critical size for a plug-in distributed over the internet.

Note: In fact it's more than just a missing property. The whole CollectionViewSource class is also missing altogether. We saw in the previous post that this class is the one that enables filtering, sorting and also selecting an item on a Collection.

The sample for this article can be downloaded here.

Binding to SelectedItem

One way to compensate for the missing property is to bind the property SelectedItem of the Selector class (of which ListBox, ComboBox and others derive) to a new property on the ViewModel.

However, this poses two problems:

  • The syntax in the XAML will be quite different from the WPF syntax. This is not a huge deal, but I would prefer to keep the Silverlight XAML as close to the WPF one as possible.
  • In the WPF sample we saw in the previous post, we provided a way to easily switch the synchronization on and off on the ComboBox. If we use the SelectedItem, however, switching it off will be more difficult, and will require some code behind.

So let's go back to the drawing board and see if we can provide a better way to do that. We will do that using something called an Attached Behavior.

Attached Behavior, you say?

An attached behavior is actually an attached property which is not used for data, but to create a behavior in the targeted element. In the words of Josh Smith: "Attaching a behavior to an object simply means making the object do something that it would not do on its own".

Attached properties are a very handy way to extend an element's features without modifying the element itself. They can be compared to extension methods in C#. This is what you use when you say Grid.Column="1" in a XAML element. Since the element will not always be placed in a Grid, why should all elements have a "Column" property? Instead, the Column property is implemented on the Grid class, and attached to the element.

Expanding on that concept, we can use an attached property to attach a feature to an element, without modifying that element. For example, you could attach events to the element in the background, without the user being really aware of that, or create bindings.

The SelectorExtension class

The attached property "IsSynchronizedWithCurrentItemProperty" is declared on a new class named SelectorExtension (see the code in the sample here). The following points are worth mentioning:

  • Since in XAML, properties can be set in any order, we must account for the fact that the ItemsSource property of the Selector might not be set yet when the IsSynchronizedWithCurrentItem property is set. To resolve this issue, we resort to a little trick: We attach an event handler to the SelectionChanged event of each control that uses the property. Whenever the event is called, we know for sure that the ItemsSource has been set, and we can replace the event by a proper data binding. In fact, we create the binding on all the Selector controls that use this collection, and remove the event handlers. A Binding is preferable to an event handler, because it creates a weaker link between the elements, and because it is easy to create a TwoWay binding.

Note: Creating an event handler on controls in the background might cause a memory leak, if the control is deleted without the event handler being removed. This is one problem with this approach. When using this extension property, the property IsSynchronizedWithCurrentItem should be set to false before deleting the control. Setting this property to false will remove the event handler, and no leak will be caused.

  • We use a boolean flag in the SelectionChanged event handler. This is needed, because when one control's event is fired, we modify the selection in all other controls using the same ItemsSource collection. This can cause an endless loop. By setting the flag to true, we just notify the application that we are currently processing the selections, and that the original event should just be allowed to go on.
  • If the property IsSynchronizedWithCurrentItem is set to false, we must remove the binding for the current control. This is needed to reproduce the exact same functionality as in WPF. Removing a Binding in Silverlight is not that easy, but you can simple create an empty binding on the SelectedItem property of the Selector control.

The ObservableCollectionEx class

The SelectorExtension we described above creates a binding in the background between the SelectedItem of the Selector control, and a corresponding property on its data source. In WPF, this is where the CollectionViewSource class comes in play in the background. However, we mentioned already that this class doesn't exist in Silverlight. We will simulate this feature by extending the existing ObservableCollection class, and creating a new class called ObservableCollectionEx. This class is also a generic class, and derives directly from ObservableCollection. It features a new property named SelectedItem (of type T, to allow generic usage).

Because the class is a generic class, it is difficult to handle it in the SelectorExtension class, because we don't know the type of the items it contains. Since we cannot make the SelectorExtension class a generic class itself (since we use it in XAML), the easiest way I found is to declare an interface named IObservableCollectionEx, with one setter property of type "object", named "SelectedObject". The class ObservableCollectionEx implements this interface, and the only action that SelectedObject does is set the SelectedItem. This allows us to work with this class in a non-generic way. For convenience, however, the SelectedItem is strongly typed, and the SelectedObject is not used either for binding, or for any other purpose. If someone has a better suggestion, I would be happy to hear it ;)

The new DataItems class

To recreate the functionality we wish to achieve, the Selector control must use an ObservableCollectionEx instead of an ObservableCollection. Since we are using the DataItems class, which is itself deriving from ObservableCollection<DataItem>, it is easy to replace this class in the Silverlight project and make it derive from ObservableCollectionEx<DataItem> instead.

This way, we can share the MainViewModel, which offers a property of type DataItems. The DataItems class in Silverlight will be different from the DataItems class in WPF, but this is hidden from the MainViewModel, and from the developer.

Sharing classes

In order to simplify the maintenance for classes that are exactly the same between WPF and Silverlight (such as the DataItem class and the MainViewModel class), you can share these classes between the 2 projects. It is a well known limitation of Silverlight that it cannot use DLLs compiled for WPF. However, the source code can be shared between both project types. This way, when one file is modified in WPF, the modifications will automatically be reflected in Silverlight. You will still have to build the Silverlight project separately (and we hope that this limitation will disappear some time in the future), but at least you have less source code files to maintain.

To add a shared source code file, right click on the Silverlight project (or on a subfolder) and select "Add / Existing item". Then select the source code file you want to add. Instead of clicking on the button "Add", click on the small arrow on this button, and select "Add As Link".

image

Adding a source code file as Link

Testing the application

If you run the application sample, you will reproduce the exact same behavior than displayed in the previous post about WPF. The goal is reached, and the syntax is almost the same in WPF and in Silverlight.

In WPF:

<ListBox ItemTemplate="{StaticResource DataTemplate1}"
         ItemsSource="{Binding Items}"
         IsSynchronizedWithCurrentItem="True" />

<ComboBox IsSynchronizedWithCurrentItem="{Binding PropertyOn}"
          ItemTemplate="{StaticResource DataTemplate1}"
          ItemsSource="{Binding Items}" />

<TextBox Text="{Binding Items/TestName, Mode=TwoWay}"
         Height="30"
         Margin="0,10,0,0" />

In Silverlight:

<ListBox ItemTemplate="{StaticResource DataTemplate1}"
         ItemsSource="{Binding Items}"
         ex:SelectorExtension.IsSynchronizedWithCurrentItem="True" />

<ComboBox ex:SelectorExtension.IsSynchronizedWithCurrentItem="{Binding PropertyOn}"
          ItemTemplate="{StaticResource DataTemplate1}"
          ItemsSource="{Binding Items}" />

<TextBox Text="{Binding Items.SelectedItem.TestName, Mode=TwoWay}"
         Height="30"
         Margin="0,10,0,0" />

image

image

Print | posted on Wednesday, February 18, 2009 11:21 PM

Feedback

# re: Simulating IsSynchronizedWithCurrentItem in Silverlight (part 2)

left by Ed McPadden at 2/19/2009 6:12 AM Gravatar
Great post Laurent! Very clever approach.

I am very new to Silverlight (and WPF) so I found it very interesting to walk thru the attached property code.

One thing I did notice is that the twoway binding on the textbox does not always work. If I edit the text and navigate away from the browser or I click on the same item then the text changes in the listbox but if I edit the textbox and navigate to a different item in the listbox the text is not updated. I wasn't sure if this was somehow related to this approach or not.

I've seen another blog post that also attempts to solve the missing CollectionViewSource in a different way. You and your readers might be interested in looking at this as well:

http://blog.boschin.it/articles/Improving-Silverlight-2.0-databinding-with-a-CollectionViewSource-control.aspx

Thanks for taking the time to post this. As I am learning WPF and Silverlight I am wrestling with the missing functionality in Silverlight and really enjoy reading posts on getting around these limitations.

...Ed

# re: Simulating IsSynchronizedWithCurrentItem in Silverlight (part 2)

left by Boyan Mihaylov at 2/19/2009 10:45 AM Gravatar
It's better to create a custom control which inherits from ListBox, for example, and add the functionality in it. You won't have any memory leak problems.

# re: Simulating IsSynchronizedWithCurrentItem in Silverlight (part 2)

left by Laurent at 2/19/2009 11:23 AM Gravatar
Hi Boyan,

The definition of "better" is a matter of many things, including personal taste. For me, I try to stay clear of creating custom controls inheriting others. I find this approach annoying and generally speaking more work than using attached behaviors. If I decide to create a custom control inheriting ListBox, I also need one for ComboBox, one for TabControl, etc... On the other hand, my solution works with all controls deriving from Selector.

Another argument is that deriving a control will be less compatible with a WPF solution, which was my main motivation in this article. If IsSynchronizedWithCurrentItem is implemented in a future version of Silverlight, my solution will be easier to revert than if I create a collection of custom controls.

Finally, using attached behaviors allows me to create small granular "function modules" that I can add in a very targeted way to controls, and only if needed. A derived custom control, on the other hand, is a bigger block, and thus less easily maintainable and testable.

Again, it is greatly a matter of taste, and if you are fine with the limitations I list here, I wouldn't recommend absolutely against it. For myself, I really prefer attached behaviors to extend functionality.

Cheers,
Laurent

# re: Simulating IsSynchronizedWithCurrentItem in Silverlight (part 2)

left by Laurent at 2/19/2009 11:33 AM Gravatar
Hi Ed,

Thank you for your kind comment. I also considered implementing CollectionViewSource in Silverlight, but it seemed a bigger work (and less fun ;)) than using an attached behavior. Also, I really wanted to write an article about attached behaviors, and saw a good occasion here.

Cheers,
Laurent

# re: Simulating IsSynchronizedWithCurrentItem in Silverlight (part 2)

left by Josh Smith at 2/19/2009 1:54 PM Gravatar
Well done, Laurent. Thanks for sharing this. :)

# re: Simulating IsSynchronizedWithCurrentItem in Silverlight (part 2)

left by Miguel at 3/6/2009 11:35 AM Gravatar
This is a good example. I just have a couple of points to mention.

-If you subscribe to the event and your handler is in a static class, then you will avoid the memory leak.
-When you create a binding for an ObservableCollection, Silverlight doesn't use a weak link as expected.

I also prefer to use Attached Behaviours instead of UserControls, for the reason you mention. Another benefit is that you can't attach different behaviours, while multiple inheritance or creating a complex inheritance chains is a less maintanable alternative and sometimes not even an option. For example, we have some behaviours for Textboxes that will filter everything that's not a number. We could've created a NumbericTextbox. At that point it looks find, but we also have another behaviour that selects all the text in the textbox whenever we get the focus. Now we could create a AutomaticallySelectedTextBox (or something like that). The problem now is that sometimes we need to use both behaviours for the same instance. Someone might decide to continue by the inheritance path and instead of creating AutomaticallySelectedTextBox modify NumberTextBox. Now the name doesn't makes sense. So we name it AutomaticallySelectedNumericTextBox, which is now an obvious smell. I think I went to far, but the point is that there's room for both techniques and I find Attached Behaviours as a really nice feature that we should make part of our toolbox and learn when to choose it over inheritance.

# re: Simulating IsSynchronizedWithCurrentItem in Silverlight (part 2)

left by Laurent at 3/23/2009 3:18 PM Gravatar
These are great comments, Miguel. Thank you for taking the time to write them.

Cheers,
Laurent

# re: Simulating IsSynchronizedWithCurrentItem in Silverlight (part 2)

left by Miguel Madero at 3/24/2009 12:37 AM Gravatar
No problem, btw, Delay from the SL team recently posted a fix for the ItemsSource that now uses a weak event handler for the CollectionChanged event.

# re: Simulating IsSynchronizedWithCurrentItem in Silverlight (part 2)

left by Laurent at 3/24/2009 9:26 AM Gravatar
I saw that too. David is a WPF Disciple and gave us a preview. Nice stuff.

# re: Simulating IsSynchronizedWithCurrentItem in Silverlight (part 2)

left by enochenoch at 5/1/2009 6:34 PM Gravatar
help me ! thank you.

pleace read the post:

http://silverlight.net/forums/t/89912.aspx

Post A Comment
Title:
Name:
Email:
Website:
Comment:
Verification: