Handling DataGrid.SelectedItems in an MVVM-friendly manner

An interesting question from one of the MVVM Light users today:

Is there an MVVM-friendly way to get a DataGrid’s SelectedItems into the ViewModel?

The issue there is as old as the DataGrid (that’s not very old but still): SelectedItem (singular) is a DependencyProperty and can be databound to a property in the ViewModel. SelectedItems (plural) is not a DependencyProperty.

Thankfully the answer is very simple: Use EventToCommand to call a Command in the ViewModel, and pass the SelectedItems collection as parameter. For example, if the command in the ViewModel is declared as follows:

Read the rest of this entry »

Print | posted on Wednesday, May 19, 2010 7:17 AM

Feedback

# re: Handling DataGrid.SelectedItems in an MVVM-friendly manner

left by Odi Kosmatos at 5/19/2010 9:07 AM Gravatar
So how is this going to work with a datagrid built for asynchronous data virtualization like Xceed's upcoming Silverlight one, which has no "SelectedItems" but rather a "BeginGetSelectedItems" and "EndGetSelectedItems" asynchronous methods?

DOH!

http://xceed.com/CS/blogs/dontpanic/archive/2010/05/07/why-there-won-t-be-a-selecteditems-property-and-why-you-will-like-it.aspx

# re: Handling DataGrid.SelectedItems in an MVVM-friendly manner

left by Laurent at 5/19/2010 9:45 AM Gravatar
Hey Odi,

