Mostefai Mohammed Amine

Software and Cloud Architect

Amine
  • Contact
Previous Post
Next Post
Dec 13, 2013 Visual StudioC#XAML

Create a Multi-Checkbox List in WPF

Hi,

In this tutorial, I will show how to implement a multi-checkbox WPF list that allows the selection of multiple items of any type. For this, will use the power of templates in WPF and generics of C#. We will also exploit the very useful “INotifyPropertyChanged” interface.

First, let’s create a WPF project called “TestMultiCheck”.

1- Create the “SelectionItem” class

Add a new class called “SelectionItem” that have to be generic. The code of the class is as follows:

/// <summary>
    /// an item that will be selected in the multi check box
    /// </summary>
    /// <typeparam name="T"></typeparam>
    public class SelectionItem<T> : INotifyPropertyChanged
    {
        #region privat fields
        /// <summary>
        /// indicates if the item is selected
        /// </summary>
        private bool _isSelected;
        #endregion
 
        public SelectionItem(T element, bool isSelected)
        {
            Element = element;
            IsSelected = IsSelected;
        }
 
        public SelectionItem(T element):this(element,false)
        {
 
        }
 
        #region public properties
        /// <summary>
        /// this UI-aware indicates if the element is selected or not
        /// </summary>
        public bool IsSelected
        {
            get
            {
                return _isSelected;
            }
            set
            {
                _isSelected = value;
                if (PropertyChanged != null)
                    PropertyChanged(this, new PropertyChangedEventArgs("IsSelected"));
            }
        }
 
        /// <summary>
        /// the element itself
        /// </summary>
        public T Element { get; set; }
        #endregion
 
        #region INotifyPropertyChanged Members
 
        public event PropertyChangedEventHandler PropertyChanged;
 
        #endregion
    }

The “SelectionItem” is a container for any element of the type “T”. The property IsSelected indicates if the item has been selected or not. Notice that the class implements the “INotifyPropertyChanged” interface to make the UI reacts if the selection is performed in code.

2- Create the “SelectionList” class

Let’s add another generic class called “SelectionList”

/// <summary>
    /// a list that will include the elements that will be selected
    /// </summary>
    /// <typeparam name="T"></typeparam>
    public class SelectionList<T> : List<SelectionItem<T>>, INotifyPropertyChanged
    {
        #region private fields
        /// <summary>
        /// the number of selected elements
        /// </summary>
        private int _selectionCount;
        #endregion
 
        #region private methods
        /// <summary>
        /// this events responds to the "IsSelectedEvent"
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        void item_PropertyChanged(object sender, PropertyChangedEventArgs e)
        {
            var item = sender as SelectionItem<T>;
            if ((item != null) && e.PropertyName == "IsSelected")
            {
                if (item.IsSelected)
                    SelectionCount = SelectionCount + 1;
                else
                    SelectionCount = SelectionCount - 1;
            }
        }
 
        #endregion
 
 
 
        public SelectionList()
        { }
 
        /// <summary>
        /// creates the selection list from an existing simple list
        /// </summary>
        /// <param name="elements"></param>
        public SelectionList(IEnumerable<T> elements)
        {
            foreach (T element in elements)
                AddItem(element);
        }
 
        #region public methods
        /// <summary>
        /// adds an element to the element and listens to its "IsSelected" property to update the SelectionCount property
        /// use this method insteand of the "Add" one
        /// </summary>
        /// <param name="element"></param>
        public void AddItem(T element)
        {
            var item = new SelectionItem<T>(element);
            item.PropertyChanged += item_PropertyChanged;
 
            Add(item);
        }
 
        /// <summary>
        /// gets the selected elements
        /// </summary>
        /// <returns></returns>
        public IEnumerable<T> GetSelection()
        {
            return this.Where(e => e.IsSelected).Select(e => e.Element);
        }
 
        /// <summary>
        /// uses linq expression to select a part of an object (for example, only id)
        /// </summary>
        /// <typeparam name="U"></typeparam>
        /// <param name="expression"></param>
        /// <returns></returns>
        public IEnumerable<U> GetSelection<U>(Func<SelectionItem<T>, U> expression)
        {
            return this.Where(e => e.IsSelected).Select(expression);
        }
 
        #endregion
 
 
        #region public properties
        /// <summary>
        /// the selection count property is ui-bindable, returns the number of selected elements
        /// </summary>
        public int SelectionCount
        {
            get { return _selectionCount; }
 
            private set
            {
                _selectionCount = value;
                if (PropertyChanged != null)
                    PropertyChanged(this, new PropertyChangedEventArgs("SelectionCount"));
            }
        }
        #endregion
 
        #region INotifyPropertyChanged Members
 
        public event PropertyChangedEventHandler PropertyChanged;
 
        #endregion
    }

