Sync Multi Select Listbox with ViewModel
Today one of my colleagues at Infusion asked me how he could sync up the selection of a ListBox with his ViewModel. WPF supports already single mode selection via the ICollectionView yet when it comes to MultiSelect there is no out of the box support in WPF.
Attached Properties to the rescue …..
I quickly when in my VS and wrote up a simple Attached property that hooks to the SelectionChanged event of the ListBox and populates a list of selected items to the ViewModel.
This is how the attached property looks like
1: #region SelectedItems
2:
3: /// <summary>
4: /// SelectedItems Attached Dependency Property
5: /// </summary>
6: public static readonly DependencyProperty SelectedItemsProperty =
7: DependencyProperty.RegisterAttached("SelectedItems", typeof(IList), typeof(ListBoxHelper),
8: new FrameworkPropertyMetadata((IList)null,
9: new PropertyChangedCallback(OnSelectedItemsChanged)));
10:
11: /// <summary>
12: /// Gets the SelectedItems property. This dependency property
13: /// indicates ....
14: /// </summary>
15: public static IList GetSelectedItems(DependencyObject d)
16: {
17: return (IList)d.GetValue(SelectedItemsProperty);
18: }
19:
20: /// <summary>
21: /// Sets the SelectedItems property. This dependency property
22: /// indicates ....
23: /// </summary>
24: public static void SetSelectedItems(DependencyObject d, IList value)
25: {
26: d.SetValue(SelectedItemsProperty, value);
27: }
28:
29: /// <summary>
30: /// Handles changes to the SelectedItems property.
31: /// </summary>
32: private static void OnSelectedItemsChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
33: {
34: var listBox = (ListBox)d;
35: ReSetSelectedItems(listBox);
36: listBox.SelectionChanged += delegate
37: {
38: ReSetSelectedItems(listBox);
39: };
40: }
41:
42: #endregion
43:
44: private static void ReSetSelectedItems(ListBox listBox)
45: {
46: IList selectedItems = GetSelectedItems(listBox);
47: selectedItems.Clear();
48: if (listBox.SelectedItems != null)
49: {
50: foreach (var item in listBox.SelectedItems)
51: selectedItems.Add(item);
52: }
53: }
and here is how you would use it in the XAML
1: <ListBox ItemsSource="{Binding MyData}" Grid.Column="1" local:ListBoxHelper.SelectedItems="{Binding SelectedData}"
2: SelectionMode="Extended"/>
As you can see all you have to set is the List you want to populate in the ViewModel and the rest is taken care of by the Attached Property.
Please Note: This code was implemented quickly without any testing to prove an implementation idea, so please do review it before putting it in production





