A nearshore team from Uruguay, South America (GMT-3) Grouping items in a Silverlight ListBox - UruIT Blog
Friday, June 17, 2011 12:20 PM uruit

Grouping items in a Silverlight ListBox

[Click here to download sample code for this article]

Introduction

ListBox grouping is one of the features that Silverlight did not inherit from WPF, even though is a common requirement. Here we present a very simple yet powerful way to implement grouping in the ListBox control that also allows subgrouping and sorting.

This approach is supported on traditional Silverlight applications, and also on Windows Phone 7 applications.

 

Untitled2 Untitled

 

The problem

Although WPF supports grouping for the ListBox control, Silverlight doesn’t. The developer is restricted to load a plain, unstructured collection of items. However, the hierarchical scenario is very common in applications that display different types of data.

The solution

To overcome this issue, we will use a Binding Converter, which will group the items internally, adding to the ListBox a ContentControl element with a specific template, for each group header it creates, and a ContentControl with another template, for each item on each group it creates. Sounds complicated? It’s not, let’s do it!

Suppose you have a list of items that you want to display hierarchically. You proceed by creating the ListBox and binding its ItemsSource dependency property to the collection of items. In XAML it would look like this:

<ListBox x:Name="List" ItemsSource="{Binding}"/>

And the code-behind would look like this:

IEnumerable<ItemType> items = ...;

List.DataContext = items;

At this point, the ListBox shows the list of items without any structure. Now, let’s add a Converter to the binding. A converter is an instance of a class that implements IValueConterter, which has only two methods: Convert and ConvertBack. Before Silverlight binds the collection of items, it will call the Convert function, allowing us to transform the data as we want. The ConvertBack method will never be called for this type of binding, since this is a OneWay binding. For more information about data bindings, see http://msdn.microsoft.com/en-us/library/ms752347.aspx

So, all we need is a new class that implements the IValueConverter interface. In this case, “PetGrouper”:

public class PetGrouper : IValueConverter
{
    public object Convert(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture)
    {
        // ...
    }

    public object ConvertBack(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture)
    {
        throw new NotImplementedException();
    }
}

Because ConvertBack will never be called, we can leave it untouched, throwing a NotImplementedException. For the Convert method, we already know the type of data we are assigning to the DataContext. That same instance comes as an object in the value parameter. We can ignore the rest of the parameters in this case.

Next, we have to group and sort the data. For each group we create, we will add a control with a template designed for headers, and for each one of its items, we will add a control with a template designed for items.

public object Convert(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture)
{
    IEnumerable<Pet> pets = value as IEnumerable<Pet>;
    List<ContentControl> groupedPets = new List<ContentControl>();
    foreach (IGrouping<string, Pet> animal in pets.GroupBy(x => x.Animal).OrderBy(x => x.Key))
    {
        groupedPets.Add(new ContentControl() { Content = animal.Key, ContentTemplate = App.Current.Resources["AnimalTemplate"] as DataTemplate });
        foreach (Pet pet in animal.OrderBy(x => x.Breed))
        {
            groupedPets.Add(new ContentControl() { Content = pet, ContentTemplate = App.Current.Resources["BreedTemplate"] as DataTemplate });
        }
    }
    return groupedPets;
}

AnimalTemplate and BreedTemplate are resources in App.xaml. In this example we are grouping the pets by animal type, sorting the groups by animal type name, and sorting each item on the groups by breed name.

The type IGrouping<string, Pet>, describes a list of groups whose headers (“keys”) are strings, and each one of them is populated by pets. The Key type may vary for different scenarios. For example if we were grouping by vertebrates and invertebrates, the Key would be a bool value, and we would have to add a significant text to the header content, so it doesn’t show “True” and “False”.

As you may note, this approach is unrestricted as to how the data is shown. You can group items on several levels, sort the items by any property, and even sort the groups themselves.

Finally, we have to link our converter to the binding. This is done by adding a resource in the page containing the ListBox, and linking the binding to that resource:

<UserControl.Resources>
    <local:PetGrouper x:Key="PetValueConverter"></local:PetGrouper>
</UserControl.Resources>
<Grid x:Name="LayoutRoot" Background="White">
    <ListBox x:Name="List" ItemsSource="{Binding Converter={StaticResource PetValueConverter}}" />
</Grid>

Sample application

Here is a sample application that implements grouping and sorting in a ListBox.

The solution contains two projects: a traditional browser application, and a Windows Phone 7 application.

Conclusion

Even the Silverlight ListBox does not have native support for structured data; we can use a binding converter to customize the binding, creating the layout that best fits our case. We have seen that this approach is simple, straightforward, and yet incredible powerful, and it’s supported on classic Silverlight applications and Phone applications.

 

Alfonso Cora

Filed under: , ,

Comments

# re: Grouping items in a Silverlight ListBox

Thursday, June 23, 2011 10:01 AM by Stimul8d

Straight up genius my man.  Real nice fix to something that should never have been a problem.  Many Thanks

# re: Grouping items in a Silverlight ListBox

Tuesday, July 26, 2011 5:49 AM by Boy

nice solution.

But how make list heads not able to be choosen?

# re: Grouping items in a Silverlight ListBox

Tuesday, August 02, 2011 5:47 PM by uruit

@Boy The easiest solution would be to fix the header styles to be the same for the selected/unselect states. If it doesn't work for you,  there's no standard way to make an item unselectable in Silverlight. Here's a solution for WPF that might work for SL too: social.msdn.microsoft.com/.../537e1d83-f21e-4374-b526-2daecaf25202

# re: Grouping items in a Silverlight ListBox

Wednesday, September 07, 2011 3:15 AM by Ilan

Grouping on multiple columns ?

Assume Pet Class contain two more attributes OwnerID , OwnerName and i would like to group on them.

How do i go about it ?

Created strong typed object  PetGroupdata with OwnerID,OwnerName.

foreach (IGrouping<PetGroupdata,Pet>

temppetGroup  in pets.GroupBy

(x => new PetGroupdata ( x.OwnerID.ToString(),x.OwnerName )))

Iam unable to get the PetGroupData and bind the attributes to the datatemplate ?

     <DataTemplate x:Key="AnimalTemplate">

           <StackPanel  Margin="-3"  Orientation="Horizontal" >

               <TextBlock Text="{Binding  Path=OwnerId}" FontSize="16" FontWeight="Bold" Foreground="ForestGreen" MaxWidth="100" />

           </StackPanel>

       </DataTemplate>

Any thought is highly appreciated ?

# re: Grouping items in a Silverlight ListBox

Tuesday, September 13, 2011 10:51 PM by uruit

@Ilan - Try using an anonymous type for the grouping. stackoverflow.com/.../group-by-with-multiple-columns-using-lambda

# re: Grouping items in a Silverlight ListBox

Tuesday, November 15, 2011 2:45 PM by McLoid

I had troubles with stretching the content to the full width of the site, so i replaced:

groupedPets.Add(new ContentControl() { Content = pet, ContentTemplate = App.Current.Resources["BreedTemplate"] as DataTemplate });

with:

groupedPets.Add(new ListBoxItem() { HorizontalContentAlignment = System.Windows.HorizontalAlignment.Stretch, Content = pet, ContentTemplate = App.Current.Resources["BreedTemplate"] as DataTemplate });

If someone has the same trouble as me...

# re: Grouping items in a Silverlight ListBox

Tuesday, November 29, 2011 1:38 AM by Lynn Neir

Works great, very elegant soln, thanks.

# re: Grouping items in a Silverlight ListBox

Sunday, December 11, 2011 1:57 PM by Ramesh Parmar

this is really good...technique of grouping..

But i hav a problem ... when this listbox itemsource is bind to wcf ria data source...that tym it won't work...can u giv me some solution for this pls....it's very important for my project so pls help me......

Thnx in advance.......PLs...(rparmar2006@gmail.com)..

Leave a Comment

(required) 
(required) 
(optional)
(required)