Tag Archives: databinding

Changing the layout of a WPF TreeView to look like multiple ListBox

Last week at work I was requested to create a new control which should have the behavior of the WPF treeview with a template involving multiple ListBox. To understand what I mean, I wanted to change the layout of this:

To this:

Of course, selection must work properly:

I asked a question on StackOverflow about this control because I didn’t find any easy way to do it. I though at the beginning that playing with the templates of the TreeView and TreeViewItem would do the trick but it didn’t.

The solution I finally choose involves creating multiple ListBox and wire them together using DataBinding:

  • I create a new CustomControl which inherits Control (I couldn’t use neither Selector or TreeView because I wouldn’t have been able to manage the SelectedItem property from the derived class)
  • In the template of this CustomControl is an ItemsControl. This ItemsControl has its ItemTemplate property set to a DataTemplate containing a ListBox.
  • The CustomControl has a Depth property of type int. This property indicates the number of ListBox that should be generated.
  • The CustomControl automatically databound ListBoxes together: each ListBox’s ItemsSource property is databound to the SelectedItem’s children property of the previous ListBox in the visual tree.
  • The CustomControl has a SelectedItem property and a SelectionChanged event (like Selector-derived class).
  • I added an IsReallySelected attached property to the ListBoxItem which are generated. This enables to databing an IsSelected property of the ViewModel class behind the control with the IsSelected of the ListBoxItem. I had to create an attached property because its value is true when the ListBoxItem is selected AND the parent ListBox has IsSelectionActive set to true.

Here is the result:

You can download the source code of this sample (VS2010 RC solution).

Filtering with or without ICollectionView ?

In my “Why should I use MVVM pattern” post, I gave a link to a post explaining the ICollectionView interface.

This interface is very important in the MVVM methodology because it enables the possibility to track the selection of the user in ItemsControl based-controls (ListView, ListBox, ComboBox, TabControl…). The ICollectionView also enables the ViewModel to perfom Sorting, Grouping and Filtering very easily (basically by using the Sort, Group and Filter properties).

In my project at work, I have a “Library” which lets the user choose an item in it, and drop it somewhere else in the application. There is around 200 items in the ListView, and because I wanted to leverage the ICollectionView interface, I used the Filter property to enable search functionality.Unfortunately, the performance was pretty poor and until now I didn’t find out what goes wrong. Each item in the ListView is rendered using a simple DataTemplate made of one Image and several TextBlock. Because the filtering was slow, I found another solution which is much faster. And thanks to the MVVM methodology, I found one quickly !

I added a IsVisible property to the ViewModel objects that are in the ListView.

?View Code CSHARP
1
2
3
4
5
6
7
8
9
10
11
12
        /// <summary>
        /// Gets or sets a value indicating whether this item is visible in the collection
        /// </summary>
        public bool IsVisible
        {
            get { return this.isVisible; }
            set
            {
                this.isVisible = value;
                this.OnPropertyChanged("IsVisible");
            }
        }

This IsVisible property is toggled when the search is performed: I iterate over the ObservableCollection of items, and change the IsVisible property regarding whether the item match the search text or not. In the View, I setup a simple style for my ListViewItem that databound the IsVisible property to the visibility property (using the BooleanToVisiblity converter included in the framework).

1
2
3
4
5
6
7
<ListView ItemsSource="{Binding Items}">
    <ListView.ItemContainerStyle>
        <Style>
            <Setter Property="Visibility" Value="{Binding Path=IsVisible, Converter={StaticResource BooleanToVisibility}"/>
        </Style>
    </ListView.ItemContainerStyle>
</ListView>

Now the filtering is much faster. I don’t know what could slow down the performance of the filtering using the ICollectionView interface.

If you have any idea, I’d be glad to know. If on the other hand you’re dealing with performance issue too, you might try my solution :)

How insert Separator in a databound ComboBox

As you might already know, I’m a big fan of the Model-View-ViewModel pattern. I’m using it extensively on the current I’m working on at work. Today I had to face a simple problem that was tricky to solve.

In the application I’m building, I have a “Library panel”. This panel contains a set of items that are used in my application. Because the number of items can be very large, I decided to add a filter capability. Filtering to a collection is very straightforward once you get familiar with the ICollectionView interface. If you want more details about it, check out Marlon’s blog post about it.

To give the user the possibility to filter the items, I added a ComboBox control. Of course, because I’m using the MVVM pattern, I’m not creating the ComboBoxItem myself, the databinding mechanism of WPF handle this (to be precise, the databinding handles collecting the item, and the ComboBox handles the creation of the ComboBoxItem to wrap them…).

The problem

In my ViewModel, I create a ObservableCollection<string> property that I called “Categories”.Then, in my view (XAML), I databound the ComboBox’s ItemsSource property to this “Categories”. Ok, it works fine and took me about 10min to do it.

Now, I want to add separator between some ComboBoxItem… Hmmm, how should I do that… I cannot do combobox.Items.Add(something) anymore because the ItemsSource property is databound… Well, I could as something in my ViewModel, but what ?

My solution

I wanted to keep the logical information about where Separator are in the ViewModel. This is typically an example of something that should stays in the ViewModel. I decided to add an empty items in my ObservableCollection for every Separator I wanter to have in the view.

In the view, I set up an ItemContainerStyle for my ComboBoxItem. The tricky part is, how could I replace my ComboBoxItem with a Separator… Well actually, we can’t. But what we can do is to change the entire template of the ComboBoxItem when the content is empty:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
        <ComboBox ItemsSource="{Binding Categories}">
            <ComboBox.ItemContainerStyle>
                <Style TargetType="{x:Type ComboBoxItem}" BasedOn="{StaticResource {x:Type ComboBoxItem}}">
                    <Style.Triggers>
                        <DataTrigger Binding="{Binding}" Value="">
                            <Setter Property="Template">
                                <Setter.Value>
                                    <ControlTemplate TargetType="{x:Type ComboBoxItem}">
                                        <Separator HorizontalAlignment="Stretch" IsEnabled="False"/>
                                    </ControlTemplate>
                                </Setter.Value>
                            </Setter>
                        </DataTrigger>
                    </Style.Triggers>
                </Style>
            </ComboBox.ItemContainerStyle>
        </ComboBox>

And voila ! It works fine and keeps the logic in the ViewModel. If you have any other idea about how we can solve this issue, please feel free to comment :-)