This class can be instantiated empty and filled using the “Additem” method. You can alternatively, use the second overload directly to create the list from an existing enumerable. The property “SelectionCount” indicates the number of selected elements.

The first “GetSelection” methods returns all the selected elements. The second overload returns only a part using linq expressions. It could be very useful for database application to get directly the ids of the selected elements.

3- Create a Sample Entity

To demonstrate the two classes, let’s create a sample entity called course defined as follows :

public class Course
    {
        public Course()
        { }
 
        public Course(int id, string name)
        {
            Id = id;
            Name = name;
        }
 
        public int Id { get; set; }
 
        public string Name { get; set; }
    }

The tutorial will allow to select multiple courses

4- Create The XAML View

In the mainwindow view, add a stackpanel, two buttons and a list box. The first button will be used to select / unselect an element in the list.

The second button will be enabled only if at least one element is checked. Once clicked, it displays the names of the selected courses. The listbox will be converted using data templates to a list that displays a checkbox aside of the name of the course. For this, we will use the following data template :

<DataTemplate x:Key="ListBoxItemTemplate" >
            <WrapPanel>
                <CheckBox Focusable="False" IsChecked="{Binding IsSelected}" VerticalAlignment="Center"  />
                <ContentPresenter Content="{Binding Element.Name, Mode=OneTime}"  Margin="2,0" />
            </WrapPanel>          
</DataTemplate>

The default behavior of the listbox is to display vertically its items. It is not the perfect behavior that we are looking for. It would be better to display the checkboxes side by side, for this, we will used an alternative ItemsPanelTempolate based on “WrapPanels” :

<ListBox.ItemsPanel>
                <ItemsPanelTemplate>
                    <WrapPanel Orientation="Horizontal"></WrapPanel>
                </ItemsPanelTemplate>
            </ListBox.ItemsPanel>

To make this behavior concrete, we have to deactivate the horizontal scrolling in the listbox. The full view xaml is like follows:

<Window x:Class="TestMultiCheck.MainWindow"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        Title="MainWindow" Height="350" Width="525">
    <Window.Resources>
        <DataTemplate x:Key="ListBoxItemTemplate" >
            <WrapPanel>
                <CheckBox Focusable="False" IsChecked="{Binding IsSelected}" VerticalAlignment="Center"  />
                <ContentPresenter Content="{Binding Element.Name, Mode=OneTime}"  Margin="2,0" />
            </WrapPanel>          
        </DataTemplate>
    </Window.Resources>
    <Grid>
        <Grid.RowDefinitions>
            <RowDefinition Height="40" />
            <RowDefinition Height="*"/>
        </Grid.RowDefinitions>
       
        <StackPanel Orientation="Horizontal">
            <Button Content="Select / Unselect Element By Code"   Click="Button_Click" Margin="5"/>
            <Button x:Name="Save" Content="Save Selection" IsEnabled="False" Margin="5" Click="Save_Click"/>
        </StackPanel>
        <ListBox x:Name="myList" Grid.Row="1" Margin="5" ItemTemplate="{DynamicResource ListBoxItemTemplate}" ScrollViewer.HorizontalScrollBarVisibility="Disabled">
            <ListBox.ItemsPanel>
                <ItemsPanelTemplate>
                    <WrapPanel Orientation="Horizontal"></WrapPanel>
                </ItemsPanelTemplate>
            </ListBox.ItemsPanel>
        </ListBox>
    </Grid>
