This is an example to show the use of MVVM in creating a grouping listbox. The example shows a list of employees with their job title, name and grade.
I’ve defaulted to grouping by Job Title and supplied to buttons on the window to allow you to toggle the grouping between job title and grade.
This is to demonstrate the dynamic behaviour of the grouping in the view model.
First let’s start with the EmployeeViewModel
:
public class EmployeeViewModel
{
public string Name
{
get;
set;
}
public string JobTitle
{
get;
set;
}
public string Grade
{
get;
set;
}
}
Nothing to strenuous here, just a POCO.
Followed by the MainViewModel
:
public class MainViewModel
{
private ObservableCollection employees = new ObservableCollection();
private ObservableCollection groupingColumns = new ObservableCollection();
public MainViewModel()
{
var view = CollectionViewSource.GetDefaultView(Employees);
GroupByJobTitleCommand = new GroupByCommand(GroupingColumns, "JobTitle");
GroupByGradeCommand = new GroupByCommand(GroupingColumns, "Grade");
GroupingColumns.CollectionChanged += (s, e) =>
{
view.GroupDescriptions.Clear();
foreach (var groupName in GroupingColumns)
{
view.GroupDescriptions.Add(new PropertyGroupDescription(groupName));
}
};
}
public ObservableCollectionEmployees
{
get { return this.employees; }
}
public ObservableCollectionGroupingColumns
{
get { return this.groupingColumns; }
}
public ICommand GroupByJobTitleCommand
{
get;
private set;
}
public ICommand GroupByGradeCommand
{
get;
private set;
}
}
And the GroupByCommand
that will allow us to switch the grouping field:
public class GroupByCommand : ICommand
{
private ObservableCollection groupingColumns;
private string groupName;
public GroupByCommand(ObservableCollectiongroupingColumns, string groupName)
{
this.groupingColumns = groupingColumns;
this.groupName = groupName;
}
public bool CanExecute(object parameter)
{
return true;
}
public event EventHandler CanExecuteChanged;
public void Execute(object parameter)
{
this.groupingColumns.Clear();
this.groupingColumns.Add(this.groupName);
}
}
Followed by the XAML:
<Grid Grid.IsSharedSizeScope="True">
<Grid.RowDefinitions>
<RowDefinition Height="*"/>
<RowDefinition Height="Auto"/>
</Grid.RowDefinitions>
<ListBox ItemsSource=’{Binding Employees}’>
<ListBox.GroupStyle>
<x:Static Member=’GroupStyle.Default’ />
</ListBox.GroupStyle>
<ListBox.ItemTemplate>
<DataTemplate>
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition SharedSizeGroup="col1" Width="200"/>
<ColumnDefinition SharedSizeGroup="col2" Width="200"/>
<ColumnDefinition SharedSizeGroup="col3" Width="200"/>
</Grid.ColumnDefinitions>
<TextBlock Text=’{Binding Name}’/>
<TextBlock Text=’{Binding JobTitle}’ Grid.Column="1"/>
<TextBlock Text=’{Binding Grade}’ Grid.Column="2"/>
</Grid>
</DataTemplate>
</ListBox.ItemTemplate>
</ListBox>
<StackPanel Orientation="Horizontal" Grid.Row="1">
<Button Command="{Binding GroupByJobTitleCommand}">Group By Job Title</Button>
<Button Command="{Binding GroupByGradeCommand}">Group By Job Grade</Button>
</StackPanel>
</Grid>
The XAML here is pretty straightforward, but a couple of points:
- We’ve used
ListBox.GroupStyle
to tell the list box we want group styling. - The
IsSharedSizeScope
on the out Grid and SharedSizeGroup
on the column definitions have been used to make each row line up and give us the appearance
of a more traditional grid with the data in columns.
And finally, the code behind (dirty words I know, but it’s just setting up the
DataContext
):
public MainWindow()
{
InitializeComponent();
var mainViewModel = new MainViewModel();
mainViewModel.Employees.Add(new EmployeeViewModel() {
Name = "Blogs, Fred", JobTitle = "Programmer", Grade = "1" });
mainViewModel.Employees.Add(new EmployeeViewModel() {
Name = "Gates, Bill", JobTitle = "Project Manager", Grade = "1" });
mainViewModel.Employees.Add(new EmployeeViewModel() {
Name = "Jobs, Steve", JobTitle = "Analyst", Grade = "2" });
mainViewModel.Employees.Add(new EmployeeViewModel() {
Name = "Smith, John", JobTitle = "Programmer", Grade = "2" });
mainViewModel.Employees.Add(new EmployeeViewModel() {
Name = "Davis, Mavis", JobTitle = "Project Manager", Grade = "3" });
this.DataContext = mainViewModel;
}
So we’ve basically set up a list of employees in the MainViewModel
and attached an instance of it as our
DataContext
. In this view model, we have a couple of commands for actually doing the grouping. By default we have a normal list of employees and then selecting either of the buttons will group them by that property.
I believe this is a more MVVM approach to doing grouping in a list box (or anything that support grouping) and keeps the logic in the view model. I’ve seen other examples, but most of them use the
XAML to control the grouping and usually have fixed data sets in XAML declared as static resources.
The styling of this leaves a lot to be desired and you can’t expand or collapse the groups. To see how this is done, I suggest you look at Karl Shifflett’s example for some inspiration.
You can download the code from this article here.
<fb:like href="http://tap-source.com/?p=172" width="45" show_faces="false" layout="box_count">