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"
       xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
       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


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


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 )

Connecting to %s

Categories

Follow

Get every new post delivered to your Inbox.

Join 457 other followers