Brian Genisio's House of Bilz

  Home  |   Contact  |   Syndication    |   Login
  62 Posts | 0 Stories | 118 Comments | 0 Trackbacks

News

Locations of visitors to this page

Archives

Post Categories

Who am I?

Shout it kick it on DotNetKicks.com

Continuing in my series of “Adventures in MVVM”, I want to talk about a few different approaches to working with List Boxes with the MVVM pattern.  What I am writing here is generally true of all controls that derive from Selector, including ListBox and ComboBox.  This example was developed in Silverlight, but the same concepts also apply to WPF.

The Problem

You have a list box in your view, and you want your ViewModel to do something when an item in the ListBox is selected. You want to do this without any code-behind, using the MVVM pattern.  There are three methods that I have come up with, and I will outline them here.  In this post, I will be using a VERY simple data class in my ListBox called Person with a First and Last name.  It is so simple, in fact, that I have chosen not to include the source for this class.

Method 1: Quick and Dirty SelectedItem binding

This method sets up a SelectedPerson property in the view model and does something when the property is changed. 

public class ViewModel : INotifyPropertyChanged
{
    public ObservableCollection<Person> People { get; private set; }

    private Person _selectedPerson = null;
    public Person SelectedPerson
    {
        get { return _selectedPerson; }
        set
        {
            _selectedPerson = value;
            OnPropertyChanged("SelectedPerson");    
            DoSomething(value);
        }
    }
    // ... rest of ViewModel
}
<ListBox ItemsSource="{Binding People}" SelectedItem="{Binding SelectedPerson, Mode=TwoWay}" />

Pros: This method is quick and simple to get going
Cons: You are introducing side effects in your property code.  If you are OK with this, then read no further.  If this bothers you the way it does for me, then lets look at our next option.

Method 2: Button Command

There are plenty of commanding libraries out there to choose from.  I will take advantage of the Prism commanding system (Microsoft.Practices.Composite.Presentation.Commands).  They have implemented bindable commands for ButtonBase.  The only problem: ListBox is not a ButtonBase.  To get around this, replace the ItemTemplate with a Button that has a template of textblock.

public class ViewModel : INotifyPropertyChanged
{
    public ViewModel()
    {
        PersonSelected = new DelegateCommand<Person>(DoSomething);
        // ... rest of constructor
    }

    public ObservableCollection<Person> People { get; private set; }
    public ICommand PersonSelected { get; private set; }

    // ... rest of ViewModel    
}
<ListBox ItemsSource="{Binding People}">
    <ListBox.ItemTemplate>
        <DataTemplate>
            <Button Commands:Click.Command="{Binding PersonSelected, Source={StaticResource ViewModel}}" Commands:Click.CommandParameter="{Binding}" >
                <Button.Template>
                    <ControlTemplate>
                        <TextBlock Text="{Binding}" />
                    </ControlTemplate>
                </Button.Template>
            </Button>
        </DataTemplate>
    </ListBox.ItemTemplate>
</ListBox>

Pros: The ViewModel is much more simple with no side effects. 
Cons: The XAML is ugly as sin.  It also changes the behavior of the ListBox in a subtle way.  Every time you select an item, the command fires, not just when it changes.  This is my LEAST favorite approach.  We can do better

Method 3: Bind Commands to the ListBox

The final mechanism is my favorite.  Even though Prism doesn’t give us the ability to bind commands to ListBoxes, we can extend their attached behavior infrastructure such that all ListBoxes and ComboBoxes (or anything that derives from Selector) can take advantage of it.  The ViewModel doesn’t change from “Method 2”, but the XAML does:

<ListBox ItemsSource="{Binding People}" Commands:Selected.Command="{Binding PersonSelected}" />

Pros: Best of both worlds.  Simple ViewModel.  Simple XAML
Cons: You have to write some extensions to the Prism infrastructure.  This code is boilerplate.  I have written some generics that can reduce the boilerplate code somewhat, but not completely, due to the static properties.

The Winner Is….

I like “Method 3” the best.  With a bit of some infrastructure code that you can tuck away, you get to bind the selected items to a command in any case.  It plays well, and it is easy to follow.

But wait… you want the Prism extensions?  Here they are:

public class SelectorSelectedCommandBehavior : CommandBehaviorBase<Selector>
{
    public SelectorSelectedCommandBehavior(Selector selectableObject)
        : base(selectableObject)
    {
        selectableObject.SelectionChanged += OnSelectionChanged;
    }

    void OnSelectionChanged(object sender, SelectionChangedEventArgs e)
    {
        CommandParameter = TargetObject.SelectedItem;
        ExecuteCommand();
    }
}
public static class Selected
{
    private static readonly DependencyProperty SelectedCommandBehaviorProperty = DependencyProperty.RegisterAttached(
        "SelectedCommandBehavior",
        typeof(SelectorSelectedCommandBehavior),
        typeof(Selected),
        null);

    public static readonly DependencyProperty CommandProperty = DependencyProperty.RegisterAttached(
        "Command",
        typeof(ICommand),
        typeof(Selected),
        new PropertyMetadata(OnSetCommandCallback));

