# ListView MultiSelect, MVVM and RoutedCommands

As far as I know (and I don't claim to know much about this!), direct binding to ListView.SelectedItems isn't possible in WPF. I've seen work-arounds involving code-behind which I'm not too crazy about, especially since I'm having a hard time with getting a DelegateCommand to work, and decided to use RoutedCommands. For the following XAML command binding...

<UserControl.CommandBindings>
</UserControl.CommandBindings>


...I have the following code-behind:

public static readonly RoutedCommand AddSelectionCommand = new RoutedCommand();

private void CanExecuteAddSelectionCommand(object sender, CanExecuteRoutedEventArgs e)
{
if (Commands != null)
{
Commands.SelectedClients = SearchResultsList.SelectedItems.Cast<ClientViewModel>().ToList();
}
e.Handled = true;
}

private void ExecuteAddSelectionCommand(object sender, ExecutedRoutedEventArgs e)
{
Commands.SelectedClients = SearchResultsList.SelectedItems.Cast<ClientViewModel>().ToList();
e.Handled = true;
}


Where Commands is a get-only private property that returns an interface implemented by the ViewModel, which defines all the CanExecuteXXXX and ExecuteXXXX methods.

Here's how I have it:

private IViewModel _viewModel;
private IClientsSearchSectionCommands Commands { get { return _viewModel as IClientsSearchSectionCommands; } }

public ClientSearchSection()
{
InitializeComponent();
DataContextChanged += ClientsSearchSection_DataContextChanged;
}

void ClientSearchSection_DataContextChanged(object sender, DependencyPropertyChangedEventArgs e)
{
_viewModel = e.NewValue as IViewModel;
}


This allows me to implement the command code in the ViewModel, for example:

public bool CanExecuteAddSelectionCommand()
{
return SelectedClients.Count > 0;
}

{
// whatever needs to happen here, I can access my model as needed
}


The trick that allows the SelectedItems to work is with this property, exposed by the IClientsSearchSectionCommands interface implemented by the ViewModel:

public IList<ClientViewModel> SelectedClients { get; set; }


...and the fact that I'm setting them in the commands' CanExecute and Executed handlers.

This works beautifully... but is it weird in any way? [How] could it be done better?

I don't want to dive into behaviors at this point, firstly because I have no clue about them, second, because I'm dragging a more junior developper into WPF & MVVM, coming from WinForms and inline-SQL-in-the-form's-code-behind, so I'd like to know if this code is easy enough to follow...

Your approach looks fine to me. Except i would probably use prefix-casting, to get cast exceptions straigt away if something goes wrong, instead of using as.

This can be achieved without modifying code-behind tho. You can bind container's IsSelected property to appropriate item's viewmodel property.

<ListView.ItemContainerStyle>
<Style TargetType="ListViewItem" >
<Setter Property="IsSelected" Value="{Binding IsSelected}"/>
</Style>
</ListView.ItemContainerStyle>


Then you can access selected items in your viewmodel by using:

protected IEnumerable<Item> SelectedItems { get { return Items.Where(x => x.IsSelected); } }

• Wow this is amazing - turns out I have those "item view models" implementing a ISelectable interface so they all have a IsSelected property, and with this XAML I can perform that binding at item level, only the designer blue-squigles {Binding IsSelected} saying it can't resolve that property.. but it works! Is there a way to make the ListView.ItemContainerStyle block understand that the view model it should validate at design-time against is that of ListView.ItemsSource? (like ListView.ItemTemplate does) ...'cause for a novice dev it's not obvious that it works at run-time! – Mathieu Guindon Sep 30 '13 at 17:21
• @retailcoder, yes, you should either set an actual DataContext in Xaml or specify a design-time DataContext (see stackoverflow.com/questions/13863099/… for example). Normally it doesn't worth the effort tho. – Nikita B Oct 1 '13 at 6:14
• I meant this:stackoverflow.com/questions/19100882/… – Mathieu Guindon Oct 1 '13 at 13:27