Interesting point. (incidentally, I am not sure that DOH and *sigh* are super appropriate when talking to potential customers... but then again I don't know anything about marketing ;)

I am wondering if a user selecting items that are not on the screen is really a more frequent use case than a user selecting items that are on the screen. In fact I am not sure I see in which use case the user will select items that are virtualized. Do you have a "for instance"?

Cheers,
Laurent

# re: Handling DataGrid.SelectedItems in an MVVM-friendly manner

left by Calabonga at 5/19/2010 10:09 AM Gravatar
Nice post, thanks. Very timely

# re: Handling DataGrid.SelectedItems in an MVVM-friendly manner

left by Nikhil Kothari at 5/19/2010 10:24 AM Gravatar
Thoughts on something like this:
<DataGrid fx:Commands.MultiSelectionCommand="{Command MethodOnVM}" />

or for single selection:
<DataGrid fx:Commands.SelectionCommand="{Command MethodOnVM}" />

This assumes a {Command} markup extension, and in the interim while that isn't possible:

<DataGrid>
<fx:Commands.MultiSelectionCommand>
<fx:Command Method="MethodOnVM" />
</fx:Commands.MultiSelectionCommand>
</DataGrid>

The command knows to use the selected items or selected item when invoking the method on the view model. I guess I prefer this sort of approach as it minimizes my xaml.

I've been thinking of various commands as they relate to common controls/primitives. Essentially a command represents the class of events that trigger work in the view model.

Thoughts?

# re: Handling DataGrid.SelectedItems in an MVVM-friendly manner

left by Laurent at 5/19/2010 10:39 AM Gravatar
Hey Nikhil,

In fact you could do the same with an attached behavior like here:
http://weblogs.asp.net/alexeyzakharov/archive/2009/06/06/silverlight-tips-amp-tricks-make-silverlight-datagrid-be-more-mvvm-friendly.aspx

What I wanted to do here was give an alternative using components that MVVM Light users know. Granted, the EventToCommand syntax is a bit too convoluted (though with Blend it's a non issue, which is what I used anyway).

As usual, multiple ways to kill a bird I guess. As I told you before, it just would be nice if we could have MyEventRaised="{Binding MethodName}" in the framework. The syntax you mention here would be a good step in that direction I guess ;)

Cheers,
Laurent

# re: Handling DataGrid.SelectedItems in an MVVM-friendly manner

left by Marc Laroche at 5/20/2010 2:26 AM Gravatar
Hi Laurent,

To answer your question on how a virtualized selection could be handy in a DataGrid component, here are the key factors that drove our design:

- When doing range selection, nothing prevents the end-user from selecting across thousands and thousands of record (accidentally, or on purpose). By virtualizing the list of selected items, the end-user workflow is not interrupted by the population of a list of selected items. The cost of populating the list of selected items is only applied when there is an actual need for said items.

- When selecting items in a virtualized data source, changes to the end-users configuration (grouping, sorting, filtering, ... ) will affect how the selected items are displayed (the spread of items in the new configuration cannot be predicted). By virtualizing the list of selected items, we have no need to keep references to items that are possibly out-of-view, simplifying paging management and possibly reducing memory usage (in cases where there are large amount of selected items).

- This enables advanced selection scenarios for the application developers, such as programmatic range selection based on specific application logic ( e.g. Select all Orders from Employee1 for the month of March 2008 ), without having to manually load all the items first ( component's UI reacts instantaneously, and retrieval cost is only required when selected items are really required ).

Let me know what you think of these scenarios.

Cheers,

Marc.

# re: Handling DataGrid.SelectedItems in an MVVM-friendly manner

left by Mike Lockyer at 5/20/2010 6:17 AM Gravatar
Hi Laurent,

I have been using MVVM Light since it first came out and I have been binding to the ItemSource in my DataGrids. (at least for SL4)

Is this wrong ?

Has not been a problem so far but you have me worried now

Thanks

Mike

# re: Handling DataGrid.SelectedItems in an MVVM-friendly manner

left by Laurent at 5/20/2010 7:49 AM Gravatar
Hi Mike,

Not wrong at all. Notice that I am handling a different problem here, which is getting the SelectedItems down in the ViewModel. The ItemsSource of the DataGrid is set to a collection, but then you want to know which items the user clicked.

All is well :)

Cheers,
Laurent

# re: Handling DataGrid.SelectedItems in an MVVM-friendly manner

left by Mike Lockyer at 5/20/2010 6:19 PM Gravatar
Laurent

Many thanks for the response

Yes - you are correct

I realised (when walking my dog last night) that I had misread the article

My apologies - I shall be more careful before I post again


Best wishes

Mike

# re: Handling DataGrid.SelectedItems in an MVVM-friendly manner

left by jerry qian at 5/23/2010 9:09 PM Gravatar
there is a IsInDesingnMode in the datagrid but I can't find it in the model ,why ,tank you very much

# re: Handling DataGrid.SelectedItems in an MVVM-friendly manner

left by simon at 7/24/2010 4:51 PM Gravatar
what about going in reverse?

ViewModel > View

this is one way only. i don't care about setting multiple values right now, but i need to clear the selection from the model

# re: Handling DataGrid.SelectedItems in an MVVM-friendly manner

left by Laurent at 7/26/2010 4:01 AM Gravatar
Hey Simon,

When I need to go from the VM to the View (for example to show a dialog, to start/stop an animation, or to clear the selection ;)), I use one of two strategies:

