1 namespace System.Activities.Presentation.View {
4 using System.Diagnostics.CodeAnalysis;
5 using System.Activities.Presentation;
6 using System.Activities.Presentation.Model;
8 using System.Activities.Presentation.Internal.Properties;
11 using System.Collections;
12 using System.Collections.Generic;
13 using System.ComponentModel;
16 /// The Selection class defines a selection of objects. Selections
17 /// consist of zero or more objects. The first object in a selection
18 /// is defined as the "primary" selection, which is used when
19 /// one object in a group must be used as a key.
21 [SuppressMessage(FxCop.Category.Naming, "CA1724:TypeNamesShouldNotMatchNamespaces",
22 Justification = "Code imported from Cider; keeping changes to a minimum as it impacts xaml files as well")]
23 public class Selection : ContextItem
26 private ICollection<ModelItem> _selectedObjects;
29 /// Creates an empty Selection object.
32 _selectedObjects = new ModelItem[0];
36 /// Creates a collection object comprising the given
37 /// selected objects. The first object in the enumeration
38 /// is considered the "primary" selection.
40 /// <param name="selectedObjects">An enumeration of objects that should be selected.</param>
41 /// <exception cref="ArgumentNullException">If selectedObjects is null.</exception>
42 public Selection(IEnumerable<ModelItem> selectedObjects) {
43 if (selectedObjects == null) {
44 throw FxTrace.Exception.ArgumentNull("selectedObjects");
47 List<ModelItem> selection = new List<ModelItem>();
48 selection.AddRange(selectedObjects);
49 _selectedObjects = selection;
53 /// Creates a collection object comprising the given
54 /// selected objects. The first object in the enumeration
55 /// is considered the "primary" selection.
57 /// <param name="selectedObjects">An enumeration of objects that should be selected.</param>
58 /// <param name="match">If provided, only those objects in selectedObjects that match the predicate will be added to the selection.</param>
59 /// <exception cref="ArgumentNullException">If selectedObjects or match is null.</exception>
60 public Selection(IEnumerable<ModelItem> selectedObjects, Predicate<ModelItem> match) {
61 if (selectedObjects == null) throw FxTrace.Exception.ArgumentNull("selectedObjects");
62 if (match == null) throw FxTrace.Exception.ArgumentNull("match");
64 List<ModelItem> selection = new List<ModelItem>();
65 foreach (ModelItem o in selectedObjects) {
71 _selectedObjects = selection;
75 /// Creates a collection object comprising the given
76 /// selected objects. The first object in the enumeration
77 /// is considered the "primary" selection.
79 /// <param name="selectedObjects">An enumeration of objects that should be selected.</param>
80 /// <exception cref="ArgumentNullException">If selectedObjects is null.</exception>
81 public Selection(IEnumerable selectedObjects) {
82 if (selectedObjects == null) throw FxTrace.Exception.ArgumentNull("selectedObjects");
84 List<ModelItem> selection = new List<ModelItem>();
85 foreach (object o in selectedObjects) {
86 ModelItem item = o as ModelItem;
92 _selectedObjects = selection;
96 /// Creates a collection object comprising the given
97 /// selected objects. The first object in the enumeration
98 /// is considered the "primary" selection.
100 /// <param name="selectedObjects">An enumeration of objects that should be selected.</param>
101 /// <param name="match">If provided, only those objects in selectedObjects that match the predicate will be added to the selection.</param>
102 /// <exception cref="ArgumentNullException">If selectedObjects is null.</exception>
103 public Selection(IEnumerable selectedObjects, Predicate<ModelItem> match) {
104 if (selectedObjects == null) throw FxTrace.Exception.ArgumentNull("selectedObjects");
105 if (match == null) throw FxTrace.Exception.ArgumentNull("match");
107 List<ModelItem> selection = new List<ModelItem>();
108 foreach (object o in selectedObjects) {
109 ModelItem item = o as ModelItem;
110 if (item != null && match(item)) {
115 _selectedObjects = selection;
119 /// Creates a collection object comprising the given
120 /// objects. The first object is considered the "primary"
123 /// <param name="selectedObjects">A parameter array of objects that should be selected.</param>
124 /// <exception cref="ArgumentNullException">If selectedObjects is null.</exception>
125 public Selection(params ModelItem[] selectedObjects)
126 : this((IEnumerable<ModelItem>)selectedObjects) {
130 /// The primary selection. Some functions require a "key"
131 /// element. For example, an "align lefts" command needs
132 /// to know which element's "left" to align to.
134 public ModelItem PrimarySelection {
136 foreach (ModelItem obj in _selectedObjects) {
145 /// The enumeration of selected objects.
147 public IEnumerable<ModelItem> SelectedObjects {
149 return _selectedObjects;
154 /// The number of objects that are currently selected into
157 public int SelectionCount {
158 get { return _selectedObjects.Count; }
162 /// Override of ContextItem's ItemType
163 /// property. The ItemType of Selection is
164 /// always "typeof(Selection)".
166 public sealed override Type ItemType {
168 return typeof(Selection);
174 /// Selection helper method. This takes the existing selection in the
175 /// context and selects an item into it. If the item is already in the
176 /// selection the selection is preserved and the item is promoted
177 /// to the primary selection.
179 /// <param name="context">The editing context to apply this selection change to.</param>
180 /// <param name="itemToSelect">The item to select.</param>
181 /// <returns>A Selection object that contains the new selection.</returns>
182 /// <exception cref="ArgumentNullException">If context or itemToSelect is null.</exception>
183 public static Selection Select(EditingContext context, ModelItem itemToSelect) {
185 if (context == null) throw FxTrace.Exception.ArgumentNull("context");
186 if (itemToSelect == null) throw FxTrace.Exception.ArgumentNull("itemToSelect");
188 Selection existing = context.Items.GetValue<Selection>();
190 // short cut if we're already in the right state.
191 if (existing.PrimarySelection == itemToSelect) {
195 Selection selection = null;
197 foreach (ModelItem obj in existing.SelectedObjects) {
198 if (obj == itemToSelect) {
199 List<ModelItem> list = new List<ModelItem>(existing.SelectedObjects);
200 list.Remove(itemToSelect);
201 list.Insert(0, itemToSelect);
202 selection = new Selection(list);
206 if (selection == null) {
207 selection = new Selection(itemToSelect);
210 context.Items.SetValue(selection);
215 /// Selection helper method. This sets itemToSelect into the selection.
216 /// Any existing items are deselected.
218 /// <param name="context">The editing context to apply this selection change to.</param>
219 /// <param name="itemToSelect">The item to select.</param>
220 /// <returns>A Selection object that contains the new selection.</returns>
221 /// <exception cref="ArgumentNullException">If context or itemToSelect is null.</exception>
222 public static Selection SelectOnly(EditingContext context, ModelItem itemToSelect) {
224 if (context == null) throw FxTrace.Exception.ArgumentNull("context");
225 if (itemToSelect == null) throw FxTrace.Exception.ArgumentNull("itemToSelect");
227 // Check to see if only this object is selected. If so, bail.
228 Selection existing = context.Items.GetValue<Selection>();
229 if (existing.PrimarySelection == itemToSelect) {
230 IEnumerator<ModelItem> en = existing.SelectedObjects.GetEnumerator();
232 if (!en.MoveNext()) {
237 DesignerPerfEventProvider designerPerfEventProvider = context.Services.GetService<DesignerPerfEventProvider>();
238 if (designerPerfEventProvider != null)
240 designerPerfEventProvider.SelectionChangedStart();
243 Selection selection = new Selection(itemToSelect);
244 context.Items.SetValue(selection);
249 /// Helper method that subscribes to selection change events.
251 /// <param name="context">The editing context to listen to.</param>
252 /// <param name="handler">The handler to be invoked when the selection changes.</param>
253 public static void Subscribe(EditingContext context, SubscribeContextCallback<Selection> handler) {
254 if (context == null) throw FxTrace.Exception.ArgumentNull("context");
255 if (handler == null) throw FxTrace.Exception.ArgumentNull("handler");
256 context.Items.Subscribe<Selection>(handler);
260 /// Selection helper method. This takes the existing selection in the
261 /// context and creates a new selection that contains the toggled
262 /// state of the item. If the item is to be
263 /// added to the selection, it is added as the primary selection.
265 /// <param name="context">The editing context to apply this selection change to.</param>
266 /// <param name="itemToToggle">The item to toggle selection for.</param>
267 /// <returns>A Selection object that contains the new selection.</returns>
268 /// <exception cref="ArgumentNullException">If context or itemToToggle is null.</exception>
269 public static Selection Toggle(EditingContext context, ModelItem itemToToggle) {
271 if (context == null) throw FxTrace.Exception.ArgumentNull("context");
272 if (itemToToggle == null) throw FxTrace.Exception.ArgumentNull("itemToToggle");
274 Selection existing = context.Items.GetValue<Selection>();
276 // Is the item already in the selection? If so, remove it.
277 // If not, add it to the beginning.
279 List<ModelItem> list = new List<ModelItem>(existing.SelectedObjects);
280 if (list.Contains(itemToToggle)) {
281 list.Remove(itemToToggle);
284 list.Insert(0, itemToToggle);
287 Selection selection = new Selection(list);
288 context.Items.SetValue(selection);
293 /// Selection helper method. This takes the existing selection in the
294 /// context and creates a new selection that contains the original
295 /// selection and the itemToAdd. If itemToAdd is already in the
296 /// original selection it is promoted to the primary selection.
298 /// <param name="context">The editing context to apply this selection change to.</param>
299 /// <param name="itemToAdd">The item to add to the selection.</param>
300 /// <returns>A Selection object that contains the new selection.</returns>
301 /// <exception cref="ArgumentNullException">If context or itemToAdd is null.</exception>
302 public static Selection Union(EditingContext context, ModelItem itemToAdd) {
304 if (context == null) throw FxTrace.Exception.ArgumentNull("context");
305 if (itemToAdd == null) throw FxTrace.Exception.ArgumentNull("itemToAdd");
307 Selection existing = context.Items.GetValue<Selection>();
309 // short cut if we're already in the right state.
310 if (existing.PrimarySelection == itemToAdd) {
314 // Is the item already in the selection? If not, add it.
315 List<ModelItem> list = new List<ModelItem>(existing.SelectedObjects);
316 if (list.Contains(itemToAdd)) {
317 list.Remove(itemToAdd);
320 list.Insert(0, itemToAdd);
321 Selection selection = new Selection(list);
322 context.Items.SetValue(selection);
326 internal static bool MultipleObjectsSelected(EditingContext context)
328 Selection selection = context.Items.GetValue<Selection>();
329 if (selection != null && selection.SelectionCount > 1)
336 internal static bool IsSelection(ModelItem item)
338 PropertyDescriptor descriptor = TypeDescriptor.GetProperties(item)["IsSelection"];
339 if (descriptor != null)
341 return (bool)descriptor.GetValue(item);
346 internal static bool IsPrimarySelection(ModelItem item)
348 PropertyDescriptor descriptor = TypeDescriptor.GetProperties(item)["IsPrimarySelection"];
349 if (descriptor != null)
351 return (bool)descriptor.GetValue(item);
357 /// Helper method that removes a previously added selection change event.
359 /// <param name="context">The editing context to listen to.</param>
360 /// <param name="handler">The handler to be invoked when the selection changes.</param>
361 public static void Unsubscribe(EditingContext context, SubscribeContextCallback<Selection> handler) {
362 if (context == null) throw FxTrace.Exception.ArgumentNull("context");
363 if (handler == null) throw FxTrace.Exception.ArgumentNull("handler");
364 context.Items.Unsubscribe<Selection>(handler);