</Window>
 

5 – The Code Behind

The code behind is as follows:

public partial class MainWindow : Window
    {
        /// <summary>
        /// the selection list
        /// </summary>
        private SelectionList<Course> _list;
 
        /// <summary>
        /// the list of courses
        /// </summary>
        Course[] courses = new Course[] { 
            new Course(1, "course1"),
            new Course(2, "course2"),
            new Course(3, "course3"),
            new Course(4, "course4"),
            new Course(5, "course5")
 
        };
        public MainWindow()
        {
            InitializeComponent();
            // create the list
            _list = new SelectionList<Course>(courses);
            // assign the source of the list (not necessary in an MVVM approach)
            myList.ItemsSource = _list;
            // listen to the property changed event to enable or disable the save button
            _list.PropertyChanged += list_PropertyChanged;
        }
 
        void list_PropertyChanged(object sender, System.ComponentModel.PropertyChangedEventArgs e)
        {
            // the save button is enabled if at least one item is checked
            Save.IsEnabled = _list.SelectionCount > 0;
        }
 
        private void Button_Click(object sender, RoutedEventArgs e)
        {
            // this methods checks or unchecks programmatically an item to view the UI reaction
            _list[1].IsSelected = !_list[1].IsSelected;
        }
 
        private void Save_Click(object sender, RoutedEventArgs e)
        {
            // get the names of the selected courses
            IEnumerable<string> selection = _list.GetSelection(elt => elt.Element.Name);
            // concantenate the strings
            var text = string.Join(",", selection);
            MessageBox.Show(text);
        }
    }

The window contains an array of courses called “courses” and a private selection list called “_list”.

At the page load event we create the list, and we assign an event to the “PropertyChanged” event. We assign the list as itemssource of the listbox. In MVVM approaches, this could be performed by using bindings or conventions.

The event “list_PropertyChanged” enabled or disables the “Save” button according to the number of the checked elements.

The event “Button_Click” checks “programmatically an element of the list to see that the view is effectively bound to the “IsSelected” property.

Finally, the “Save_Click” gets directly the names of the selected courses by using Linq expressions. Then, it displays them.

image

 

 

 

 

 

 

Enjoy !

The solution code can be downloaded here

TemplateCheckboxListBox
Share This Post

Related posts

  • Create a Multi-Checkbox List in WPF Hi, In this tutorial, I will show how to implement a multi-checkbox WPF list that allows the select...
  • Tutoriel Workflow Foundation 2.3–Création et Réutilisation d’Activités L’objectif de ce tutoriel est de créer des activités par code. L’objectif est de voir aussi comment
  • WF Cours 4–Services Avancés. Tutoriel 4.1 Persistance–Partie 2 Ce tutoriel est la suite de la première partie qui consiste à mettre en place un workflow utilisant ...

Comments (6) -

  • Joe
    01 May 2014 | Reply
    This was very helpful and useful, thanks!
  • Trieu
    29 May 2015 | Reply
    How to make select all items?
    Please help me.
    Thanks
  • Amine
    30 May 2015 | Reply
    You have to add a "SelectAll" method to the "SelectionList" class that changes the "IsSelected" to true to all its elements.
    If you are using MVVM, you can bind this to a command in the ViewModel.
  • Trieu
    31 May 2015 | Reply
    Thanks,
    I got it.
  • Brant
    06 Feb 2017 | Reply
    Bug in SelectionItem constructor:
                IsSelected = IsSelected;
    should be:
                IsSelected = isSelected;

      Awesome example, though. Thanks a bunch!

    • Amine
      07 Feb 2017 | Reply
      Indeed. Thanks for reporting it
Saving the comment

Cancel reply to comment

The captcha value you provided is incorrect.