C# Disciples

my life in Avalon ….

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 :)

Download the source code

About these ads

June 2, 2009 - Posted by | mvvm, tips and tricks, WPF

14 Comments »

  1. [...] Sync Multi Select Listbox with ViewModel (Marlon Grech) [...]

    Pingback by Dew Drop - June 2, 2009 | Alvin Ashcraft's Morning Dew | June 2, 2009 | Reply

  2. 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

    Comment by RRaveen | June 10, 2009 | Reply

  3. Thanks a lot, it’s very usefull.

    Comment by rebel | July 28, 2009 | Reply

  4. Helped me save time . Good One.

    Comment by Shree Menon | September 17, 2009 | Reply

  5. 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;
    }
    }
    }

    Comment by Brian Rice | September 24, 2009 | Reply

  6. 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);
    };

    Comment by Andris Lubans | December 28, 2009 | Reply

  7. 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

    Comment by Gabriel Bogea Perez | January 4, 2010 | Reply

  8. 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

    Comment by Bavo Ketels | September 10, 2010 | Reply

    • @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.

      Comment by DIN W0 | October 8, 2010 | Reply

  9. Thanks a lot, works great in Silverlight 4 just by replacing FrameworkPropertyMetadata by PropertyMetadata.

    Great !

    Comment by Glop75 | January 14, 2011 | Reply

  10. You rock Marlon.. nice one!

    Comment by Rhett Clinton | March 1, 2011 | Reply

  11. 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.

    Comment by Ido Ran | March 14, 2011 | Reply

  12. [...] 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 | Reply

  13. For what purpose do you need SetSelectedItems, and how to use it? 10x

    Comment by harmyder | November 29, 2012 | Reply


Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Connecting to %s

Follow

Get every new post delivered to your Inbox.

Join 845 other followers

%d bloggers like this: