using System; using System.Collections; using System.Linq; using System.Windows; using System.Windows.Controls; namespace MahTweets.Mango.Core.Behaviours { public class ListBoxBehavior { /// /// Identifies the ListBoxExtension.SelectedItemsSource attached property. /// public static readonly DependencyProperty SelectedItemsSourceProperty = DependencyProperty.RegisterAttached( "SelectedItemsSource", typeof (IList), typeof (ListBoxBehavior), new PropertyMetadata(null, OnSelectedItemsSourceChanged)); private static readonly DependencyProperty IsResynchingProperty = DependencyProperty.RegisterAttached( "IsResynching", typeof (bool), typeof (ListBoxBehavior), new PropertyMetadata(false)); /// /// Gets the IList that contains the values that should be selected. /// /// The ListBox to check. public static IList GetSelectedItemsSource(DependencyObject element) { if (element == null) throw new ArgumentNullException("element"); return (IList) element.GetValue(SelectedItemsSourceProperty); } /// /// Sets the IList that contains the values that should be selected. /// /// The ListBox being set. /// The value of the property. public static void SetSelectedItemsSource(DependencyObject element, IList value) { if (element == null) throw new ArgumentNullException("element"); element.SetValue(SelectedItemsSourceProperty, value); } // Used to set a flag on the ListBox to avoid reentry of SelectionChanged due to // a full syncronisation pass private static void OnSelectedItemsSourceChanged(DependencyObject d, DependencyPropertyChangedEventArgs e) { var listBox = d as ListBox; if (listBox == null) throw new InvalidOperationException("The ListBoxExtension.SelectedItemsSource attached " + "property can only be applied to ListBox controls."); listBox.SelectionChanged -= OnListBoxSelectionChanged; listBox.Loaded -= ListBoxLoaded; if (e.NewValue != null) ListenForChanges(listBox); } private static void ListenForChanges(ListBox listBox) { listBox.SelectionChanged += OnListBoxSelectionChanged; listBox.Loaded += ListBoxLoaded; ResynchList(listBox); } static void ListBoxLoaded(object sender, RoutedEventArgs e) { var listBox = sender as ListBox; ResynchList(listBox); } private static void OnListBoxSelectionChanged(object sender, SelectionChangedEventArgs e) { var listBox = sender as ListBox; if (listBox == null) return; var isResynching = (bool) listBox.GetValue(IsResynchingProperty); if (isResynching) return; var list = GetSelectedItemsSource(listBox); if (list == null) return; foreach (var obj in e.RemovedItems.Cast().Where(list.Contains)) { list.Remove(obj); } foreach (var obj in e.AddedItems.Cast().Where(obj => !list.Contains(obj))) { list.Add(obj); } } private static void ResynchList(ListBox listBox) { if (listBox == null) return; listBox.SetValue(IsResynchingProperty, true); var list = GetSelectedItemsSource(listBox); if (listBox.SelectionMode == SelectionMode.Single) { listBox.SelectedItem = null; if (list != null) { if (list.Count > 1) { // There is more than one item selected, but the listbox is in Single selection mode throw new InvalidOperationException( "ListBox is in Single selection mode, but was given more than one selected value."); } if (list.Count == 1) listBox.SelectedItem = list[0]; } } else { listBox.SelectedItems.Clear(); if (list != null) { foreach (var obj in listBox.Items.Where(list.Contains)) { listBox.SelectedItems.Add(obj); } } } listBox.SetValue(IsResynchingProperty, false); } } }