1) Send a message using the Messenger class.
or
2) Create a service interface for this (for example IListSelectionService) and have the MainPage implement it. For example, in this case, it could have a single method named "ClearSelection". Depending on the complexity of the application, you can have the MainPage implement this interface, or have a specialized class do this. Define a property if type IListSelectionService. Then, in the MainPage constructor, set this property on the viewmodel (for example (DataContext as MainViewModel).ListSelectionService = this;

The second way is quite handy though a little less decoupled than the Messenger one. However, I find that it suits most of my needs. Depending on your sitation, you can even define test services that are used during unit testing of the viewmodel.

HTH,
Laurent

# re: Handling DataGrid.SelectedItems in an MVVM-friendly manner

left by Greg Foote at 7/31/2010 9:52 AM Gravatar
Laurent,

Awesome framework !!!!! Thanks so much for your efforts !!

I have recently come upon a strange phenmoenon when trying to implement this example in my own app... The items collection passed in is ALWAYS 0 length no matter what, event though there are clearly multiple items selected in the listbox.

So I created a event handler in XAML to examine what the listbox said about its own selectedItems collection... As soon as I did this the collection passed into the relay command was correct !??!

Take the event handler out and the collection is 0 length again ?? The selectionChanged event fires and the eventtocommand executes but the collection is simply wrong ?! Wierd

Here is the XAML


<ListBox Margin="226,8,0,30"
x:Name="PhotosLB"
ItemsSource="{Binding FlickRPhotos}"
SelectionChanged="PhotosLB_SelectionChanged"
Style="{StaticResource ListBoxStylePhotos}"
ItemContainerStyle="{StaticResource ListBoxItemStyleFlickRPhotosBrowse}"
ItemsPanel="{StaticResource ItemsPanelTemplateWrapPanel}"
ScrollViewer.HorizontalScrollBarVisibility="Disabled"
HorizontalAlignment="Left"
Width="516"
ItemTemplate="{StaticResource DataTemplateFlickRPhotosBrowse}"
Background="{x:Null}"
SelectionMode="Multiple"
Grid.Row="1">
<i:Interaction.Triggers>
<i:EventTrigger EventName="SelectionChanged">
<GalaSoft_MvvmLight_Command:EventToCommand Command="{Binding SelectionChangedCommand}"
CommandParameter="{Binding SelectedItems, ElementName=PhotosLB}" />
</i:EventTrigger>
</i:Interaction.Triggers>
</ListBox>



And the ViewModel Code

SelectionChangedCommand = new RelayCommand<IList>(items =>
{
if (items == null)
{
SelectedPhotosCount = 0;
return;
}

SelectedPhotosCount = items.Count;
});


public RelayCommand<IList> SelectionChangedCommand { get; private set; }


Have you ever heard of this ?

I am using observableCollections for my VM properties that bind to the listboxes... Could that have something to do with it ?

# re: Handling DataGrid.SelectedItems in an MVVM-friendly manner

left by Laurent Bugnion at 8/2/2010 12:25 AM Gravatar
Hey Greg,

Weird indeed. I spent a little time thinking about this and cannot find an explanation off the top of my head.

I will test here as soon as I have a little more free time.

Cheers,
Laurent

# re: Handling DataGrid.SelectedItems in an MVVM-friendly manner

left by Tony at 8/6/2010 4:51 PM Gravatar
I have the same issue. same code does not work with a listbox (SelectedItems)... any ideas... thank you.

# re: Handling DataGrid.SelectedItems in an MVVM-friendly manner

left by ZhengokUSA at 8/27/2010 2:01 AM Gravatar
Maybe this will help.
<extras:EventToCommand Command="{Binding TestButtonCommand}" PassEventArgsToCommand="True"/>

By seting PassEventArgsToCommand to true, you basicly
can get the same eventArgs as a command parameter back to you ViewModel, then you can get your select listed from the eventArgs just like you do in code behind. I have not tested this on the listbox yet, but It does work for other cases. Hope it will help.

Laurent Bugnion as you said earlier, If you want to go from VM to V, you will use Messenger class or a Interface. Would you point me to the example of using Interface?

# re: Handling DataGrid.SelectedItems in an MVVM-friendly manner

left by Voss at 9/2/2010 1:08 AM Gravatar
Any update on the ListBox selecteditems count=0 always?

thanks,

# re: Handling DataGrid.SelectedItems in an MVVM-friendly manner

left by Mike Lavery at 9/8/2010 4:57 AM Gravatar
Hello,
I am trying to use this example as a guide for my application. I am using the latest MVVM Light Tool kit for WPF4. I am getting the following error:

"Error 1 A value of type 'EventToCommand' cannot be added to a collection or dictionary of type 'TriggerActionCollection'. "

Below is my Xaml. If I figure out what's wrong I will post the solution.

<DataGrid x:Name="musicFileGrid" ItemsSource="{Binding AvailableMusicFiles}" >
<i:Interaction.Triggers>
<i:EventTrigger EventName="SelectionChanged">
<cmd:EventToCommand Command="{Binding SelectionChangedCommand, Mode=TwoWay}" CommandParameter="{Binding SelectedItems, ElementName=musicFileGrid}" />
</i:EventTrigger>
</i:Interaction.Triggers>
<DataGrid.Columns>
<DataGridTextColumn Header="Artist" Binding ="{Binding Path= FirstPerformer}" IsReadOnly="True" Width="Auto"/>

<DataGridTextColumn ScrollViewer.HorizontalScrollBarVisibility="Visible" x:Name="TitleCol"
Binding="{Binding Path= Title}" Header="Title/Phrase" IsReadOnly="True" Width="Auto"/>


</DataGrid.Columns>
</DataGrid>

# re: Handling DataGrid.SelectedItems in an MVVM-friendly manner

left by Mike Lavery at 9/9/2010 2:29 AM Gravatar
Well, came down to a little CM issue. I wasn't referencing the most up to date System.Windows.Interactivity.dll.

# re: Handling DataGrid.SelectedItems in an MVVM-friendly manner

left by Bavo Ketels at 9/12/2010 9:45 PM Gravatar
Hello Laurent

I think I have a solution for the ListBox problem.

I add three items in code to the SelectedItems collection. They are selected in the ListBox.

When I select another item, the EventToCommand gets fired up, but only the previously three selected items are passed to the ViewModel. The fourth item is not passed in the items IList.

But when I add a SelectionChanged event to the ListBox and add some code in there involving the SelectedItems, it mysteriously works.

When I remove the code in the selectionchanged event, it doesn't work anymore. Appaerently there seems to be a bug in the ListBox SelelectedItems property.

XAML:
<ListBox x:Name="PatientsListBox" ItemsSource="{Binding DataContext.Patients, ElementName=LayoutRoot}" SelectionChanged="PatientsListBox_SelectionChanged" Style="{StaticResource CheckBoxList}" Grid.Row="1" Grid.Column="1" DisplayMemberPath="Name">
<interactivity:Interaction.Triggers>
<interactivity:EventTrigger EventName="SelectionChanged" x:Name="PatientsListBoxSelectionChangedEventTrigger">
<mvvm:EventToCommand Command="{Binding DataContext.PatientsListBoxSelectionChangedCommand, ElementName=LayoutRoot, Mode=OneWay}"
CommandParameter="{Binding SelectedItems, ElementName=PatientsListBox}"/>
</interactivity:EventTrigger>
</interactivity:Interaction.Triggers>
</ListBox>

Code View:
private void PatientsListBox_SelectionChanged(object sender, SelectionChangedEventArgs e)
{
//This code does not do anything, but magically solves the issue
int patientsCount = PatientsListBox.SelectedItems.Count;
}

# re: Handling DataGrid.SelectedItems in an MVVM-friendly manner

left by Laurent at 9/12/2010 10:09 PM Gravatar
Interesting... I wish I had more time to investigate, but I made good note of that and will see if someone at MSFT can take a look.

Thanks!
Cheers,
Laurent

# re: Handling DataGrid.SelectedItems in an MVVM-friendly manner

left by mark baer at 9/14/2010 11:58 AM Gravatar
Very Nice, Laurent. This helped me out today as I was trying to pass a Selected Item from a Listbox to call another Service. Can't wait for your SL4 book to come out in November. Thanks again.

# re: Handling DataGrid.SelectedItems in an MVVM-friendly manner

left by Atif at 9/23/2010 8:18 AM Gravatar
Anyone knows how to use MVVM toolkit for vb.net

# re: Handling DataGrid.SelectedItems in an MVVM-friendly manner

left by NoiZe at 10/18/2010 6:03 PM Gravatar
@ Bavo Keltes:
Actually this works without the additional code at my side.

@ Atif:
If I'm right u can use the compiled DLL's of the Toolkit without probs in VB.NET. Just the code to make use of it would look a bit different.
U can try to convert some samples/code snippets with one of the free online C# to VB Converter.
But to be true I would think about switching to C# cause most of the advanced stuff u will find in web is written in C#. Thats why i switched too.
First C# looks a bit wired, but if u get the changed syntax its a lot better, cleaner and faster then VB.
And it's pretty easy to learn, if u know VB already. I didn't need much time and I'm by no way a professional programmer.
That's why I love to use this awesome toolkit. It's pretty easy to make use of it, compared to my other approaches to understand MVVM ;)
Big thx @ Laurent.

