Click here to Skip to main content
15,889,808 members
Articles / Desktop Programming / WPF
Alternative
Tip/Trick

SelectedItems Behavior for ListBox and MultiSelector

Rate me:
Please Sign up or sign in to vote.
3.64/5 (5 votes)
14 Oct 2017CPOL 8.4K   91   2   2
This is an alternative for "SelectedItems Behavior for ListBox and MultiSelector"

Introduction

I found the originals concept ( <- please follow the link ) good, but the implementation contains several great mistakes.

Issues of the Original

C#
public class ViewModel{
   public string[] ItemsSource { get; } = new[] 
   { "Frist Item", "Second Item", "Third Item", "Fourth Item" };
   public ObservableCollection<string> SelectedItems { get; } = new ObservableCollection<string>();
}

On each access to this SelectedItems-Property, a new ObservableCollection will be created!
Binding several Controls to that, or access such Properties by Code will lead to complex mis-behavior.

C#
public class SelectedItemsBahavior    {
   public static readonly DependencyProperty SelectedItemsProperty =
      DependencyProperty.RegisterAttached
      ("SelectedItems", typeof(INotifyCollectionChanged), typeof(SelectedItemsBahavior),
      new PropertyMetadata(default(IList), OnSelectedItemsChanged));

   public static void SetSelectedItems(DependencyObject d, INotifyCollectionChanged value)        
   {
      d.SetValue(SelectedItemsProperty, value);
   }
   public static IList GetSelectedItems(DependencyObject d)        {
      return (IList)d.GetValue(SelectedItemsProperty);
   }
   //...

This is puzzling: Register a DependencyProperty as INotifyCollectionChanged, but the Property-Getter retrieves an IList-Object??
Moreover - what is it good for, to Register the INotifyCollectionChanged - Type?

In the following you see the CollectionChanged-Event subscribed, but step-wise debugging shows, that the code executes, but has no effect at all.

C#
  1  private static void OnSelectedItemsChanged
  2     (DependencyObject d, DependencyPropertyChangedEventArgs e)  {
  3     IList selectedItems = null;
  4     void CollectionChangedEventHandler(object sender, NotifyCollectionChangedEventArgs args)  {
  5           if (args.OldItems != null) foreach 
  6           (var item in args.OldItems)if (selectedItems.Contains(item))selectedItems.Remove(item);
  7           if (args.NewItems != null) foreach 
  8           (var item in args.NewItems) if (!selectedItems.Contains(item)) selectedItems.Add(item);
  9     }
 10     if (d is MultiSelector multiSelector)   {
 11           selectedItems = multiSelector.SelectedItems;
 12           multiSelector.SelectionChanged += OnSelectionChanged;
 13     }
 14     if (d is ListBox listBox)   {
 15           selectedItems = listBox.SelectedItems;
 16           listBox.SelectionMode = SelectionMode.Multiple;
 17           listBox.SelectionChanged += OnSelectionChanged;
 18     }
 19     if (selectedItems == null) return;
 20     if (e.OldValue is INotifyCollectionChanged collection1)
 21           collection1.CollectionChanged -= CollectionChangedEventHandler;
 22     if (e.NewValue is INotifyCollectionChanged collection2)
 23           collection2.CollectionChanged += CollectionChangedEventHandler;
 24  }
 25  
 26  private static void OnSelectionChanged(object sender, SelectionChangedEventArgs e) {
 27     var s = sender as DependencyObject;
 28     if (!GetIsBusy(s))   {
 29           SetIsBusy(s, true);
 30           var list = GetSelectedItems((DependencyObject)sender);
 31           foreach (var item in e.RemovedItems) if (list.Contains(item)) list.Remove(item);
 32           foreach (var item in e.AddedItems) if (!list.Contains(item)) list.Add(item);
 33           SetIsBusy(s, false);
 34     }
 35  }
 36  private static readonly DependencyProperty IsBusyProperty =
 37     DependencyProperty.RegisterAttached("IsBusy", typeof(bool), 
 38     typeof(SelectedItemsBahavior), new PropertyMetadata(default(bool)));
 39  
 40  private static void SetIsBusy(DependencyObject element, bool value) {
 41     element.SetValue(IsBusyProperty, value);
 42  }
 43  
 44  private static bool GetIsBusy(DependencyObject element) {
 45     return (bool)element.GetValue(IsBusyProperty);
 46  }

There is no need, that the behavior consumes a .CollectionChanged - Event of anything (lines #2-6 + #16-20) - Moreover the IsBusy-Dependancy-Property (lines #25-26, #30, #33-42) has no effect too.


Proposal to fix it

C#
public static class SelectedItemsBahavior {
   public static readonly DependencyProperty SelectedItemsProperty =
         DependencyProperty.RegisterAttached
         ("SelectedItems", typeof(IList), typeof(SelectedItemsBahavior),
         new PropertyMetadata(default(IList), OnAttach));
   public static void SetSelectedItems(DependencyObject d, IList value) {
      d.SetValue(SelectedItemsProperty, value);
   }
   public static IList GetSelectedItems(DependencyObject d) {
      return (IList)d.GetValue(SelectedItemsProperty);
   }
   private static void OnAttach(DependencyObject d, DependencyPropertyChangedEventArgs e) {
      var multiSelector = d as MultiSelector;
      if (multiSelector != null) {
         multiSelector.SelectionChanged += OnSelectionChanged;
         return;
      }
      var listBox = d as ListBox;
      if (listBox != null) {
         listBox.SelectionChanged += OnSelectionChanged;
         listBox.SelectionMode = SelectionMode.Multiple;
      }
   }
   private static void OnSelectionChanged(object sender, SelectionChangedEventArgs e) {
      var list = GetSelectedItems((DependencyObject)sender);
      foreach (var item in e.RemovedItems) list.Remove(item);
      foreach (var item in e.AddedItems) list.Add(item);
   }
}

Common implementation of an IList-DependencyProperty, and the behaviors job is simply to keep that lists content up to date.

License

This article, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)


Written By
Germany Germany
This member has not yet provided a Biography. Assume it's interesting and varied, and probably something to do with programming.

Comments and Discussions

 
QuestionPlease don't set SelectionMode in Behavior Pin
Dirk Bahle26-Nov-17 6:58
Dirk Bahle26-Nov-17 6:58 
PraiseNice Modification Pin
Bill S20-Oct-17 9:57
professionalBill S20-Oct-17 9:57 
This is a much better alternative and appreciate the effort.
Bill L

General General    News News    Suggestion Suggestion    Question Question    Bug Bug    Answer Answer    Joke Joke    Praise Praise    Rant Rant    Admin Admin   

Use Ctrl+Left/Right to switch messages, Ctrl+Up/Down to switch threads, Ctrl+Shift+Left/Right to switch pages.