Skip Navigation LinksHome > View Post

ViewModels and CheckListBoxes

During a recent WPF presentation I did for the folk at the .NET Developer Network (dotnetdevnet.com - neat domain) in Bristol, one of the attendees posed me a question about how you might implement a ViewModel to deal with a CheckListBox in WPF.

The first thing to note is kind of important: there is no CheckListBox in WPF. Uh oh. Well, not really. It's mostly just a case of composition thanks to WPF's lookless controls.

For a quick win, we can just 're-template' a ListBox to provide CheckBoxes in the individual ItemTemplate(s). We'll look at exactly how we do this a little later.

The obvious feature missing from this implementation is the checking/selection algorithm implemented in Windows Forms CheckListBox but that shouldn't be too hard to put together. I'll leave it as an exercise for the reader for now to keep this article concise.

Now we know how to create a simple CheckListBox in WPF, how do we create a suitable ViewModel for it to bind to. Key to this is the idea of composing multiple ViewModels to create the full ViewModel picture.

In this post we'll look at creating a simple solution to this. There are many possible ways to skin this particular cat and this is just one. YMMV etc.

I like and agree with David Hill's perspective on ICollectionView in WPF: "... CollectionViews were actually good examples of the ViewModel pattern applied to collection-based models. In this post I explain in more detail why I think of CollectionViews as ViewModels." (from his post CollectionViewModel).

We'll take this idea and run with it by creating a special collection based view to deal with the 'checkability' required by our CheckListBox. Obviously, we want to bind our CheckListBox to some kind of IEnumerable, preferably an ObservableCollection to better support updates for bindings. And so my thread of thought began. Step 1: Inherit from an ObservableCollection and wrap the item type in a CheckableWrapper<T>:

public class CheckWrapper<T> : INotifyPropertyChanged
{
    private readonly CheckableObservableCollection<T> _parent;

    public CheckWrapper(CheckableObservableCollection<T> parent)
    {
        _parent = parent;
    }

    private T _value;

    public T Value
    {
        get { return _value; }
        set
        {
            _value = value;
            OnPropertyChanged("Value");
        }
    }

    private bool _isChecked;

    public bool IsChecked
    {
        get { return _isChecked; }
        set
        {
            _isChecked = value;
            CheckChanged();
            OnPropertyChanged("IsChecked");
        }
    }

    private void CheckChanged()
    {
        _parent.Refresh();
    }

    public event PropertyChangedEventHandler PropertyChanged;

    protected virtual void OnPropertyChanged(string propertyName)
    {
        PropertyChangedEventHandler pceh = PropertyChanged;
        if (pceh != null)
        {
            pceh(this, new PropertyChangedEventArgs(propertyName));
        }
    }
}

Which as you can see, is 'owned' by a new CheckableObservableCollection<T> type:

public class CheckableObservableCollection<T> : ObservableCollection<CheckWrapper<T>>
{
    private ListCollectionView _selected;

    public CheckableObservableCollection()
    {
        _selected = new ListCollectionView(this);
        _selected.Filter = delegate(object checkObject) {
            return ((CheckWrapper<T>)checkObject).IsChecked;
        };
    }

    public void Add(T item)
    {
        this.Add(new CheckWrapper<T>(this) { Value = item });
    }

    public ICollectionView SelectedItems
    {
        get { return _selected; }
    }

    public void Refresh()
    {
        _selected.Refresh();
    }
}

I've done the bare minimum to get this scenario to work here, you can imagine a bunch of other methods and properties that would be useful to these classes. (Note, the call to Refresh on the ListCollectionView everytime an item is checked is a bit wasteful WRT performance - but it would be easy to fix with a bit more time and quite a bit more code).

The scenario I want to implement in this case is pretty simple. Populate a CheckListBox using binding and have the check status represented in the ViewModel so that I can code against it to write back to the database.

Also, I'd like to be able to show the Checked Items in a seperate ItemsControl just using some databinding.

We now need to hydrate our ViewModel with some data. For now I'll hardcode this in the form of some strings but in a later post I'll look at how I might get this from a database and save any changes back to a database.

Here's the very simple parent ViewModel and you can see the data population taking place in the constructor:

public class ViewModel : INotifyPropertyChanged
{
    public ViewModel()
    {
        Items = new CheckableObservableCollection<string> { "hello", "goodbye", "this is cool", "here's another option" };
    }

    private CheckableObservableCollection<string> _items;

    public CheckableObservableCollection<string> Items
    {
        get { return _items; }
        set
        {
            _items = value;
            OnPropertyChanged("Items");
        }
    }

    public event PropertyChangedEventHandler PropertyChanged;

    protected virtual void OnPropertyChanged(string propertyName)
    {
        PropertyChangedEventHandler pceh = PropertyChanged;
        if (pceh != null)
        {
            pceh(this, new PropertyChangedEventArgs(propertyName));
        }
    }
}

Cool. So now all we need is some Xaml to bind to this stuff. First, here's our 'CheckListBox' made especially for binding to CheckableObservableCollection in this case:

<!-- TODO: Implement Checking/Selection algo -->
<ListBox ItemsSource="{Binding Items}" Margin="4" SelectionMode="Extended">
    <ListBox.ItemTemplate>
        <DataTemplate>
            <StackPanel Orientation="Horizontal">
                <CheckBox Name="check" IsChecked="{Binding IsChecked, Mode=TwoWay}" Margin="3" VerticalAlignment="Center" />
                <ContentPresenter Content="{Binding Value}" Margin="1"/>
            </StackPanel>
        </DataTemplate>
    </ListBox.ItemTemplate>
</ListBox>

And finally, we need to bind our 'Selected Items' part of the view to the CheckedItems (ICollectionView) on our CheckableObservableCollection.

<ItemsControl Margin="4" x:Name="items" ItemsSource="{Binding Path=Items.CheckedItems}" DisplayMemberPath="Value" />

And here's the result - the list on the right is kept up to date based on selections on the left. I must stress again that this is my first implementation based on my first train of thought for tackling this (with a mindset of writing as little code as possible, but making as much of what I do write - 'reusable and generic').

Bound CheckListbox

Source code

Usual disclaimers apply, your own mileage may vary, you may lose your house if you stop paying for it. Get the code here -> Download Source Code (10KB).

Tags: MVVM WPF

 
Josh Post By Josh Twist
12:58 PM
26 May 2009

» Next Post: If you can't beat XAML, improve it
« Previous Post: My new favourite keyboard shortcut in Visual Studio

Comments are closed for this post.

Posted by Aicha @ 30 Dec 2010 8:40 AM
Thanks fot the post, have a nice day

Posted by Shakti Shekhar @ 20 Oct 2011 7:34 PM

Can we use above approach if we want to load the default selection of checkbox during load?

Posted by nyland @ 29 Jan 2012 5:42 PM
Brilliant! Thank you.

© 2005 - 2014 Josh Twist - All Rights Reserved.