On my journey to learning MVVM I've established some basic understanding of WPF and the ViewModel pattern. I'm using the following abstraction when providing a list and am interested in a single selected item.
public ObservableCollection<OrderViewModel> Orders { get; private set; }
public ICollectionView OrdersView
{
get
{
if( _ordersView == null )
_ordersView = CollectionViewSource.GetDefaultView( Orders );
return _ordersView;
}
}
private ICollectionView _ordersView;
public OrderViewModel CurrentOrder
{
get { return OrdersView.CurrentItem as OrderViewModel; }
set { OrdersView.MoveCurrentTo( value ); }
}
I can then bind the OrdersView along with supporting sorting and filtering to a list in WPF:
<ListView ItemsSource="{Binding Path=OrdersView}"
IsSynchronizedWithCurrentItem="True">
This works really well for single selection views. But I'd like to also support multiple selections in the view and have the model bind to the list of selected items.
How would I bind the ListView.SelectedItems to a backer property on the ViewModel?
Add an IsSelected
property to your child ViewModel (OrderViewModel
in your case):
public bool IsSelected { get; set; }
Bind the selected property on the container to this (for ListBox in this case):
<ListBox.ItemContainerStyle>
<Style TargetType="{x:Type ListBoxItem}">
<Setter Property="IsSelected" Value="{Binding Mode=TwoWay, Path=IsSelected}"/>
</Style>
</ListBox.ItemContainerStyle>
IsSelected
is updated to match the corresponding field on the container.
You can get the selected children in the view model by doing the following:
public IEnumerable<OrderViewModel> SelectedOrders
{
get { return Orders.Where(o => o.IsSelected); }
}
I can assure you: SelectedItems
is indeed bindable as a XAML CommandParameter
There is a simple solution to this common issue; to make it work you must follow ALL the following rules:
Following Ed Ball's suggestion, on your XAML command databinding, define the CommandParameter
attribute BEFORE the Command
attribute. This a very time-consuming bug.
Make sure your ICommand
's CanExecute
and Execute
methods have a parameter of type object
. This way you can prevent silenced cast exceptions that occur whenever the databinding's CommandParameter
type does not match your Command
method's parameter type:
private bool OnDeleteSelectedItemsCanExecute(object SelectedItems)
{
// Your code goes here
}
private bool OnDeleteSelectedItemsExecute(object SelectedItems)
{
// Your code goes here
}
For example, you can either send a ListView
/ListBox
's SelectedItems
property to your ICommand
methods or the ListView
/ListBox
itself. Great, isn't it?
I hope this prevents someone from spending the huge amount of time I did to figure out how to receive SelectedItems
as a CanExecute
parameter.
One can try creating an attached property.
Doing so will save one from adding the IsSelected
property for each and every list you bind. I have done it for ListBox
, but it can be modified for use a in a list view.
<ListBox SelectionMode="Multiple"
local:ListBoxMultipleSelection.SelectedItems="{Binding SelectedItems}" >
More info: WPF – Binding ListBox SelectedItems – Attached Property VS Style .
If you're using MVVM-LIGHT you can use this pattern:
https://galasoft.ch/posts/2010/05/handling-datagrid-selecteditems-in-an-mvvm-friendly-manner
Not especially elegant but looks like it should be reliable at least
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With