Checkbox Inside Listbox to Select Multiple Items in Silverlight Using MVVM


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 found a way to do it. In the following article, I will explain how we did that.

In a UI, the user will have an option to select a list of courses in a listbox and the selected courses will be listed in the gridview like in the following figure.

ChkSIl1.gif

Course Class

I have a class Course with the following properties.

  1. CourseName
  2. CourseId
  3. 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 one BaseViewModel and a viewmodel for a 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 2 properties in the viewmodel. One is for a list of courses and the other is for a 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 added 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 a checkbox and binding a CourseName with checkbox content. Also, in the green highlighted code, there 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 another problem for us. Since we can't bind a SelectedItems property in XAML, we have to bind that in code behind. Wiring up a viewmodel in a view is not a violattion of MVVM, sometimes we can use that. So in the code behind, in the constructor, I added the following 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 listbox, I added the following code to add selecteditems to the 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();
            //since i bound viewmodel to grid, here am binding to entire view.
            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 the application now.


Similar Articles