SelectingItemsControl.cs 27 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771
  1. // Copyright (c) The Perspex Project. All rights reserved.
  2. // Licensed under the MIT license. See licence.md file in the project root for full license information.
  3. using System;
  4. using System.Collections;
  5. using System.Collections.Generic;
  6. using System.Collections.Specialized;
  7. using System.Linq;
  8. using Perspex.Collections;
  9. using Perspex.Controls.Generators;
  10. using Perspex.Input;
  11. using Perspex.Interactivity;
  12. using Perspex.Styling;
  13. using Perspex.VisualTree;
  14. namespace Perspex.Controls.Primitives
  15. {
  16. /// <summary>
  17. /// An <see cref="ItemsControl"/> that maintains a selection.
  18. /// </summary>
  19. /// <remarks>
  20. /// <para>
  21. /// <see cref="SelectingItemsControl"/> provides a base class for <see cref="ItemsControl"/>s
  22. /// that maintain a selection (single or multiple). By default only its
  23. /// <see cref="SelectedIndex"/> and <see cref="SelectedItem"/> properties are visible; the
  24. /// current multiple selection <see cref="SelectedItems"/> together with the
  25. /// <see cref="SelectionMode"/> properties are protected, however a derived class can expose
  26. /// these if it wishes to support multiple selection.
  27. /// </para>
  28. /// <para>
  29. /// <see cref="SelectingItemsControl"/> maintains a selection respecting the current
  30. /// <see cref="SelectionMode"/> but it does not react to user input; this must be handled in a
  31. /// derived class. It does, however, respond to <see cref="IsSelectedChangedEvent"/> events
  32. /// from items and updates the selection accordingly.
  33. /// </para>
  34. /// </remarks>
  35. public class SelectingItemsControl : ItemsControl
  36. {
  37. /// <summary>
  38. /// Defines the <see cref="SelectedIndex"/> property.
  39. /// </summary>
  40. public static readonly PerspexProperty<int> SelectedIndexProperty =
  41. PerspexProperty.RegisterDirect<SelectingItemsControl, int>(
  42. nameof(SelectedIndex),
  43. o => o.SelectedIndex,
  44. (o, v) => o.SelectedIndex = v);
  45. /// <summary>
  46. /// Defines the <see cref="SelectedItem"/> property.
  47. /// </summary>
  48. public static readonly PerspexProperty<object> SelectedItemProperty =
  49. PerspexProperty.RegisterDirect<SelectingItemsControl, object>(
  50. nameof(SelectedItem),
  51. o => o.SelectedItem,
  52. (o, v) => o.SelectedItem = v);
  53. /// <summary>
  54. /// Defines the <see cref="SelectedItems"/> property.
  55. /// </summary>
  56. protected static readonly PerspexProperty<IList> SelectedItemsProperty =
  57. PerspexProperty.RegisterDirect<SelectingItemsControl, IList>(
  58. nameof(SelectedItems),
  59. o => o.SelectedItems,
  60. (o, v) => o.SelectedItems = v);
  61. /// <summary>
  62. /// Defines the <see cref="SelectionMode"/> property.
  63. /// </summary>
  64. protected static readonly PerspexProperty<SelectionMode> SelectionModeProperty =
  65. PerspexProperty.Register<SelectingItemsControl, SelectionMode>(
  66. nameof(SelectionMode));
  67. /// <summary>
  68. /// Event that should be raised by items that implement <see cref="ISelectable"/> to
  69. /// notify the parent <see cref="SelectingItemsControl"/> that their selection state
  70. /// has changed.
  71. /// </summary>
  72. public static readonly RoutedEvent<RoutedEventArgs> IsSelectedChangedEvent =
  73. RoutedEvent.Register<SelectingItemsControl, RoutedEventArgs>("IsSelectedChanged", RoutingStrategies.Bubble);
  74. private int _selectedIndex = -1;
  75. private object _selectedItem;
  76. private IList _selectedItems;
  77. private bool _ignoreContainerSelectionChanged;
  78. private IList _clearSelectedItemsAfterDataContextChanged;
  79. /// <summary>
  80. /// Initializes static members of the <see cref="SelectingItemsControl"/> class.
  81. /// </summary>
  82. static SelectingItemsControl()
  83. {
  84. IsSelectedChangedEvent.AddClassHandler<SelectingItemsControl>(x => x.ContainerSelectionChanged);
  85. }
  86. /// <summary>
  87. /// Initializes a new instance of the <see cref="SelectingItemsControl"/> class.
  88. /// </summary>
  89. public SelectingItemsControl()
  90. {
  91. ItemContainerGenerator.ContainersInitialized.Subscribe(ContainersInitialized);
  92. }
  93. /// <summary>
  94. /// Gets or sets the index of the selected item.
  95. /// </summary>
  96. public int SelectedIndex
  97. {
  98. get
  99. {
  100. return _selectedIndex;
  101. }
  102. set
  103. {
  104. var old = SelectedIndex;
  105. var effective = (value >= 0 && value < Items?.Cast<object>().Count()) ? value : -1;
  106. if (old != effective)
  107. {
  108. _selectedIndex = effective;
  109. RaisePropertyChanged(SelectedIndexProperty, old, effective, BindingPriority.LocalValue);
  110. SelectedItem = ElementAt(Items, effective);
  111. }
  112. }
  113. }
  114. /// <summary>
  115. /// Gets or sets the selected item.
  116. /// </summary>
  117. public object SelectedItem
  118. {
  119. get
  120. {
  121. return _selectedItem;
  122. }
  123. set
  124. {
  125. var old = SelectedItem;
  126. var index = IndexOf(Items, value);
  127. var effective = index != -1 ? value : null;
  128. if (effective != old)
  129. {
  130. _selectedItem = effective;
  131. RaisePropertyChanged(SelectedItemProperty, old, effective, BindingPriority.LocalValue);
  132. SelectedIndex = index;
  133. if (effective != null)
  134. {
  135. if (SelectedItems.Count != 1 || SelectedItems[0] != effective)
  136. {
  137. SelectedItems.Clear();
  138. SelectedItems.Add(effective);
  139. }
  140. }
  141. else if (SelectedItems.Count > 0)
  142. {
  143. if (!IsDataContextChanging)
  144. {
  145. SelectedItems.Clear();
  146. }
  147. else
  148. {
  149. // The DataContext is changing, and it's quite possible that our
  150. // selection is being cleared because both Items and SelectedItems
  151. // are bound to something on the DataContext. However, if we clear
  152. // the collection now, we may be clearing a the SelectedItems from
  153. // the DataContext which is being unbound, so do it after DataContext
  154. // has notified all interested parties, in
  155. // the OnDataContextFinishedChanging method.
  156. _clearSelectedItemsAfterDataContextChanged = SelectedItems;
  157. }
  158. }
  159. }
  160. }
  161. }
  162. /// <summary>
  163. /// Gets the selected items.
  164. /// </summary>
  165. protected IList SelectedItems
  166. {
  167. get
  168. {
  169. if (_selectedItems == null)
  170. {
  171. _selectedItems = new PerspexList<object>();
  172. SubscribeToSelectedItems();
  173. }
  174. return _selectedItems;
  175. }
  176. set
  177. {
  178. UnsubscribeFromSelectedItems();
  179. _selectedItems = value ?? new PerspexList<object>();
  180. SubscribeToSelectedItems();
  181. }
  182. }
  183. /// <summary>
  184. /// Gets or sets the selection mode.
  185. /// </summary>
  186. protected SelectionMode SelectionMode
  187. {
  188. get { return GetValue(SelectionModeProperty); }
  189. set { SetValue(SelectionModeProperty, value); }
  190. }
  191. /// <summary>
  192. /// Gets a value indicating whether <see cref="SelectionMode.AlwaysSelected"/> is set.
  193. /// </summary>
  194. protected bool AlwaysSelected => (SelectionMode & SelectionMode.AlwaysSelected) != 0;
  195. /// <summary>
  196. /// Tries to get the container that was the source of an event.
  197. /// </summary>
  198. /// <param name="eventSource">The control that raised the event.</param>
  199. /// <returns>The container or null if the event did not originate in a container.</returns>
  200. protected IControl GetContainerFromEventSource(IInteractive eventSource)
  201. {
  202. var item = ((IVisual)eventSource).GetSelfAndVisualAncestors()
  203. .OfType<ILogical>()
  204. .FirstOrDefault(x => x.LogicalParent == this);
  205. return item as IControl;
  206. }
  207. /// <inheritdoc/>
  208. protected override void ItemsChanged(PerspexPropertyChangedEventArgs e)
  209. {
  210. base.ItemsChanged(e);
  211. if (SelectedIndex != -1)
  212. {
  213. SelectedIndex = IndexOf((IEnumerable)e.NewValue, SelectedItem);
  214. }
  215. else if (AlwaysSelected && Items != null & Items.Cast<object>().Any())
  216. {
  217. SelectedIndex = 0;
  218. }
  219. }
  220. /// <inheritdoc/>
  221. protected override void ItemsCollectionChanged(object sender, NotifyCollectionChangedEventArgs e)
  222. {
  223. base.ItemsCollectionChanged(sender, e);
  224. switch (e.Action)
  225. {
  226. case NotifyCollectionChangedAction.Add:
  227. if (AlwaysSelected && SelectedIndex == -1)
  228. {
  229. SelectedIndex = 0;
  230. }
  231. break;
  232. case NotifyCollectionChangedAction.Remove:
  233. case NotifyCollectionChangedAction.Replace:
  234. var selectedIndex = SelectedIndex;
  235. if (selectedIndex >= e.OldStartingIndex &&
  236. selectedIndex < e.OldStartingIndex + e.OldItems.Count)
  237. {
  238. if (!AlwaysSelected)
  239. {
  240. SelectedIndex = -1;
  241. }
  242. else
  243. {
  244. LostSelection();
  245. }
  246. }
  247. break;
  248. case NotifyCollectionChangedAction.Reset:
  249. SelectedIndex = IndexOf(e.NewItems, SelectedItem);
  250. break;
  251. }
  252. }
  253. protected override void OnDataContextFinishedChanging()
  254. {
  255. if (_clearSelectedItemsAfterDataContextChanged == SelectedItems)
  256. {
  257. _clearSelectedItemsAfterDataContextChanged.Clear();
  258. }
  259. _clearSelectedItemsAfterDataContextChanged = null;
  260. }
  261. /// <summary>
  262. /// Updates the selection for an item based on user interaction.
  263. /// </summary>
  264. /// <param name="index">The index of the item.</param>
  265. /// <param name="select">Whether the item should be selected or unselected.</param>
  266. /// <param name="rangeModifier">Whether the range modifier is enabled (i.e. shift key).</param>
  267. /// <param name="toggleModifier">Whether the toggle modifier is enabled (i.e. ctrl key).</param>
  268. protected void UpdateSelection(
  269. int index,
  270. bool select = true,
  271. bool rangeModifier = false,
  272. bool toggleModifier = false)
  273. {
  274. if (index != -1)
  275. {
  276. if (select)
  277. {
  278. var mode = SelectionMode;
  279. var toggle = toggleModifier || (mode & SelectionMode.Toggle) != 0;
  280. var multi = (mode & SelectionMode.Multiple) != 0;
  281. var range = multi && SelectedIndex != -1 ? rangeModifier : false;
  282. if (!toggle && !range)
  283. {
  284. SelectedIndex = index;
  285. }
  286. else if (multi && range)
  287. {
  288. SynchronizeItems(
  289. SelectedItems,
  290. GetRange(Items, SelectedIndex, index));
  291. }
  292. else
  293. {
  294. var item = ElementAt(Items, index);
  295. var i = SelectedItems.IndexOf(item);
  296. if (i != -1 && (!AlwaysSelected || SelectedItems.Count > 1))
  297. {
  298. SelectedItems.Remove(item);
  299. }
  300. else
  301. {
  302. if (multi)
  303. {
  304. SelectedItems.Add(item);
  305. }
  306. else
  307. {
  308. SelectedIndex = index;
  309. }
  310. }
  311. }
  312. if (Presenter?.Panel != null)
  313. {
  314. var container = ItemContainerGenerator.ContainerFromIndex(index);
  315. KeyboardNavigation.SetTabOnceActiveElement(
  316. (InputElement)Presenter.Panel,
  317. container);
  318. }
  319. }
  320. else
  321. {
  322. LostSelection();
  323. }
  324. }
  325. }
  326. /// <summary>
  327. /// Updates the selection for a container based on user interaction.
  328. /// </summary>
  329. /// <param name="container">The container.</param>
  330. /// <param name="select">Whether the container should be selected or unselected.</param>
  331. /// <param name="rangeModifier">Whether the range modifier is enabled (i.e. shift key).</param>
  332. /// <param name="toggleModifier">Whether the toggle modifier is enabled (i.e. ctrl key).</param>
  333. protected void UpdateSelection(
  334. IControl container,
  335. bool select = true,
  336. bool rangeModifier = false,
  337. bool toggleModifier = false)
  338. {
  339. var index = ItemContainerGenerator.IndexFromContainer(container);
  340. if (index != -1)
  341. {
  342. UpdateSelection(index, select, rangeModifier, toggleModifier);
  343. }
  344. }
  345. /// <summary>
  346. /// Updates the selection based on an event source that may have originated in a container
  347. /// that belongs to the control.
  348. /// </summary>
  349. /// <param name="eventSource">The control that raised the event.</param>
  350. /// <param name="select">Whether the container should be selected or unselected.</param>
  351. /// <param name="rangeModifier">Whether the range modifier is enabled (i.e. shift key).</param>
  352. /// <param name="toggleModifier">Whether the toggle modifier is enabled (i.e. ctrl key).</param>
  353. /// <returns>
  354. /// True if the event originated from a container that belongs to the control; otherwise
  355. /// false.
  356. /// </returns>
  357. protected bool UpdateSelectionFromEventSource(
  358. IInteractive eventSource,
  359. bool select = true,
  360. bool rangeModifier = false,
  361. bool toggleModifier = false)
  362. {
  363. var item = GetContainerFromEventSource(eventSource);
  364. if (item != null)
  365. {
  366. UpdateSelection(item, select, rangeModifier, toggleModifier);
  367. return true;
  368. }
  369. return false;
  370. }
  371. /// <summary>
  372. /// Gets the item at the specified index in a collection.
  373. /// </summary>
  374. /// <param name="items">The collection.</param>
  375. /// <param name="index">The index.</param>
  376. /// <returns>The index of the item or -1 if the item was not found.</returns>
  377. private static object ElementAt(IEnumerable items, int index)
  378. {
  379. var typedItems = items?.Cast<object>();
  380. if (index != -1 && typedItems != null && index < typedItems.Count())
  381. {
  382. return typedItems.ElementAt(index) ?? null;
  383. }
  384. else
  385. {
  386. return null;
  387. }
  388. }
  389. /// <summary>
  390. /// Gets the index of an item in a collection.
  391. /// </summary>
  392. /// <param name="items">The collection.</param>
  393. /// <param name="item">The item.</param>
  394. /// <returns>The index of the item or -1 if the item was not found.</returns>
  395. private static int IndexOf(IEnumerable items, object item)
  396. {
  397. if (items != null && item != null)
  398. {
  399. var list = items as IList;
  400. if (list != null)
  401. {
  402. return list.IndexOf(item);
  403. }
  404. else
  405. {
  406. int index = 0;
  407. foreach (var i in items)
  408. {
  409. if (Equals(i, item))
  410. {
  411. return index;
  412. }
  413. ++index;
  414. }
  415. }
  416. }
  417. return -1;
  418. }
  419. /// <summary>
  420. /// Gets a range of items from an IEnumerable.
  421. /// </summary>
  422. /// <param name="items">The items.</param>
  423. /// <param name="first">The index of the first item.</param>
  424. /// <param name="last">The index of the last item.</param>
  425. /// <returns>The items.</returns>
  426. private static IEnumerable<object> GetRange(IEnumerable items, int first, int last)
  427. {
  428. var list = (items as IList) ?? items.Cast<object>().ToList();
  429. int step = first > last ? -1 : 1;
  430. for (int i = first; i != last; i += step)
  431. {
  432. yield return list[i];
  433. }
  434. yield return list[last];
  435. }
  436. /// <summary>
  437. /// Makes a list of objects equal another.
  438. /// </summary>
  439. /// <param name="items">The items collection.</param>
  440. /// <param name="desired">The desired items.</param>
  441. private static void SynchronizeItems(IList items, IEnumerable<object> desired)
  442. {
  443. int index = 0;
  444. foreach (var i in desired)
  445. {
  446. if (index < items.Count)
  447. {
  448. if (items[index] != i)
  449. {
  450. items[index] = i;
  451. }
  452. }
  453. else
  454. {
  455. items.Add(i);
  456. }
  457. ++index;
  458. }
  459. while (index < items.Count)
  460. {
  461. items.RemoveAt(items.Count - 1);
  462. }
  463. }
  464. /// <summary>
  465. /// Called when new containers are initialized by the <see cref="ItemContainerGenerator"/>.
  466. /// </summary>
  467. /// <param name="containers">The containers.</param>
  468. private void ContainersInitialized(ItemContainers containers)
  469. {
  470. var selectedIndex = SelectedIndex;
  471. var selectedContainer = containers.Items.OfType<ISelectable>().FirstOrDefault(x => x.IsSelected);
  472. if (selectedContainer != null)
  473. {
  474. SelectedIndex = containers.Items.IndexOf((IControl)selectedContainer) + containers.StartingIndex;
  475. }
  476. else if (selectedIndex >= containers.StartingIndex &&
  477. selectedIndex < containers.StartingIndex + containers.Items.Count)
  478. {
  479. var container = containers.Items[selectedIndex - containers.StartingIndex];
  480. MarkContainerSelected(container, true);
  481. }
  482. }
  483. /// <summary>
  484. /// Called when a container raises the <see cref="IsSelectedChangedEvent"/>.
  485. /// </summary>
  486. /// <param name="e">The event.</param>
  487. private void ContainerSelectionChanged(RoutedEventArgs e)
  488. {
  489. if (!_ignoreContainerSelectionChanged)
  490. {
  491. var selectable = (ISelectable)e.Source;
  492. if (selectable != null)
  493. {
  494. UpdateSelectionFromEventSource(e.Source, selectable.IsSelected);
  495. }
  496. }
  497. }
  498. /// <summary>
  499. /// Called when the currently selected item is lost and the selection must be changed
  500. /// depending on the <see cref="SelectionMode"/> property.
  501. /// </summary>
  502. private void LostSelection()
  503. {
  504. var items = Items?.Cast<object>();
  505. if (items != null && AlwaysSelected)
  506. {
  507. var index = Math.Min(SelectedIndex, items.Count() - 1);
  508. if (index > -1)
  509. {
  510. SelectedItem = items.ElementAt(index);
  511. return;
  512. }
  513. }
  514. SelectedIndex = -1;
  515. }
  516. /// <summary>
  517. /// Sets a container's 'selected' class or <see cref="ISelectable.IsSelected"/>.
  518. /// </summary>
  519. /// <param name="container">The container.</param>
  520. /// <param name="selected">Whether the control is selected</param>
  521. private void MarkContainerSelected(IControl container, bool selected)
  522. {
  523. try
  524. {
  525. var selectable = container as ISelectable;
  526. var styleable = container as IStyleable;
  527. _ignoreContainerSelectionChanged = true;
  528. if (selectable != null)
  529. {
  530. selectable.IsSelected = selected;
  531. }
  532. else if (styleable != null)
  533. {
  534. if (selected)
  535. {
  536. styleable.Classes.Add(":selected");
  537. }
  538. else
  539. {
  540. styleable.Classes.Remove(":selected");
  541. }
  542. }
  543. }
  544. finally
  545. {
  546. _ignoreContainerSelectionChanged = false;
  547. }
  548. }
  549. /// <summary>
  550. /// Sets an item container's 'selected' class or <see cref="ISelectable.IsSelected"/>.
  551. /// </summary>
  552. /// <param name="index">The index of the item.</param>
  553. /// <param name="selected">Whether the item should be selected or deselected.</param>
  554. private void MarkItemSelected(int index, bool selected)
  555. {
  556. var container = ItemContainerGenerator.ContainerFromIndex(index);
  557. if (container != null)
  558. {
  559. MarkContainerSelected(container, selected);
  560. }
  561. }
  562. /// <summary>
  563. /// Sets an item container's 'selected' class or <see cref="ISelectable.IsSelected"/>.
  564. /// </summary>
  565. /// <param name="item">The item.</param>
  566. /// <param name="selected">Whether the item should be selected or deselected.</param>
  567. private void MarkItemSelected(object item, bool selected)
  568. {
  569. var index = IndexOf(Items, item);
  570. if (index != -1)
  571. {
  572. MarkItemSelected(index, selected);
  573. }
  574. }
  575. /// <summary>
  576. /// Called when the <see cref="SelectedItems"/> CollectionChanged event is raised.
  577. /// </summary>
  578. /// <param name="sender">The event sender.</param>
  579. /// <param name="e">The event args.</param>
  580. private void SelectedItemsCollectionChanged(object sender, NotifyCollectionChangedEventArgs e)
  581. {
  582. switch (e.Action)
  583. {
  584. case NotifyCollectionChangedAction.Add:
  585. SelectedItemsAdded(e.NewItems.Cast<object>().ToList());
  586. break;
  587. case NotifyCollectionChangedAction.Remove:
  588. if (SelectedItems.Count == 0)
  589. {
  590. SelectedIndex = -1;
  591. }
  592. else
  593. {
  594. foreach (var item in e.OldItems)
  595. {
  596. MarkItemSelected(item, false);
  597. }
  598. }
  599. break;
  600. case NotifyCollectionChangedAction.Reset:
  601. foreach (var item in ItemContainerGenerator.Containers)
  602. {
  603. MarkContainerSelected(item, false);
  604. }
  605. SelectedIndex = -1;
  606. SelectedItemsAdded(SelectedItems);
  607. break;
  608. case NotifyCollectionChangedAction.Replace:
  609. foreach (var item in e.OldItems)
  610. {
  611. MarkItemSelected(item, false);
  612. }
  613. foreach (var item in e.NewItems)
  614. {
  615. MarkItemSelected(item, true);
  616. }
  617. if (SelectedItem != SelectedItems[0])
  618. {
  619. var oldItem = SelectedItem;
  620. var oldIndex = SelectedIndex;
  621. var item = SelectedItems[0];
  622. var index = IndexOf(Items, item);
  623. _selectedIndex = index;
  624. _selectedItem = item;
  625. RaisePropertyChanged(SelectedIndexProperty, oldIndex, index, BindingPriority.LocalValue);
  626. RaisePropertyChanged(SelectedItemProperty, oldItem, item, BindingPriority.LocalValue);
  627. }
  628. break;
  629. }
  630. }
  631. /// <summary>
  632. /// Called when items are added to the <see cref="SelectedItems"/> collection.
  633. /// </summary>
  634. /// <param name="items">The added items.</param>
  635. private void SelectedItemsAdded(IList items)
  636. {
  637. if (items.Count > 0)
  638. {
  639. foreach (var item in items)
  640. {
  641. MarkItemSelected(item, true);
  642. }
  643. if (SelectedItem == null)
  644. {
  645. var index = IndexOf(Items, items[0]);
  646. if (index != -1)
  647. {
  648. _selectedItem = items[0];
  649. _selectedIndex = index;
  650. RaisePropertyChanged(SelectedIndexProperty, -1, index, BindingPriority.LocalValue);
  651. RaisePropertyChanged(SelectedItemProperty, null, items[0], BindingPriority.LocalValue);
  652. }
  653. }
  654. }
  655. }
  656. /// <summary>
  657. /// Subscribes to the <see cref="SelectedItems"/> CollectionChanged event, if any.
  658. /// </summary>
  659. private void SubscribeToSelectedItems()
  660. {
  661. var incc = _selectedItems as INotifyCollectionChanged;
  662. if (incc != null)
  663. {
  664. incc.CollectionChanged += SelectedItemsCollectionChanged;
  665. }
  666. SelectedItemsCollectionChanged(
  667. _selectedItems,
  668. new NotifyCollectionChangedEventArgs(NotifyCollectionChangedAction.Reset));
  669. }
  670. /// <summary>
  671. /// Unsubscribes from the <see cref="SelectedItems"/> CollectionChanged event, if any.
  672. /// </summary>
  673. private void UnsubscribeFromSelectedItems()
  674. {
  675. var incc = _selectedItems as INotifyCollectionChanged;
  676. if (incc != null)
  677. {
  678. incc.CollectionChanged -= SelectedItemsCollectionChanged;
  679. }
  680. }
  681. }
  682. }