Posted by: Zeeshan Amjad | May 15, 2011

Applying Multiple Group Style in ListView


We already saw an example of applying multiple group style here. Now we are going to do the same thing with ListView. The basic concept is almost similar. The only different is how we are going to bind and display items in these controls. At first step we are going to define two data template resources in XAML, one for each group header. Here is a XAML code for it.

Code Snippet
<DataTemplate x:Key="StateTemplate">
    <Border Margin="2" BorderBrush="Brown" BorderThickness="1" CornerRadius="5">
        <TextBlock Margin="2" FontSize="16" FontWeight="Bold"
                  Foreground="Brown" Text="{Binding Path=Name}"/>
    </Border>
</DataTemplate>
<DataTemplate x:Key="CountyTemplate">
    <Border Margin="2" BorderBrush="Brown" Background="AliceBlue" BorderThickness="1" CornerRadius="5">
        <TextBlock Margin="2" FontSize="12" Foreground="Red"
                  TextAlignment="Center" Text="{Binding Path=Name}"/>
    </Border>
</DataTemplate>

 

Then we are going to add one class in our project. That class is inherited by DataTemplateSelector and define two DataTemplate type properties in it. We also override SelectTemplate method.

But now we have a problem. Here container is in fact CollectionViewGroupInternal class, which is inherited by CollectionViewGroup class. And we can’t access that class directly from our code, at least not easily.

But we know that our grouping is only two level depth therefore we can apply little trick here. If it is a first level grouping then all of items in this group is also a type of CollectionViewGroupInternal type. On the other hand if it is second level of grouping then its items type is StateInfo class. We are going to use this information and update our overridden method. Here is our new overridden method.

Code Snippet
public class MyTemplateSelector : DataTemplateSelector
{
    public DataTemplate StateTemplate
    { get; set; }

    public DataTemplate CountyTemplate
    { get; set; }

    public override DataTemplate SelectTemplate(object item, DependencyObject container)
    {
        ContentPresenter cp = container as ContentPresenter;

        if (cp != null)
        {
            CollectionViewGroup cvg = cp.Content as CollectionViewGroup;

            if (cvg.Items.Count > 0)
            {
                StateInfo stinfo = cvg.Items[0] as StateInfo;

                if (stinfo != null)
                    return CountyTemplate;
                else
                    return StateTemplate;
            }
        }

        return base.SelectTemplate(item, container);
    }
}

 

Now it works in this case. But of course there is limitation, we can’t use the same technique if the grouping is more than two level depth. Here is a complete XAML code of our program.

Code Snippet
<Window x:Class="WpfListViewGroup.MainWindow"
       xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation&quot;
       xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml&quot;
       xmlns:local="clr-namespace:WpfListViewGroup"
       Title="MainWindow" Height="400" Width="525">
    <Window.Resources>
        <DataTemplate x:Key="StateTemplate">
            <Border Margin="2" BorderBrush="Brown" BorderThickness="1" CornerRadius="5">
                <TextBlock Margin="2" FontSize="16" FontWeight="Bold"
                          Foreground="Brown" Text="{Binding Path=Name}"/>
            </Border>
        </DataTemplate>
        <DataTemplate x:Key="CountyTemplate">
            <Border Margin="2" BorderBrush="Brown" Background="AliceBlue" BorderThickness="1" CornerRadius="5">
                <TextBlock Margin="2" FontSize="12" Foreground="Red"
                          TextAlignment="Center" Text="{Binding Path=Name}"/>
            </Border>
        </DataTemplate>
        <local:MyTemplateSelector x:Key="MyTemplateSelectorObj"
             CountyTemplate="{StaticResource CountyTemplate}"
             StateTemplate="{StaticResource StateTemplate}"/>
    </Window.Resources>
    <Grid>
        <ListView Name="list" Margin="5" ItemsSource="{Binding}">
            <ListView.GroupStyle>
                <GroupStyle HeaderTemplateSelector="{StaticResource MyTemplateSelectorObj}"/>
            </ListView.GroupStyle>
            <ListView.View>
                <GridView>
                    <GridViewColumn Header="State" Width="100" DisplayMemberBinding="{Binding State}"/>
                    <GridViewColumn Header="County" Width="100" DisplayMemberBinding="{Binding County}"/>
                    <GridViewColumn Header="City" Width="100" DisplayMemberBinding="{Binding City}"/>
                    <GridViewColumn Header="Population" Width="200" DisplayMemberBinding="{Binding Population}"/>
                </GridView>
            </ListView.View>
        </ListView>
    </Grid>
