-
Notifications
You must be signed in to change notification settings - Fork 2
/
Ex.cs
161 lines (154 loc) · 7.54 KB
/
Ex.cs
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
using System;
using System.Collections;
using System.Collections.Specialized;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Controls.Primitives;
namespace SelectedItemsTestApplication
{
public class Ex : DependencyObject
{
public static readonly DependencyProperty IsSubscribedToSelectionChangedProperty = DependencyProperty.RegisterAttached(
"IsSubscribedToSelectionChanged", typeof(bool), typeof(Ex), new PropertyMetadata(default(bool)));
public static void SetIsSubscribedToSelectionChanged(DependencyObject element, bool value) { element.SetValue(IsSubscribedToSelectionChangedProperty, value); }
public static bool GetIsSubscribedToSelectionChanged(DependencyObject element) { return (bool)element.GetValue(IsSubscribedToSelectionChangedProperty); }
public static readonly DependencyProperty SelectedItemsProperty = DependencyProperty.RegisterAttached(
"SelectedItems", typeof(IList), typeof(Ex), new PropertyMetadata(default(IList), OnSelectedItemsChanged));
public static void SetSelectedItems(DependencyObject element, IList value) { element.SetValue(SelectedItemsProperty, value); }
public static IList GetSelectedItems(DependencyObject element) { return (IList)element.GetValue(SelectedItemsProperty); }
/// <summary>
/// Attaches a list or observable collection to the grid or listbox, syncing both lists (one way sync for simple lists).
/// </summary>
/// <param name="d">The DataGrid or ListBox</param>
/// <param name="e">The list to sync to.</param>
private static void OnSelectedItemsChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
{
if (!(d is ListBox || d is MultiSelector))
throw new ArgumentException("Somehow this got attached to an object I don't support. ListBoxes and Multiselectors (DataGrid), people. Geesh =P!");
var selector = (Selector)d;
var oldList = e.OldValue as IList;
if (oldList != null)
{
var obs = oldList as INotifyCollectionChanged;
if (obs != null)
{
obs.CollectionChanged -= OnCollectionChanged;
}
// If we're orphaned, disconnect lb/dg events.
if (e.NewValue == null)
{
selector.SelectionChanged -= OnSelectorSelectionChanged;
SetIsSubscribedToSelectionChanged(selector, false);
}
}
var newList = (IList)e.NewValue;
if (newList != null)
{
var obs = newList as INotifyCollectionChanged;
if (obs != null)
{
obs.CollectionChanged += OnCollectionChanged;
}
PushCollectionDataToSelectedItems(newList, selector);
var isSubscribed = GetIsSubscribedToSelectionChanged(selector);
if (!isSubscribed)
{
selector.SelectionChanged += OnSelectorSelectionChanged;
SetIsSubscribedToSelectionChanged(selector, true);
}
}
}
/// <summary>
/// Initially set the selected items to the items in the newly connected collection,
/// unless the new collection has no selected items and the listbox/grid does, in which case
/// the flow is reversed. The data holder sets the state. If both sides hold data, then the
/// bound IList wins and dominates the helpless wpf control.
/// </summary>
/// <param name="obs">The list to sync to</param>
/// <param name="selector">The grid or listbox</param>
private static void PushCollectionDataToSelectedItems(IList obs, DependencyObject selector)
{
var listBox = selector as ListBox;
if (listBox != null)
{
if (obs.Count > 0)
{
listBox.SelectedItems.Clear();
foreach (var ob in obs) { listBox.SelectedItems.Add(ob); }
}
else
{
foreach (var ob in listBox.SelectedItems) { obs.Add(ob); }
}
return;
}
// Maybe other things will use the multiselector base... who knows =P
var grid = selector as MultiSelector;
if (grid != null)
{
if (obs.Count > 0)
{
grid.SelectedItems.Clear();
foreach (var ob in obs) { grid.SelectedItems.Add(ob); }
}
else
{
foreach (var ob in grid.SelectedItems) { obs.Add(ob); }
}
return;
}
throw new ArgumentException("Somehow this got attached to an object I don't support. ListBoxes and Multiselectors (DataGrid), people. Geesh =P!");
}
/// <summary>
/// When the listbox or grid fires a selectionChanged even, we update the attached list to
/// match it.
/// </summary>
/// <param name="sender">The listbox or grid</param>
/// <param name="e">Items added and removed.</param>
private static void OnSelectorSelectionChanged(object sender, SelectionChangedEventArgs e)
{
var dep = (DependencyObject)sender;
var items = GetSelectedItems(dep);
var col = items as INotifyCollectionChanged;
// Remove the events so we don't fire back and forth, then re-add them.
if (col != null) col.CollectionChanged -= OnCollectionChanged;
foreach (var oldItem in e.RemovedItems) items.Remove(oldItem);
foreach (var newItem in e.AddedItems) items.Add(newItem);
if (col != null) col.CollectionChanged += OnCollectionChanged;
}
/// <summary>
/// When the attached object implements INotifyCollectionChanged, the attached listbox
/// or grid will have its selectedItems adjusted by this handler.
/// </summary>
/// <param name="sender">The listbox or grid</param>
/// <param name="e">The added and removed items</param>
private static void OnCollectionChanged(object sender, NotifyCollectionChangedEventArgs e)
{
// Push the changes to the selected item.
var listbox = sender as ListBox;
if (listbox != null)
{
listbox.SelectionChanged -= OnSelectorSelectionChanged;
if (e.Action == NotifyCollectionChangedAction.Reset) listbox.SelectedItems.Clear();
else
{
foreach (var oldItem in e.OldItems) listbox.SelectedItems.Remove(oldItem);
foreach (var newItem in e.NewItems) listbox.SelectedItems.Add(newItem);
}
listbox.SelectionChanged += OnSelectorSelectionChanged;
}
var grid = sender as MultiSelector;
if (grid != null)
{
grid.SelectionChanged -= OnSelectorSelectionChanged;
if (e.Action == NotifyCollectionChangedAction.Reset) grid.SelectedItems.Clear();
else
{
foreach (var oldItem in e.OldItems) grid.SelectedItems.Remove(oldItem);
foreach (var newItem in e.NewItems) grid.SelectedItems.Add(newItem);
}
grid.SelectionChanged += OnSelectorSelectionChanged;
}
}
}
}