Introduction
When I was working for a project, we had requirements like a List
should be displayed in a
ListView
and the user should be able to select items through a checkbox. Since Silverlight doesn’t have
a ListBox
control with a checkbox, we tried many ways to achieve this. Finally we got a way to accomplish this. In the below article, I will explain how we did that.
In the UI, the user will have the option to select a list of courses in the
ListBox
and the selected courses will be listed in a GridView
like
in the below figure.
Course Class
I have a class Course
with the properties below:
CourseName
CourseId
NoOfStudents
Class.cs:
using System;
using System.Net;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Documents;
using System.Windows.Ink;
using System.Windows.Input;
using System.Windows.Media;
using System.Windows.Media.Animation;
using System.Windows.Shapes;
namespace ListBoxWithCheckBoxSLXAML
{
public class Course
{
public string CourseName { get; set; }
public int CourseId { get; set; }
public int NoOfStudents { get; set; }
}
}
ViewModel
As usual I have a BaseViewModel
and a viewmodel for the view which inherits from
BaseViewModel
.
BaseViewModel.cs
using System;
using System.Net;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Documents;
using System.Windows.Ink;
using System.Windows.Input;
using System.Windows.Media;
using System.Windows.Media.Animation;
using System.Windows.Shapes;
using System.ComponentModel;
namespace ListBoxWithCheckBoxSLXAML
{
public class BaseViewModel : INotifyPropertyChanged
{
public event PropertyChangedEventHandler PropertyChanged;
protected void NotifyPropertyChanged(string propertyName)
{
if (PropertyChanged != null)
{
PropertyChanged(this, new PropertyChangedEventArgs(propertyName));
}
}
}
}
ViewModel.cs
I have two properties in the viewmodel. One is for list of courses and the other for
the list of selected courses.
using System;
using System.Net;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Documents;
using System.Windows.Ink;
using System.Windows.Input;
using System.Windows.Media;
using System.Windows.Media.Animation;
using System.Windows.Shapes;
using System.Collections.ObjectModel;
namespace ListBoxWithCheckBoxSLXAML
{
public class ViewModel : BaseViewModel
{
private ObservableCollection<Course> _courses;
private ObservableCollection<Course> _selectedcourses;
public ObservableCollection<Course> Courses
{
get
{
return _courses;
}
set
{
_courses = value;
NotifyPropertyChanged("Courses");
}
}
public ObservableCollection<Course> SelectedCourses
{
get
{
return _selectedcourses;
}
set
{
_selectedcourses = value;
NotifyPropertyChanged("SelectedCourses");
}
}
public ViewModel()
{
Courses = new ObservableCollection<Course>()
{
new Course(){CourseId=1,CourseName="JAVA",NoOfStudents=10},
new Course(){CourseId=2,CourseName=".Net",NoOfStudents=10},
new Course(){CourseId=3,CourseName="MainFrame",NoOfStudents=10}
};
SelectedCourses = new ObservableCollection<Course>();
SelectedCourses.CollectionChanged +=
new System.Collections.Specialized.NotifyCollectionChangedEventHandler(
SelectedCourses_CollectionChanged);
}
void SelectedCourses_CollectionChanged(object sender,
System.Collections.Specialized.NotifyCollectionChangedEventArgs e)
{
NotifyPropertyChanged("SelectedCourses");
}
}
}
View
MainPage.xaml:
In my XAML file, under usercontrol.resources
, I add the following code:
<Style x:Key="CheckBoxList" TargetType="ListBox" >
<Setter Property="ItemContainerStyle">
<Setter.Value>
<Style TargetType="ListBoxItem">
<Setter Property="Template">
<Setter.Value>
<ControlTemplate>
<Border Background="Transparent" Margin="{TemplateBinding Padding}">
<CheckBox Content="{Binding CourseName}" VerticalContentAlignment="Center"
IsChecked="{Binding Path=IsSelected,RelativeSource={RelativeSource TemplatedParent},Mode=TwoWay}"/>
</Border>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
</Setter.Value>
</Setter>
</Style>
Here within the ListBox
, for the ListBox
item, I am adding
a template as checkbox and binding
CourseName
with the checkbox content. Also the green highlighted code, I am binding
the IsChecked
property of the checkbox to the IsSelected
property of the ListBox
. So whenever a checkbox (ListBoxItem
) is checked, that item will be added to
the SelectedItems
property of the ListBox
. Simple, isn’t it?
You can find the complete XAML code below:
<UserControl x:Class="ListBoxWithCheckBoxSLXAML.MainPage"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:sdk="http://schemas.microsoft.com/winfx/2006/xaml/presentation/sdk"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:viewmodel="clr-namespace:ListBoxWithCheckBoxSLXAML"
DataContext="{Binding Path=ViewModel}"
mc:Ignorable="d"
d:DesignHeight="300" d:DesignWidth="400">
<UserControl.Resources>
<viewmodel:ViewModel x:Key="ViewModel"/>
<Style x:Key="CheckBoxList" TargetType="ListBox" >
<Setter Property="ItemContainerStyle">
<Setter.Value>
<Style TargetType="ListBoxItem">
<Setter Property="Template">
<Setter.Value>
<ControlTemplate>
<Border Background="Transparent" Margin="{TemplateBinding Padding}">
<CheckBox Content="{Binding CourseName}" VerticalContentAlignment="Center"
IsChecked="{Binding Path=IsSelected,RelativeSource=
{RelativeSource TemplatedParent},Mode=TwoWay}"/>
</Border>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
</Setter.Value>
</Setter>
</Style>
</UserControl.Resources>
<Grid x:Name="LayoutRoot" Background="White" DataContext="{Binding Source={StaticResource ViewModel}}">
<Grid.RowDefinitions>
<RowDefinition Height="Auto"/>
<RowDefinition Height="Auto"/>
<RowDefinition Height="Auto"/>
<RowDefinition Height="Auto"/>
</Grid.RowDefinitions>
<ListBox x:Name="Courses" Grid.Row="0" SelectionMode="Multiple"
Style="{StaticResource CheckBoxList}" HorizontalAlignment="Stretch"
ItemsSource="{Binding Courses}" Height="100" Width="100"
SelectionChanged="Courses_SelectionChanged"/>
<sdk:DataGrid x:Name="gvSelectedCourses" ItemsSource="{Binding SelectedCourses}"
Grid.Row="1" AutoGenerateColumns="False" Width="300">
<sdk:DataGrid.Columns>
<sdk:DataGridTextColumn Header="Course Name" Binding="{Binding CourseName}" Width="100"/>
<sdk:DataGridTextColumn Header="Course Id" Binding="{Binding CourseId}" Width="100"/>
<sdk:DataGridTextColumn Header="No Of Students" Binding="{Binding NoOfStudents}" Width="100"/>
</sdk:DataGrid.Columns>
</sdk:DataGrid>
</Grid>
</UserControl>
This is not over yet. Here is the problem for us. Since we can’t bind the
SelectedItems
property in XAML, we have to bind that in the code behind. Wiring up
the viewmodel in
the view is not violating MVVM, sometimes we can use that. So in the code behind, in the constructor, I add the below code, since I am binding
the viewmodel with the grid
in XAML and not with the entire view.
this.DataContext = LayoutRoot.DataContext;
In the selection changed event of the ListBox
, I add the below code to add
SelectedItems
to SelectedCourses
.
((ViewModel)DataContext).SelectedCourses.Clear();
foreach (Course item in Courses.SelectedItems)
{
((ViewModel)DataContext).SelectedCourses.Add(item);
}
This is my complete code behind code.
using System;
using System.Collections.Generic;
using System.Linq;
using System.Net;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Documents;
using System.Windows.Input;
using System.Windows.Media;
using System.Windows.Media.Animation;
using System.Windows.Shapes;
using System.Collections.ObjectModel;
namespace ListBoxWithCheckBoxSLXAML
{
public partial class MainPage : UserControl
{
public MainPage()
{
InitializeComponent();
this.DataContext = LayoutRoot.DataContext;
}
private void Courses_SelectionChanged(object sender, SelectionChangedEventArgs e)
{
((ViewModel)DataContext).SelectedCourses.Clear();
foreach (Course item in Courses.SelectedItems)
{
((ViewModel)DataContext).SelectedCourses.Add(item);
}
}
}
}
You can play around with the application now.