    [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Design", "CA1011:ConsiderPassingBaseTypesAsParameters", Justification = "Only works for selector")]
    public static void SetCommand(Selector selector, ICommand command)
    {
        selector.SetValue(CommandProperty, command);
    }

    [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Design", "CA1011:ConsiderPassingBaseTypesAsParameters", Justification = "Only works for selector")]
    public static ICommand GetCommand(Selector selector)
    {
        return selector.GetValue(CommandProperty) as ICommand;
    }

    private static void OnSetCommandCallback(DependencyObject dependencyObject, DependencyPropertyChangedEventArgs e)
    {
        var selector = dependencyObject as Selector;
        if (selector != null)
        {
            GetOrCreateBehavior(selector).Command = e.NewValue as ICommand;
        }
    }

    private static SelectorSelectedCommandBehavior GetOrCreateBehavior(Selector selector)
    {
        var behavior = selector.GetValue(SelectedCommandBehaviorProperty) as SelectorSelectedCommandBehavior;
        if (behavior == null)
        {
            behavior = new SelectorSelectedCommandBehavior(selector);
            selector.SetValue(SelectedCommandBehaviorProperty, behavior);
        }

        return behavior;
    }
}
posted on Friday, May 29, 2009 11:27 PM

Feedback

# re: Adventures in MVVM – Commanding with List Boxes 5/30/2009 6:13 AM Laurent Kempé
Why not simply using CollectionViewSource?

Items = CollectionViewSource.GetDefaultView(BacklogItems);
Items.CurrentChanged += OnCurrentBacklogItemChanged;

# re: Adventures in MVVM – Commanding with List Boxes 5/30/2009 8:21 AM Brian Genisio
Laurent,

I think your question is more about MVVM architecture philosophy. In my opinion, CollectionViewSource is is purely a View construct, thus the ViewModel never knows anything about the CollectionViewSource.

As far as I am concerned, if a class derives from DependencyObject, then it lives in the View, not the ViewModel.

Because of this, you can't use the code you suggested.

# re: Adventures in MVVM – Commanding with List Boxes 6/24/2009 10:16 PM Justin Angel
I'm with you Brian :)
SelectionChanged always looked like a command to me. Even if CollectionViewSource solves that issue in another way.

But Laurent does have a point, your ViewModel should give the View data in the form most usable for it. If it's a CollectionViewSource, than it's a CollectionViewSource.

-- Justin

# re: Adventures in MVVM – Commanding with List Boxes 8/27/2009 8:17 PM George
Hi Brian,

One question, is it possible to use one of those three solutions to a ListView control?

# re: Adventures in MVVM – Commanding with List Boxes 9/16/2009 8:21 AM Abdul
How can we get Multiple selected items(Multi select list box) in view model using method 1 or method 3

Thanks


# re: Adventures in MVVM – Commanding with List Boxes 9/16/2009 8:41 AM Brian Genisio
@Abdul: This is a problem that the SelectedItems property of ListBox is not a DependencyProperty, so you can't bind directly in XAML. I have, however, figured out how to do it using method 3 :)

I answered someone's question on StackOverflow with the solution: http://stackoverflow.com/questions/1297643/sync-selecteditems-in-a-muliselect-listbox-with-a-collection-in-viewmodel/1299544#1299544

I plan to formalize it in a blog post soon.

# re: Adventures in MVVM – Commanding with List Boxes 10/23/2009 12:34 PM El Guapo
"Because of this, you can't use the code you suggested. "

OH really? Cause I use CollectionViewSource all the time in the ViewModel and it works great. It achieves your goal of simple clean XAML and code. It even achieves your dubious goal of "zero tolerance for code-behind".

I find your statement that I cannot use something based on DependencyObject in my ViewModel to be frustrating. This sounds like zealotry to me.

Compare my solution to your "solution" #2, which is so tortured and bizarre that I would dread working on a codebase filled with such hacks just so we in our ivory tower could look down from on high and brag about our pure MVVM achitecture. Meanwhile your devs doing maintenance on your spaghetti xaml are looking for other jobs.

Sure you have pure MVVM - what a victory for you... a pyrrhic victory...


# re: Adventures in MVVM – Commanding with List Boxes 10/23/2009 12:53 PM Brian Genisio
El Guapo,

Actually, I failed to articulate in my statement about not being able to use something that derives from DependencyObject in a ViewModel. My objection to it is much more grounded in practicalities.

In my case, I use NUnit to write my unit tests which do not run in the browser. The Silverlight engine never gets instantiated. Anything that derives from DependencyObject fails in the base constructor with an exception. I have found no way around this and I have not seen a better unit testing strategy that fits all of my needs. This is not a problem in WPF.

Anyways, I hear your point. Pure MVVM is really an exercise. In my real-world, large-scale application, I make heavy use of MVVM. It is in no way pure.

Lately, I have been striving for more expressive code with less plumbing. I think any code that achieves this AND is testable is a win in my book.

Finally, in fairness to me, I did refer to the XAML in "solution #2" as being "ugly as sin". I continued to say that we can do better than that. I am trying to document my thought process. That was a solution in the thought process which I chose to dismiss.


# re: Adventures in MVVM – Commanding with List Boxes 10/23/2009 1:00 PM El Guapo
Sorry, I was having a really bad day. Your points are well taken. Sorry for going on a rant there. When I read it again I realized I was out of line.


# re: Adventures in MVVM – Commanding with List Boxes 10/23/2009 1:07 PM Brian Genisio
El Guapo,

At the same time, I was extremely inarticulate by saying "Because of this, you can't use the code you suggested."

I deserved some crap for that... and I owe it to anyone reading this post to elaborate more. Thanks for getting me to do that.

# re: Adventures in MVVM – Commanding with List Boxes 11/3/2009 11:50 PM Flexman
I've used your behaviour in an example of how to do commanding with the listbox/combobox in Silverlight and Prism.

http://www.g7x.com/Blog/post/2009/11/04/Combobox-Listbox-commanding-with-Silverlight-Prism.aspx

Post A Comment
Title:
Name:
Email:
Website:
Comment:
Verification: