Nov 11, 2005

How to sync selection of two data bound ListBoxes

9CollectionViewSourceSample

I will show you two ways of syncing the selection of two data bound ListBoxes.

In the first solution, I will create a custom view over the collection and bind both ListBoxes to it. Views track the current item of their underlying collection, and allow us to sort, group and filter their items. CollectionViewSource is a new class introduced in September CTP that makes it possible to create a custom view in markup. Because the custom view created tracks the current item of the collection, and currency and selection are in sync in this scenario, binding both ListBoxes to the same view causes their selected items to be in sync.

    <Window.Resources>
        <local:GreekGods x:Key="source" />
        <CollectionViewSource Source="{StaticResource source}" x:Key="cvs"/>
    </Window.Resources>

    <ListBox ItemsSource="{Binding Source={StaticResource cvs}}" DisplayMemberPath="Name"/>
    <ListBox ItemsSource="{Binding Source={StaticResource cvs}}" DisplayMemberPath="Name"/>

I will write about how to use CollectionViewSource to sort, group and filter items in a future post.

An alternative way to achieve the same behavior is to set both ItemsSource properties to the data source and set the IsSynchronizedWithCurrentItem properties to true:

    <ListBox ItemsSource="{StaticResource source}" IsSynchronizedWithCurrentItem="True" DisplayMemberPath="Name"/>
    <ListBox ItemsSource="{StaticResource source}" IsSynchronizedWithCurrentItem="True" DisplayMemberPath="Name"/>

This markup works because when binding to a collection, a view is always created. If you don’t specify one, a default view is created for you internally. Although this view tracks current item the same way the custom one did, when you have a default view currency and selection do not sync by default. The way to override this behavior is by setting the IsSynchronizedWithCurrentItem property to true.

The data team made the default synchronization behavior be different for custom views and default views based on customer feedback. This way, users that are aware of the concept of view and are explicit about it get the synchronization they expect, and the rest of the users don’t.

In the image below, the first and second ListBoxes are bound to the CollectionViewSource and the third and fourth ones have InSynchronizedWithCurrentItem set to true.

Here you can find the VS project with this sample code. This works with September CTP WPF bits.

4 Comments
  1. Chris (MueMeister)

    Gia sou Beatriz,

    First of all, cool blogg about data binding :-)
    Tora thelo na .. ah .. Now I want to ask you whether you have some experiences on using Collections (instantiated in code) in XAML as data base. A read about ObjectDataProvider and now about CollectionViewSource. I’m a little confused now. How can I bind an ObservableCollection< ...> to a ListBox in XAML?

    efkaristo poli
    ta leme

    Chris :-)

    • Bea

      To bind a ListBox to an ObservableCollection in XAML, you can either use an ObjectDataProvider or add your collection directly in the resources. For the most part these two techniques are identical (although there are a few little differences - a good idea for a blog post). I usually do the latter - I add the collection to the resources. You can see examples of this in many of the sample I posted (for example, look at the September 21 post - How can I get a ListBoxItem from a data bound ListBox?). Basically, to do this you should add a mapping PI to the top of your XAML file:

      &lt:?Mapping ClrNamespace=”GetListBoxItem” XmlNamespace=”local”?>
      &lt:Window
      (…)
      xmlns:local=”local” >

      You can now add your collection to the resources directly (the GreekGods class derives from ObservableCollection in this case).

      &lt:Window.Resources>
      &lt:local:GreekGods x:Key=”greekGods”/>
      (…)
      &lt:/Window.Resources>

      Binding to the collection is now straight forward:

      &lt:ListBox ItemsSource=”{StaticResource greekGods}” />

      You don’t need CollectionViewSource if you simply want to bind a ListBox to an ObservableCollection. You should use CollectionViewSource when you want to add sorting or grouping to your collection in markup or if you want selection and current item to sync by default.

  2. Bryce

    Bea,

    First off, your blog has been very helpful in my exploration of XAML, so thank you! Still, I’m a little confused about something and I was hoping you could clarify it for me. I’m working on a set-up where I have a listview displaying thumbnails of images and I want the user to be able to select an image and have display in another panel with details and options. The problem: I can’t get the selection from the thumbnail listbox page to carry over into my details page. The image path and information is stored in an xml document which both pages are bound to the CollectionViewSource of; I’ve also tried IsSynchronizedWithCurrentItem=”True” with no results.

    Is there any kind of special action required to synchronize the selections between pages?

    • Bea

      Hi Bryce,

      It seems like you’re implementing a master-detail scenario.
      The two most common reasons for master-detail not to work are:

      - Forgetting to set IsSynchronizedWithCurrentItem to true, which you already mentioned you remembered. And also, this property defaults to true when binding to a CollectionViewSource. (You should only need to set it when binding directly to a collection).

      - In certain scenarios, you may need to set “Path=/” in the “details” section of this scenario. “Path=/” gets the current item of the view. For most scenarios this is not necessary because binding is clever: for example, if your details section is a <TextBlock Text=”{Binding Path=Name}” /> and each item in the collection has a Name property of the same type as the Text DP, we infer that you really want to bind to the current item of the view, and not the view itself. However, especially when binding to ContentControl’s Content or other DPs of type object, the data binding engine doesn’t have enough information to infer that. In these scenarios, you need to give binding a little help by setting Path=/.

      I can’t think of any other common reason for master-detail to fail. What error message do you get (if any)?

      Bea

Comments are closed.