Building a ListView with Checkboxes in WPF

So, in my work to build the GUI for my DMF project, I came across a need for a list view with check boxes in the first column.

I was creating a dialog window for editing a user account, and needed a simple way of defining what roles that user would have assigned to them.  I know some traditional methods are to display two lists with back and forth buttons between them, but that’s so 1990’s.

I wanted to display the checkbox, its title, category name, and description of what the role provides.  This is best done in a list view, as opposed to just plain check boxes in a list box.

Naturally, I started with some google searches because that’s how you always start these little adventures.  I found out that putting a check box in the list view column was actually very easy!  But none of the articles really went past that point.  The check box is there, but the whole list view still looks like crap and doesn’t operate very well at all.

I’ll try and list all the missing pieces here to get to that finished list view that works great.

So, to start off, here is the basic piece of code needed to get the check box in there. Add a Window.Resources section to your XAML with the following DataTemplate.

[cclne_csharp width=”800″ lines=”-1″ width=”700″]



[/cclne_csharp]

When run, you will see that this does actually get you some check boxes in the list view column. But there are problems. This is what you’ll see.

Check boxes with problems

To fix it, we want to get rid of all the selection highlights. Add this new style definition to your Windows.Resources section as well. What this is doing is redefining four system colors used by the ControlTemplate within the ListView. You can use other colors if you like, but I wanted the highlight bar to match the normal white background, and the text to always stay black.

[cclne_csharp width=”800″ lines=”-1″ width=”700″]
[/cclne_csharp]

We are setting the Focusable property of the ListViewItems to be False. This stops the mouse from clicking on list view rows. The problem with list views is that when you click in a row, any other selected rows are unselected in favour of the clicked row. This unchecks all those other check boxes. With the Focusable property turned off, it ignores all mouse clicks so that the user must actually click the check box, or check box content. The check box is still focusable though so you can tab into the list view and move around and select check boxes with the space bar.

We will try and fix the mouse clicking issue later because we really do want to click anywhere in the row.

In order for the new style to be used, you must add the following attribute to your ListView tag.

[cclne_csharp width=”800″ lines=”-1″ width=”700″]
[/cclne_csharp]

Now, when you run the code, you should see the following.

Nicer looking check box view

So, now you want to add the ability to click anywhere in the row to select a check box. This is actually very easy by adding an event handler. Add the following attribute to the ListView.

[cclne_csharp width=”800″ lines=”-1″ width=”700″] MouseLeftButtonUp=”roleList_MouseLeftButtonUp”
[/cclne_csharp]

Then, I added the following code for the event handler.

[cclne_csharp width=”800″ lines=”-1″ width=”700″] private void roleList_MouseLeftButtonUp(object sender, MouseButtonEventArgs e)
{
var item = ((FrameworkElement)e.OriginalSource).DataContext as DmfRole;
if (item == null) return;

if (roleList.SelectedItems.Contains(item))
roleList.SelectedItems.Remove(item);
else
roleList.SelectedItems.Add(item);
}
[/cclne_csharp]

The first couple lines find the data object that was clicked on. If the user clicked somewhere other than a row, it returns right away. Next, we look at the selected items in the list view and see if the item is already there or not. If it is, we remove it. If it isn’t, we add it. Simple!

Oh, and in case you’re trying to figure out how to pre-select some of the check boxes, just add the items to the SelectedItems collection like I did above. Just make sure the item you’re adding is the same type as the items in the collection you used in the ItemsSource.

[cclne_csharp width=”800″ lines=”-1″ width=”700″] foreach (DmfRole role in roleList.Items)
{
if (_user.HasRole(role.Name)) roleList.SelectedItems.Add(role);
}
[/cclne_csharp]

I hope this helps someone out!

This entry was posted in WPF and tagged , , , . Bookmark the permalink.