[...] Sync Multi Select Listbox with ViewModel (Marlon Grech) [...]
Pingback by Dew Drop - June 2, 2009 | Alvin Ashcraft's Morning Dew | June 2, 2009 |
Dear Sir,
I hope you are doing well. I got this email address from one of your contribution web site. I have launched a web site http://www.codegain.com and it is basically aimed C#,JAVA,VB.NET,ASP.NET,AJAX,Sql Server,Oracle,WPF,WCF and etc resources, programming help, articles, code snippet, video demonstrations and problems solving support. I would like to invite you as an author and a supporter.
Looking forward to hearing from you and hope you will join with us soon.
Thank you
RRaveen
Founder CodeGain.com
Thanks a lot, it’s very usefull.
Helped me save time . Good One.
Here’s a version that updates the selection when the SelectedItems list changes… so you can programatically change the multi-selection (The bound SelectedItems must be an ObservableCollection.
- Brian
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Collections;
using System.Windows;
using System.Windows.Controls;
using System.Collections.ObjectModel;
namespace UndoRedoSample
{
public class ListBoxHelper
{
#region SelectedItems
///
/// SelectedItems Attached Dependency Property
///
public static readonly DependencyProperty SelectedItemsProperty =
DependencyProperty.RegisterAttached(“SelectedItems”, typeof(ObservableCollection), typeof(ListBoxHelper),
new FrameworkPropertyMetadata((ObservableCollection)null,
new PropertyChangedCallback(OnSelectedItemsChanged)));
///
/// Gets the SelectedItems property. This dependency property
/// indicates ….
///
public static ObservableCollection GetSelectedItems(DependencyObject d)
{
return (ObservableCollection)d.GetValue(SelectedItemsProperty);
}
///
/// Sets the SelectedItems property. This dependency property
/// indicates ….
///
public static void SetSelectedItems(DependencyObject d, ObservableCollection value)
{
d.SetValue(SelectedItemsProperty, value);
}
///
/// Called when SelectedItems is set
///
private static void OnSelectedItemsChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
{
var listBox = (ListBox)d;
ListBoxSelectionChanged(listBox);
ObservableCollection selectedItems = GetSelectedItems(listBox);
selectedItems.CollectionChanged += delegate
{
BoundCollectionChanged(listBox);
};
listBox.SelectionChanged += delegate
{
ListBoxSelectionChanged(listBox);
};
}
private static bool _busy = false;
///
/// Update the listbox
///
static void BoundCollectionChanged(ListBox listBox)
{
if (_busy) // whichever one called first, blocks the other
return;
_busy = true;
ObservableCollection selectedItems = GetSelectedItems(listBox);
listBox.SelectedItems.Clear();
if (selectedItems != null)
{
foreach (var item in selectedItems)
listBox.SelectedItems.Add(item);
}
_busy = false;
}
#endregion
///
/// Update the bound collection
///
private static void ListBoxSelectionChanged(ListBox listBox)
{
if (_busy) // whichever one called first, blocks the other
return;
_busy = true;
ObservableCollection selectedItems = GetSelectedItems(listBox);
selectedItems.Clear();
if (listBox.SelectedItems != null)
{
foreach (var item in listBox.SelectedItems)
selectedItems.Add(item);
}
_busy = false;
}
}
}
IList must be used instead Observable collection (as it is in original sample code). And to add delegate to CollectionChanged, collection of items must be cast to INotifyCollectionChanged:
((INotifyCollectionChanged)selectedItems).CollectionChanged += delegate
{
BoundCollectionChanged(listBox);
};
Hi Marlon, this post was really useful. Thanks!
I developed an alternate solution that involves having a specialized list to take care of the multiselection. Here is the link if anyone is interested.
http://www.gbogea.com/2010/01/02/mvvm-multiselect-listbox
Hi Marlon
While this solution may work for WPF, I can’t get it to work for Silverlight MVVM. Could you provide us with a Silverlight solution for this problem?
Thanks
@Marlon: Great Article, saved me a lot of time
@Brian: I will try your code, as I also missed the possibility to select items programatically
@Bavo: I’m developing for Windows Phone 7 (even more restricted Silverlight) and you just have to replace FrameworkPropertyMetadata by PropertyMetadata in ListBoxHelper. In my case Visual Studio didn’t recognize the ListBoxHelper in Xaml and showed an error, but after compiling once, it disappeared and works fine.
Thanks a lot, works great in Silverlight 4 just by replacing FrameworkPropertyMetadata by PropertyMetadata.
Great !
You rock Marlon.. nice one!
Hi,
This code works great, I have one thing I will like to add to it and want to know if you already did that: I want to use SelectValuePath property to bind the items to the specific value path and not just to the whole item.
The reason is that many time I use some wrapper around the real object and in my ViewModel I want collection of the real items, not the wrappers.
I’ve look at the code of Selector class but most (if now all) of the methods it use for setting the SelectedValue are internal and thus not accessible to me.
Thank you,
Ido.
[...] I am using ListBoxHelper ( in Infrastructure) from http://marlongrech.wordpress.com/2009/06/02/sync-multi-select-listbox-with-viewmodel/ [...]
Pingback by WPF Nested ListBox Databinding using ViewModel | December 19, 2011 |
For what purpose do you need SetSelectedItems, and how to use it? 10x