# Also ListView.SelectedItems in an MVVM-friendly manner

left by Tridy at 10/27/2010 8:55 AM Gravatar
It applies/works exactly to ListView/GridView SelectedItems.

# re: Handling DataGrid.SelectedItems in an MVVM-friendly manner

left by David Adams at 11/26/2010 11:49 PM Gravatar
Is there any MVVM friendly way to pass which column was clicked in a DataGrid? I would like to take different actions depending on which column was clicked. I'm having a heck of a time withi this one.

Great work Laurent. This has been a godsend with my mvvm crash course.

# re: Handling DataGrid.SelectedItems in an MVVM-friendly manner

left by Vina at 1/25/2011 11:17 PM Gravatar
I tried to use it in my wpf application. But it is not able to find Interaction.Triggers. I included expression blend's Interaction dll (latest). When I checked the assembly with the object browser, I cannot find Interaction.Triggers. So what else do I need to add?

Is there any other assembly that I need to add.

Thanks for your help and a great article.
Vina.

# re: Handling DataGrid.SelectedItems in an MVVM-friendly manner

left by Miro at 2/5/2011 5:35 PM Gravatar
Thanks for the great post. I've bumping my head for over a week now on something similar for WP7. Also, thanks to Bavo for his ListBox solution. It really works.

# re: Handling DataGrid.SelectedItems in an MVVM-friendly manner

left by Dennis Vroegop at 3/30/2011 9:07 AM Gravatar
I noticed the same thing. The moment I added the SelectionChanged eventhandler to the code-behind (shiver...) the IList passed to the RelayCommand somehow started to contain the data I needed.
Must be a bug somewhere, but I can't figure out why it is.
Ah well, it works now (albeit a bit ugly).

# re: Handling DataGrid.SelectedItems in an MVVM-friendly manner

left by Oskar at 4/13/2011 3:09 PM Gravatar
@Simon

To clear selection you can use a TwoWay binding against the SelectedItem property and set it to null in your ViewModel. This works for both single and extended SelectionMode.

# re: Handling DataGrid.SelectedItems in an MVVM-friendly manner

left by Cedric Arnould at 5/18/2012 5:04 PM Gravatar
Hi,

Thank you for your article, it was helpfull, but I have a problem.

I have a listbox of products inside a listbox of categories.
When I click on a product nothing happens:

<ListBox Margin="12,0,-12,0" ItemsSource="{Binding Categories}">
<ListBox.ItemTemplate>
<DataTemplate>
<StackPanel Style="{StaticResource StackPanelCategoryStyle}">
<TextBlock Text="{Binding Name, Converter={StaticResource UpperCaseConverter}}" Style="{StaticResource TextBlockCategoryStyle}"/>
<ListBox ItemsSource="{Binding SubCategories}">
<ListBox.ItemTemplate>
<DataTemplate>
<StackPanel>
<ListBox Name="ProductsListBox"
ItemsSource="{Binding Products}">
<ListBox.ItemTemplate>
<DataTemplate>
<StackPanel Orientation="Horizontal" Style="{StaticResource StackPanelProductStyle}">
<Grid>
<Rectangle Style="{StaticResource RectangleProductStyle}"/>
<Image Source="{Binding Image.Uri}" Style="{StaticResource ImageProductStyle}" />
</Grid>
<StackPanel>
<TextBlock Text="{Binding Name}" Style="{StaticResource TextBlockProductStyle}"/>
<TextBlock Text="{Binding Franchise}" Style="{StaticResource TextBlockFranchiseStyle}"/>
</StackPanel>
</StackPanel>
</DataTemplate>
</ListBox.ItemTemplate>
<i:Interaction.Triggers>
<i:EventTrigger EventName="Tap">
<MvvmLight:EventToCommand x:Name="SelectionCommand"
Command="{Binding NavigateToProductDetail, Mode=OneWay}"
CommandParameter="{Binding SelectedItem, ElementName=ProductsListBox}"/>
</i:EventTrigger>
</i:Interaction.Triggers>
</ListBox>
</StackPanel>
</DataTemplate>
</ListBox.ItemTemplate>
</ListBox>
</StackPanel>
</DataTemplate>
</ListBox.ItemTemplate>
</ListBox>

Problem comes from here, i think, "ElementName=ProductsListBox", ProductsListBox is not found, do you know another way to make it work?
Comments have been closed on this topic.