</Window>

 

Here is complete C# code of our program.

Code Snippet
using System.Collections.ObjectModel;
using System.Windows;
using System.Windows.Data;
using System.Windows.Controls;

namespace WpfListViewGroup
{
    /// <summary>
    /// Interaction logic for MainWindow.xaml
    /// </summary>
    public partial class MainWindow : Window
    {
        private ObservableCollection<StateInfo> states = new ObservableCollection<StateInfo>();

        public MainWindow()
        {
            InitializeComponent();

            states.Add(new StateInfo("Maryland", "Frederick", "Frederick", 52767));
            states.Add(new StateInfo("Maryland", "Frederick", "Brunswick", 4894));
            states.Add(new StateInfo("Maryland", "Frederick", "New Market", 427));
            states.Add(new StateInfo("Maryland", "Montgomery", "Rockville", 60734));
            states.Add(new StateInfo("Maryland", "Montgomery", "Gaithersburg", 52613));
            states.Add(new StateInfo("Taxes", "Harris", "Deer Park", 28520));
            states.Add(new StateInfo("Taxes", "Harris", "Jersey Village", 6880));
            states.Add(new StateInfo("Taxes", "Dallas", "Irving", 201927));
            states.Add(new StateInfo("Taxes", "Dallas", "DeSoto", 37646));
            states.Add(new StateInfo("Taxes", "Bexar", "Leon Valley", 9239));
            states.Add(new StateInfo("California", "Los Angeles", "Burbank", 100316));
            states.Add(new StateInfo("California", "Los Angeles", "Azusa", 44712));
            states.Add(new StateInfo("California", "Los Angeles", "Culver City", 38816));
            states.Add(new StateInfo("California", "Los Angeles", "Glendale", 194973));
            states.Add(new StateInfo("California", "Sacramento", "Citrus Heights", 85071));
            states.Add(new StateInfo("California", "Sacramento", "Elk Grove", 59984));

            list.ItemsSource = states;

            CollectionView cv = (CollectionView)CollectionViewSource.GetDefaultView(list.ItemsSource);
            PropertyGroupDescription group1 = new PropertyGroupDescription("State");
            PropertyGroupDescription group2 = new PropertyGroupDescription("County");
            cv.GroupDescriptions.Add(group1);
            cv.GroupDescriptions.Add(group2);
        }
    }

    public class StateInfo
    {
        public StateInfo(string state, string county, string city, int population)
        {
            State = state;
            County = county;
            City = city;
            Population = population;
        }

        public string State
        { get; set; }

        public string County
        { get; set; }

        public string City
        { get; set; }

        public int Population
        { get; set; }
    }

    public class MyTemplateSelector : DataTemplateSelector
    {
        public DataTemplate StateTemplate
        { get; set; }

        public DataTemplate CountyTemplate
        { get; set; }

        public override DataTemplate SelectTemplate(object item, DependencyObject container)
        {
            ContentPresenter cp = container as ContentPresenter;

            if (cp != null)
            {
                CollectionViewGroup cvg = cp.Content as CollectionViewGroup;

                if (cvg.Items.Count > 0)
                {
                    StateInfo stinfo = cvg.Items[0] as StateInfo;

                    if (stinfo != null)
                        return CountyTemplate;
                    else
                        return StateTemplate;
                }
            }

            return base.SelectTemplate(item, container);
        }
    }
}

 

Here is the output of our program.

ListViewMultiLevelGroupStyle

Advertisements

Responses

  1. I need source code help me

    • Thanks to visit my blog and post comment here. I email you the source code of complete project.

      Regards
      Zeeshan Amjad

  2. Thank you very much 🙂
    it would be better if you put a link to source code

    • I am planning to do it, but currently I am quite busy in other stuff. I will do it as soon as I can. Thanks to visit and like the blog.
      Regards
      Zeeshan Amjad

  3. Many thanks for the great tutorial!

    I’m wondering how to add an Expander for the grouped output.
    When I add the Expander inside the ControlTemplate inside the GroupStyle.ContainerStyle, it overwrites the DataTemplates 😦
    Please can you support me with this task.

    Many thanks in advance 🙂

    • Thanks to like it. I am not sure what is going wrong in your code without seeing it. Can you please email me the code to reproduce this, so i will take a look at this.


Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s

Categories

%d bloggers like this: