PageRenderTime 230ms CodeModel.GetById 17ms RepoModel.GetById 0ms app.codeStats 1ms

/wp-toolkit/Microsoft.Phone.Controls.Toolkit/LongListSelector/LongListSelector.cs

https://bitbucket.org/jeremejevs/milk-manager
C# | 1778 lines | 1112 code | 239 blank | 427 comment | 272 complexity | 29c7b77772068135a0cf8a5fee91be21 MD5 | raw file

Large files files are truncated, but you can click here to view the full file

  1. // (c) Copyright Microsoft Corporation.
  2. // This source is subject to the Microsoft Public License (Ms-PL).
  3. // Please see http://go.microsoft.com/fwlink/?LinkID=131993 for details.
  4. // All other rights reserved.
  5. using System;
  6. using System.Collections;
  7. using System.Collections.Generic;
  8. using System.Collections.ObjectModel;
  9. using System.Collections.Specialized;
  10. using System.Diagnostics.CodeAnalysis;
  11. using System.Linq;
  12. using System.Windows;
  13. using System.Windows.Controls;
  14. using System.Windows.Media;
  15. using Microsoft.Phone.Controls.Primitives;
  16. namespace Microsoft.Phone.Controls
  17. {
  18. /// <summary>
  19. /// Represents a virtualizing list designed for grouped lists. Can also be
  20. /// used with flat lists.
  21. /// </summary>
  22. /// <QualityBand>Preview</QualityBand>
  23. [SuppressMessage("Microsoft.Maintainability", "CA1506:AvoidExcessiveClassCoupling", Justification = "LongListSelector is a complicated control.")]
  24. [TemplatePart(Name = TemplatedListBoxName, Type = typeof(TemplatedListBox))]
  25. public partial class LongListSelector : Control
  26. {
  27. #region Constants
  28. /// <summary>
  29. /// The templated list box name.
  30. /// </summary>
  31. private const string TemplatedListBoxName = "TemplatedListBox";
  32. /// <summary>
  33. /// This constant is not actively used in the new version, however, the
  34. /// value is exposed through a deprecated property. For backward-
  35. /// compatibility only.
  36. /// </summary>
  37. private const double BufferSizeDefault = 1.0;
  38. /// <summary>
  39. /// The Scrolling state name.
  40. /// </summary>
  41. private const string ScrollingState = "Scrolling";
  42. /// <summary>
  43. /// The NotScrolling state name.
  44. /// </summary>
  45. private const string NotScrollingState = "NotScrolling";
  46. /// <summary>
  47. /// The vertical compression top state name.
  48. /// </summary>
  49. private const string CompressionTop = "CompressionTop";
  50. /// <summary>
  51. /// The vertical compression bottom state name.
  52. /// </summary>
  53. private const string CompressionBottom = "CompressionBottom";
  54. /// <summary>
  55. /// The absense of vertical compression state name.
  56. /// </summary>
  57. private const string NoVerticalCompression = "NoVerticalCompression";
  58. /// <summary>
  59. /// The vertical compression state name.
  60. /// </summary>
  61. private const string VerticalCompressionStateName = "VerticalCompression";
  62. /// <summary>
  63. /// The name of the scroll states visual state group.
  64. /// </summary>
  65. private const string ScrollStatesGroupName = "ScrollStates";
  66. #endregion
  67. /// <summary>
  68. /// Reference to the ListBox hosted in this control.
  69. /// </summary>
  70. private TemplatedListBox _listBox;
  71. /// <summary>
  72. /// Reference to the visual state group for scrolling.
  73. /// </summary>
  74. private VisualStateGroup _scrollGroup;
  75. /// <summary>
  76. /// Reference to the visual state group for vertical compression.
  77. /// </summary>
  78. private VisualStateGroup _verticalCompressionGroup;
  79. /// <summary>
  80. /// // Used to listen for changes in the ItemsSource
  81. /// (_rootCollection = ItemsSource as INotifyCollectionChanged).
  82. /// </summary>
  83. private INotifyCollectionChanged _rootCollection;
  84. /// <summary>
  85. /// Used to listen for changes in the groups within ItemsSource.
  86. /// </summary>
  87. private List<INotifyCollectionChanged> _groupCollections = new List<INotifyCollectionChanged>();
  88. /// <summary>
  89. /// Indicates the LLS is in the process of setting the SelectedItem back to its old value.
  90. /// </summary>
  91. private bool _isResettingSelectedItem = false;
  92. #region Properties
  93. /// <summary>
  94. /// Gets or sets whether the list is flat instead of a group hierarchy.
  95. /// </summary>
  96. public bool IsFlatList { get; set; }
  97. /// <summary>
  98. /// Gets or sets whether the list can be (temporarily) scrolled off of the ends.
  99. /// </summary>
  100. [SuppressMessage("Microsoft.Performance", "CA1822:MarkMembersAsStatic", Justification = "Backward compatible public setter.")]
  101. [SuppressMessage("Microsoft.Usage", "CA1801:ReviewUnusedParameters", MessageId = "value", Justification = "Backward compatible public setter.")]
  102. [Obsolete("IsBouncy is always set to true.")]
  103. public bool IsBouncy
  104. {
  105. get { return true; }
  106. set { }
  107. }
  108. /// <summary>
  109. /// Gets whether a list header is shown.
  110. /// </summary>
  111. private bool HasListHeader { get { return ListHeaderTemplate != null || ListHeader is UIElement; } }
  112. /// <summary>
  113. /// Gets whether a list footer is shown.
  114. /// </summary>
  115. private bool HasListFooter { get { return ListFooterTemplate != null || ListFooter is UIElement; } }
  116. /// <summary>
  117. /// Gets whether or not the user is manipulating the list, or if an inertial animation is taking place.
  118. /// </summary>
  119. public bool IsScrolling
  120. {
  121. get;
  122. private set;
  123. }
  124. /// <summary>
  125. /// Gets whether or not stretching is taking place.
  126. /// </summary>
  127. public bool IsStretching
  128. {
  129. get;
  130. private set;
  131. }
  132. #endregion
  133. #region Dependency Properties
  134. #region ItemsSource DependencyProperty
  135. /// <summary>
  136. /// The DataSource property. Where all of the items come from.
  137. /// </summary>
  138. public IEnumerable ItemsSource
  139. {
  140. get { return (IEnumerable)GetValue(ItemsSourceProperty); }
  141. set { SetValue(ItemsSourceProperty, value); }
  142. }
  143. /// <summary>
  144. /// The DataSource DependencyProperty.
  145. /// </summary>
  146. public static readonly DependencyProperty ItemsSourceProperty =
  147. DependencyProperty.Register("ItemsSource", typeof(IEnumerable), typeof(LongListSelector), new PropertyMetadata(null, OnItemsSourceChanged));
  148. private static void OnItemsSourceChanged(DependencyObject obj, DependencyPropertyChangedEventArgs e)
  149. {
  150. ((LongListSelector)obj).OnItemsSourceChanged();
  151. }
  152. #endregion
  153. #region SelectedItem DependencyProperty
  154. /// <summary>
  155. /// Gets or sets the selected item.
  156. /// </summary>
  157. public object SelectedItem
  158. {
  159. get { return GetValue(SelectedItemProperty); }
  160. set { SetValue(SelectedItemProperty, value); }
  161. }
  162. /// <summary>
  163. /// The SelectedItem DependencyProperty.
  164. /// </summary>
  165. public static readonly DependencyProperty SelectedItemProperty =
  166. DependencyProperty.Register("SelectedItem", typeof(object), typeof(LongListSelector), new PropertyMetadata(null, OnSelectedItemChanged));
  167. /// <summary>
  168. /// Called when the selected item changes
  169. /// </summary>
  170. private static void OnSelectedItemChanged(DependencyObject obj, DependencyPropertyChangedEventArgs e)
  171. {
  172. var lls = ((LongListSelector)obj);
  173. bool shouldFireSelectionChanged = !lls._isResettingSelectedItem;
  174. // Set the selected item of the ListBox if necessary
  175. if (lls._listBox != null)
  176. {
  177. LongListSelectorItem selectedListBoxItem = lls._listBox.SelectedItem as LongListSelectorItem;
  178. if (e.NewValue == null)
  179. {
  180. lls._listBox.SelectedItem = null;
  181. }
  182. else if (selectedListBoxItem == null || e.NewValue != selectedListBoxItem.Item || selectedListBoxItem.ItemType != LongListSelectorItemType.Item)
  183. {
  184. bool itemFound = false;
  185. // Find the corresponding LLS item in the listbox.
  186. foreach (LongListSelectorItem item in lls._listBox.Items)
  187. {
  188. if (item.ItemType == LongListSelectorItemType.Item && item.Item == e.NewValue)
  189. {
  190. lls._listBox.SelectedItem = item;
  191. itemFound = true;
  192. break;
  193. }
  194. }
  195. // If the item doesn't exist in the list, select null
  196. if (!itemFound)
  197. {
  198. lls._isResettingSelectedItem = true;
  199. try
  200. {
  201. lls.SelectedItem = null;
  202. }
  203. finally
  204. {
  205. lls._isResettingSelectedItem = false;
  206. }
  207. }
  208. }
  209. }
  210. // Fire the SelectionChanged event
  211. if (shouldFireSelectionChanged && e.OldValue != lls.SelectedItem)
  212. {
  213. var handler = lls.SelectionChanged;
  214. if (handler != null)
  215. {
  216. var added = new List<object>();
  217. var removed = new List<object>();
  218. if (e.OldValue != null)
  219. {
  220. removed.Add(e.OldValue);
  221. }
  222. if (lls.SelectedItem != null)
  223. {
  224. added.Add(lls.SelectedItem);
  225. }
  226. handler(obj, new SelectionChangedEventArgs(removed, added));
  227. }
  228. }
  229. }
  230. #endregion
  231. #region ListHeader DependencyProperty
  232. /// <summary>
  233. /// The ListHeader property. Will be used as the first scrollItem in the list.
  234. /// </summary>
  235. public object ListHeader
  236. {
  237. get { return (object)GetValue(ListHeaderProperty); }
  238. set { SetValue(ListHeaderProperty, value); }
  239. }
  240. /// <summary>
  241. /// The ListHeader DependencyProperty.
  242. /// </summary>
  243. public static readonly DependencyProperty ListHeaderProperty =
  244. DependencyProperty.Register("ListHeader", typeof(object), typeof(LongListSelector), new PropertyMetadata(null));
  245. #endregion
  246. #region ListHeaderTemplate DependencyProperty
  247. /// <summary>
  248. /// The ListHeaderTemplate provides the template for the ListHeader.
  249. /// </summary>
  250. public DataTemplate ListHeaderTemplate
  251. {
  252. get { return (DataTemplate)GetValue(ListHeaderTemplateProperty); }
  253. set { SetValue(ListHeaderTemplateProperty, value); }
  254. }
  255. /// <summary>
  256. /// The ListHeaderTemplate DependencyProperty.
  257. /// </summary>
  258. public static readonly DependencyProperty ListHeaderTemplateProperty =
  259. DependencyProperty.Register("ListHeaderTemplate", typeof(DataTemplate), typeof(LongListSelector), new PropertyMetadata(null, OnDataTemplateChanged));
  260. #endregion
  261. #region ListFooter DependencyProperty
  262. /// <summary>
  263. /// The ListFooter property. Will be used as the first scrollItem in the list.
  264. /// </summary>
  265. public object ListFooter
  266. {
  267. get { return (object)GetValue(ListFooterProperty); }
  268. set { SetValue(ListFooterProperty, value); }
  269. }
  270. /// <summary>
  271. /// The ListFooter DependencyProperty.
  272. /// </summary>
  273. public static readonly DependencyProperty ListFooterProperty =
  274. DependencyProperty.Register("ListFooter", typeof(object), typeof(LongListSelector), new PropertyMetadata(null));
  275. #endregion
  276. #region ListFooterTemplate DependencyProperty
  277. /// <summary>
  278. /// The ListFooterTemplate provides the template for the ListFooter.
  279. /// </summary>
  280. public DataTemplate ListFooterTemplate
  281. {
  282. get { return (DataTemplate)GetValue(ListFooterTemplateProperty); }
  283. set { SetValue(ListFooterTemplateProperty, value); }
  284. }
  285. /// <summary>
  286. /// The ListFooterTemplate DependencyProperty.
  287. /// </summary>
  288. public static readonly DependencyProperty ListFooterTemplateProperty =
  289. DependencyProperty.Register("ListFooterTemplate", typeof(DataTemplate), typeof(LongListSelector), new PropertyMetadata(null, OnDataTemplateChanged));
  290. #endregion
  291. #region GroupHeaderTemplate DependencyProperty
  292. /// <summary>
  293. /// The GroupHeaderTemplate provides the template for the groups in the items view.
  294. /// </summary>
  295. public DataTemplate GroupHeaderTemplate
  296. {
  297. get { return (DataTemplate)GetValue(GroupHeaderProperty); }
  298. set { SetValue(GroupHeaderProperty, value); }
  299. }
  300. /// <summary>
  301. /// The GroupHeaderTemplate DependencyProperty.
  302. /// </summary>
  303. public static readonly DependencyProperty GroupHeaderProperty =
  304. DependencyProperty.Register("GroupHeaderTemplate", typeof(DataTemplate), typeof(LongListSelector), new PropertyMetadata(null, OnDataTemplateChanged));
  305. #endregion
  306. #region GroupFooterTemplate DependencyProperty
  307. /// <summary>
  308. /// The GroupFooterTemplate provides the template for the groups in the items view.
  309. /// </summary>
  310. public DataTemplate GroupFooterTemplate
  311. {
  312. get { return (DataTemplate)GetValue(GroupFooterProperty); }
  313. set { SetValue(GroupFooterProperty, value); }
  314. }
  315. /// <summary>
  316. /// The GroupFooterTemplate DependencyProperty.
  317. /// </summary>
  318. public static readonly DependencyProperty GroupFooterProperty =
  319. DependencyProperty.Register("GroupFooterTemplate", typeof(DataTemplate), typeof(LongListSelector), new PropertyMetadata(null, OnDataTemplateChanged));
  320. #endregion
  321. #region ItemTemplate DependencyProperty
  322. /// <summary>
  323. /// The ItemTemplate provides the template for the items in the items view.
  324. /// </summary>
  325. public DataTemplate ItemTemplate
  326. {
  327. get { return (DataTemplate)GetValue(ItemsTemplateProperty); }
  328. set { SetValue(ItemsTemplateProperty, value); }
  329. }
  330. /// <summary>
  331. /// The ItemTemplate DependencyProperty.
  332. /// </summary>
  333. public static readonly DependencyProperty ItemsTemplateProperty =
  334. DependencyProperty.Register("ItemTemplate", typeof(DataTemplate), typeof(LongListSelector), new PropertyMetadata(null, OnDataTemplateChanged));
  335. #endregion
  336. #region DisplayAllGroups DependencyProperty
  337. /// <summary>
  338. /// Display all groups whether or not they have items.
  339. /// </summary>
  340. public bool DisplayAllGroups
  341. {
  342. get { return (bool)GetValue(DisplayAllGroupsProperty); }
  343. set { SetValue(DisplayAllGroupsProperty, value); }
  344. }
  345. /// <summary>
  346. /// DisplayAllGroups DependencyProperty
  347. /// </summary>
  348. public static readonly DependencyProperty DisplayAllGroupsProperty =
  349. DependencyProperty.Register("DisplayAllGroups", typeof(bool), typeof(LongListSelector), new PropertyMetadata(false, OnDisplayAllGroupsChanged));
  350. #endregion
  351. #region GroupItemTemplate DependencyProperty
  352. /// <summary>
  353. /// The GroupItemTemplate specifies the template that will be used in group view mode.
  354. /// </summary>
  355. public DataTemplate GroupItemTemplate
  356. {
  357. get { return (DataTemplate)GetValue(GroupItemTemplateProperty); }
  358. set { SetValue(GroupItemTemplateProperty, value); }
  359. }
  360. /// <summary>
  361. /// The GroupItemTemplate DependencyProperty.
  362. /// </summary>
  363. public static readonly DependencyProperty GroupItemTemplateProperty =
  364. DependencyProperty.Register("GroupItemTemplate", typeof(DataTemplate), typeof(LongListSelector), new PropertyMetadata(null));
  365. #endregion
  366. #region GroupItemsPanel DependencyProperty
  367. /// <summary>
  368. /// The GroupItemsPanel specifies the panel that will be used in group view mode.
  369. /// </summary>
  370. public ItemsPanelTemplate GroupItemsPanel
  371. {
  372. get { return (ItemsPanelTemplate)GetValue(GroupItemsPanelProperty); }
  373. set { SetValue(GroupItemsPanelProperty, value); }
  374. }
  375. /// <summary>
  376. /// The GroupItemsPanel DependencyProperty.
  377. /// </summary>
  378. public static readonly DependencyProperty GroupItemsPanelProperty =
  379. DependencyProperty.Register("GroupItemsPanel", typeof(ItemsPanelTemplate), typeof(LongListSelector), new PropertyMetadata(null));
  380. #endregion
  381. #region BufferSize DependencyProperty
  382. /// <summary>
  383. /// The number of "screens" (as defined by the ActualHeight of the LongListSelector) above and below the visible
  384. /// items of the list that will be filled with items.
  385. /// </summary>
  386. [Obsolete("BufferSize no longer affect items virtualization")]
  387. public double BufferSize
  388. {
  389. get { return (double)GetValue(BufferSizeProperty); }
  390. set { SetValue(BufferSizeProperty, value); }
  391. }
  392. /// <summary>
  393. /// The BufferSize DependencyProperty
  394. /// </summary>
  395. [Obsolete("BufferSizeProperty no longer affect items virtualization")]
  396. public static readonly DependencyProperty BufferSizeProperty =
  397. DependencyProperty.Register("BufferSize", typeof(double), typeof(LongListSelector), new PropertyMetadata(BufferSizeDefault, OnBufferSizeChanged));
  398. [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Usage", "CA2208:InstantiateArgumentExceptionsCorrectly")]
  399. private static void OnBufferSizeChanged(DependencyObject obj, DependencyPropertyChangedEventArgs e)
  400. {
  401. double newValue = (double)e.NewValue;
  402. if (newValue < 0)
  403. {
  404. throw new ArgumentOutOfRangeException("BufferSize");
  405. }
  406. }
  407. #endregion
  408. #region MaximumFlickVelocity DependencyProperty
  409. /// <summary>
  410. /// The maximum velocity for flicks, in pixels per second.
  411. /// </summary>
  412. [Obsolete("MaximumFlickVelocity is not used anymore.")]
  413. public double MaximumFlickVelocity
  414. {
  415. get { return (double)GetValue(MaximumFlickVelocityProperty); }
  416. set { SetValue(MaximumFlickVelocityProperty, value); }
  417. }
  418. /// <summary>
  419. /// The MaximumFlickVelocity DependencyProperty.
  420. /// </summary>
  421. [Obsolete("MaximumFlickVelocityProperty is not used anymore.")]
  422. public static readonly DependencyProperty MaximumFlickVelocityProperty =
  423. DependencyProperty.Register("MaximumFlickVelocity", typeof(double), typeof(LongListSelector), new PropertyMetadata(MotionParameters.MaximumSpeed));
  424. #endregion
  425. #region ShowListHeader DependencyProperty
  426. /// <summary>
  427. /// Controls whether or not the ListHeader is shown.
  428. /// </summary>
  429. public bool ShowListHeader
  430. {
  431. get { return (bool)GetValue(ShowListHeaderProperty); }
  432. set { SetValue(ShowListHeaderProperty, value); }
  433. }
  434. /// <summary>
  435. /// The ShowListHeader DependencyProperty.
  436. /// </summary>
  437. public static readonly DependencyProperty ShowListHeaderProperty =
  438. DependencyProperty.Register("ShowListHeader", typeof(bool), typeof(LongListSelector), new PropertyMetadata(true, OnShowListHeaderChanged));
  439. private static void OnShowListHeaderChanged(DependencyObject obj, DependencyPropertyChangedEventArgs e)
  440. {
  441. LongListSelector control = (LongListSelector)obj;
  442. if (control._listBox != null)
  443. {
  444. Collection<LongListSelectorItem> tuples = (Collection<LongListSelectorItem>)control._listBox.ItemsSource;
  445. if (control.ShowListHeader)
  446. {
  447. control.AddListHeader(tuples);
  448. }
  449. else
  450. {
  451. RemoveListHeader(tuples);
  452. }
  453. }
  454. }
  455. #endregion
  456. #region ShowListFooter DependencyProperty
  457. /// <summary>
  458. /// Controls whether or not the ListFooter is shown.
  459. /// </summary>
  460. public bool ShowListFooter
  461. {
  462. get { return (bool)GetValue(ShowListFooterProperty); }
  463. set { SetValue(ShowListFooterProperty, value); }
  464. }
  465. /// <summary>
  466. /// The ShowListFooter DependencyProperty.
  467. /// </summary>
  468. public static readonly DependencyProperty ShowListFooterProperty =
  469. DependencyProperty.Register("ShowListFooter", typeof(bool), typeof(LongListSelector), new PropertyMetadata(true, OnShowListFooterChanged));
  470. private static void OnShowListFooterChanged(DependencyObject obj, DependencyPropertyChangedEventArgs e)
  471. {
  472. LongListSelector control = (LongListSelector)obj;
  473. if (control._listBox != null)
  474. {
  475. Collection<LongListSelectorItem> tuples = (Collection<LongListSelectorItem>)control._listBox.ItemsSource;
  476. if (control.ShowListFooter)
  477. {
  478. control.AddListFooter(tuples);
  479. }
  480. else
  481. {
  482. RemoveListFooter(tuples);
  483. }
  484. }
  485. }
  486. #endregion
  487. #endregion
  488. #region Events
  489. /// <summary>
  490. /// Occurs when the currently selected item changes.
  491. /// </summary>
  492. public event SelectionChangedEventHandler SelectionChanged;
  493. /// <summary>
  494. /// Occurs when this control starts scrolling.
  495. /// </summary>
  496. public event EventHandler ScrollingStarted;
  497. /// <summary>
  498. /// Occurs when this control stops scrolling.
  499. /// </summary>
  500. public event EventHandler ScrollingCompleted;
  501. /// <summary>
  502. /// Occurs when the group Popup's IsOpen has been set to true.
  503. /// </summary>
  504. public event EventHandler<GroupViewOpenedEventArgs> GroupViewOpened;
  505. /// <summary>
  506. /// Occurs when the group popup is about to close.
  507. /// </summary>
  508. public event EventHandler<GroupViewClosingEventArgs> GroupViewClosing;
  509. /// <summary>
  510. /// Occurs when an item is about to be "realized".
  511. /// </summary>
  512. public event EventHandler<LinkUnlinkEventArgs> Link;
  513. /// <summary>
  514. /// Occurs when an item is about to be "un-realized".
  515. /// </summary>
  516. public event EventHandler<LinkUnlinkEventArgs> Unlink;
  517. /// <summary>
  518. /// Occurs when the user has dragged the items up from the bottom as far as they can go.
  519. /// </summary>
  520. public event EventHandler StretchingBottom;
  521. /// <summary>
  522. /// Occurs when the user is no longer stretching.
  523. /// </summary>
  524. public event EventHandler StretchingCompleted;
  525. /// <summary>
  526. /// Occurs when the user has dragged the items down from the top as far as they can go.
  527. /// </summary>
  528. public event EventHandler StretchingTop;
  529. #endregion
  530. #region Constructor
  531. /// <summary>
  532. /// Initializes a new instance of <see cref="LongListSelector"/>.
  533. /// </summary>
  534. public LongListSelector()
  535. {
  536. DefaultStyleKey = typeof(LongListSelector);
  537. }
  538. #endregion
  539. //
  540. // Public methods
  541. //
  542. #region ScrollTo(...)
  543. /// <summary>
  544. /// Instantly jump to the specified item.
  545. /// </summary>
  546. /// <param name="item">Item to jump to.</param>
  547. public void ScrollTo(object item)
  548. {
  549. if (_listBox != null && item != null)
  550. {
  551. ObservableCollection<LongListSelectorItem> tuples = (ObservableCollection<LongListSelectorItem>)_listBox.ItemsSource;
  552. LongListSelectorItem lastTuple = tuples[tuples.Count - 1];
  553. _listBox.ScrollIntoView(lastTuple);
  554. UpdateLayout();
  555. foreach (LongListSelectorItem tuple in _listBox.ItemsSource)
  556. {
  557. if (tuple.Item != null && tuple.Item.Equals(item))
  558. {
  559. _listBox.ScrollIntoView(tuple);
  560. break;
  561. }
  562. }
  563. }
  564. }
  565. #endregion
  566. #region ScrollToGroup(...)
  567. /// <summary>
  568. /// Instantly jump to the specified group.
  569. /// </summary>
  570. /// <param name="group">Group to jump to.</param>
  571. public void ScrollToGroup(object group)
  572. {
  573. ScrollTo(group);
  574. }
  575. #endregion
  576. #region DisplayGroupView()
  577. /// <summary>
  578. /// Invokes the group view if a GroupItemTemplate has been defined.
  579. /// </summary>
  580. public void DisplayGroupView()
  581. {
  582. if (GroupItemTemplate != null && !IsFlatList)
  583. {
  584. OpenPopup();
  585. }
  586. }
  587. #endregion
  588. #region CloseGroupView()
  589. /// <summary>
  590. /// Closes the group view unconditionally.
  591. /// </summary>
  592. /// <remarks>Does not raise the GroupViewClosingEventArgs event.</remarks>
  593. public void CloseGroupView()
  594. {
  595. ClosePopup(null, false);
  596. }
  597. #endregion
  598. // Obsolete:
  599. #region AnimateTo(...)
  600. /// <summary>
  601. /// Animate the scrolling of the list to the specified item.
  602. /// </summary>
  603. /// <param name="item">Item to scroll to.</param>
  604. [Obsolete("AnimateTo(...) call ScrollTo(...) to jump without animation to the given item.")]
  605. public void AnimateTo(object item)
  606. {
  607. ScrollTo(item);
  608. }
  609. #endregion
  610. /// <summary>
  611. /// Returns either containers or items for either all items with
  612. /// containers or just the visible ones, as specified by the
  613. /// parameters.
  614. /// </summary>
  615. /// <param name="onlyItemsInView">When true, will return values for
  616. /// only items that are in view.</param>
  617. /// <param name="getContainers">When true, will return the containers
  618. /// rather than the items.</param>
  619. /// <returns>Returns either containers or items for either all items
  620. /// with containers or just the visible ones, as specified by the
  621. /// parameters. </returns>
  622. [SuppressMessage("Microsoft.Usage", "CA1801:ReviewUnusedParameters", MessageId = "getContainers", Justification = "This is an obsolete old method that cannot change now.")]
  623. [SuppressMessage("Microsoft.Usage", "CA1801:ReviewUnusedParameters", MessageId = "onlyItemsInView", Justification = "This is an obsolete old method that cannot change now.")]
  624. [SuppressMessage("Microsoft.Performance", "CA1822:MarkMembersAsStatic", Justification = "This is an obsolete old method that cannot change now.")]
  625. [Obsolete("GetItemsWithContainers(...) always returns an empty collection of items. Rely on Link/Unlink to know an item get realized or unrealized.")]
  626. public ICollection<Object> GetItemsWithContainers(bool onlyItemsInView, bool getContainers)
  627. {
  628. return new Collection<Object>();
  629. }
  630. #region GetItemsInView()
  631. /// <summary>
  632. /// Returns all of the items that are currently in view.
  633. /// This is not the same as the items that have associated visual elements: there are usually some visuals offscreen.
  634. /// This might be an empty list if scrolling is happening too quickly.
  635. /// </summary>
  636. /// <returns>Returns all of the items that are currently in view.</returns>
  637. [Obsolete("GetItemsInView() always returns an empty collection of items. Rely on Link/Unlink to know an item get realized or unrealized.")]
  638. public ICollection<Object> GetItemsInView()
  639. {
  640. return GetItemsWithContainers(true, false);
  641. }
  642. #endregion
  643. #region OnItemsSourceChanged()
  644. /// <summary>
  645. /// Called when the ItemsSource dependency property changes.
  646. /// </summary>
  647. private void OnItemsSourceChanged()
  648. {
  649. // Reload the whole list.
  650. LoadDataIntoListBox();
  651. }
  652. #endregion
  653. #region OnApplyTemplate()
  654. /// <summary>
  655. /// Called whenever a template gets applied on this control.
  656. /// </summary>
  657. public override void OnApplyTemplate()
  658. {
  659. base.OnApplyTemplate();
  660. // Unsubscribe from events we registered for in the past.
  661. if (_listBox != null)
  662. {
  663. _listBox.SelectionChanged -= OnListBoxSelectionChanged;
  664. _listBox.Link -= OnLink;
  665. _listBox.Unlink -= OnUnlink;
  666. }
  667. if (_scrollGroup != null)
  668. {
  669. _scrollGroup.CurrentStateChanging -= OnScrollStateChanging;
  670. }
  671. // Locates and setup the TemplatedListBox in the template. If no TemplatedListBox is found, we
  672. // initialize one.
  673. _listBox = GetTemplateChild(TemplatedListBoxName) as TemplatedListBox ?? new TemplatedListBox();
  674. _listBox.ListHeaderTemplate = ListHeaderTemplate;
  675. _listBox.ListFooterTemplate = ListFooterTemplate;
  676. _listBox.GroupHeaderTemplate = GroupHeaderTemplate;
  677. _listBox.GroupFooterTemplate = GroupFooterTemplate;
  678. _listBox.ItemTemplate = ItemTemplate;
  679. _listBox.SelectionChanged += OnListBoxSelectionChanged;
  680. _listBox.Link += OnLink;
  681. _listBox.Unlink += OnUnlink;
  682. // Retrieves the ScrollViewer of the list box.
  683. ScrollViewer sv = _listBox.GetFirstLogicalChildByType<ScrollViewer>(true);
  684. if (sv != null)
  685. {
  686. // Visual States are always on the first child of the control template
  687. FrameworkElement element = VisualTreeHelper.GetChild(sv, 0) as FrameworkElement;
  688. if (element != null)
  689. {
  690. _scrollGroup = VisualStates.TryGetVisualStateGroup(sv, ScrollStatesGroupName);
  691. if (_scrollGroup != null)
  692. {
  693. _scrollGroup.CurrentStateChanging += OnScrollStateChanging;
  694. }
  695. _verticalCompressionGroup = VisualStates.TryGetVisualStateGroup(sv, VerticalCompressionStateName);
  696. if(_verticalCompressionGroup != null)
  697. {
  698. _verticalCompressionGroup.CurrentStateChanging += OnStretchStateChanging;
  699. }
  700. }
  701. }
  702. LoadDataIntoListBox();
  703. }
  704. #endregion
  705. #region LoadDataIntoListBox()
  706. /// <summary>
  707. /// Loads ItemsSource into the hosted list box.
  708. /// </summary>
  709. private void LoadDataIntoListBox()
  710. {
  711. if (_listBox != null)
  712. {
  713. ObservableCollection<LongListSelectorItem> tuples = new ObservableCollection<LongListSelectorItem>();
  714. AddListHeader(tuples);
  715. UnsubscribeFromAllCollections();
  716. // if it's a flat list, add the items without grouping.
  717. if (IsFlatList)
  718. {
  719. if (ItemsSource != null)
  720. {
  721. foreach (object item in ItemsSource)
  722. {
  723. tuples.Add(new LongListSelectorItem() { Item = item, ItemType = LongListSelectorItemType.Item });
  724. }
  725. }
  726. }
  727. // Otherwise, apply the grouping logic.
  728. else
  729. {
  730. IEnumerable groups = ItemsSource;
  731. if (groups != null)
  732. {
  733. foreach (object group in groups)
  734. {
  735. AddGroup(group, tuples);
  736. }
  737. }
  738. }
  739. AddListFooter(tuples);
  740. _rootCollection = ItemsSource as INotifyCollectionChanged;
  741. if (_rootCollection != null)
  742. {
  743. _rootCollection.CollectionChanged += OnCollectionChanged;
  744. }
  745. _listBox.ItemsSource = tuples;
  746. }
  747. }
  748. #endregion
  749. #region List/Footer Headers
  750. /// <summary>
  751. /// Adds a list header to the given list.
  752. /// </summary>
  753. private void AddListHeader(IList<LongListSelectorItem> tuples)
  754. {
  755. if (HasListHeader && ShowListHeader && // Adds the list header if it got a template or if it's a UI element itself.
  756. (tuples.Count == 0 || tuples[0].ItemType != LongListSelectorItemType.ListHeader)) // Also, make sure its not already there
  757. {
  758. tuples.Insert(0, new LongListSelectorItem() { Item = ListHeader, ItemType = LongListSelectorItemType.ListHeader });
  759. }
  760. }
  761. /// <summary>
  762. /// Adds a list header to the listbox.
  763. /// </summary>
  764. private void AddListHeader()
  765. {
  766. AddListHeader((ObservableCollection<LongListSelectorItem>)_listBox.ItemsSource);
  767. }
  768. /// <summary>
  769. /// Removes the list header from the given list.
  770. /// </summary>
  771. private static void RemoveListHeader(IList<LongListSelectorItem> tuples)
  772. {
  773. if (tuples.Count > 0 && tuples[0].ItemType == LongListSelectorItemType.ListHeader)
  774. {
  775. tuples.RemoveAt(0);
  776. }
  777. }
  778. /// <summary>
  779. /// Removes the list header from the listbox.
  780. /// </summary>
  781. private void RemoveListHeader()
  782. {
  783. RemoveListHeader((ObservableCollection<LongListSelectorItem>)_listBox.ItemsSource);
  784. }
  785. /// <summary>
  786. /// Adds a list footer to the given list.
  787. /// </summary>
  788. private void AddListFooter(IList<LongListSelectorItem> tuples)
  789. {
  790. if (HasListFooter && ShowListFooter && // Adds the list footer if it got a template or if it's a UI element itself.
  791. (tuples.Count == 0 || tuples[tuples.Count - 1].ItemType != LongListSelectorItemType.ListFooter)) // Also, make sure its not already there
  792. {
  793. tuples.Add(new LongListSelectorItem() { Item = ListFooter, ItemType = LongListSelectorItemType.ListFooter });
  794. }
  795. }
  796. /// <summary>
  797. /// Adds a list footer to the listbox.
  798. /// </summary>
  799. private void AddListFooter()
  800. {
  801. AddListFooter((ObservableCollection<LongListSelectorItem>)_listBox.ItemsSource);
  802. }
  803. /// <summary>
  804. /// Removes the list footer from the given list.
  805. /// </summary>
  806. private static void RemoveListFooter(IList<LongListSelectorItem> tuples)
  807. {
  808. LongListSelectorItem lastTuple = tuples[tuples.Count - 1];
  809. if (lastTuple != null && lastTuple.ItemType == LongListSelectorItemType.ListFooter)
  810. {
  811. tuples.RemoveAt(tuples.Count - 1);
  812. }
  813. }
  814. /// <summary>
  815. /// Removes the list footer from the listbox.
  816. /// </summary>
  817. private void RemoveListFooter()
  818. {
  819. RemoveListFooter((ObservableCollection<LongListSelectorItem>)_listBox.ItemsSource);
  820. }
  821. #endregion
  822. #region AddGroup(...)
  823. /// <summary>
  824. /// Adds a group to a list of tuples.
  825. /// </summary>
  826. /// <param name="groupToAdd">Group to add.</param>
  827. /// <param name="tuples">List to which the method will add the group.</param>
  828. private void AddGroup(object groupToAdd, IList tuples)
  829. {
  830. IEnumerable group = groupToAdd as IEnumerable;
  831. if (group != null)
  832. {
  833. bool groupHasItems = false;
  834. // Adds the group header
  835. if (GroupHeaderTemplate != null)
  836. {
  837. tuples.Add(new LongListSelectorItem() { Item = group, ItemType = LongListSelectorItemType.GroupHeader });
  838. }
  839. // For each group header, add its items
  840. foreach (object item in group)
  841. {
  842. tuples.Add(new LongListSelectorItem() { Item = item, ItemType = LongListSelectorItemType.Item, Group = group });
  843. groupHasItems = true;
  844. }
  845. // Add the group footer if the group has items or if we must display all groups whether or not they have items.
  846. if (groupHasItems || DisplayAllGroups)
  847. {
  848. if (GroupFooterTemplate != null)
  849. {
  850. tuples.Add(new LongListSelectorItem() { Item = group, ItemType = LongListSelectorItemType.GroupFooter });
  851. }
  852. }
  853. // Otherwise, remove the group header
  854. else if (GroupHeaderTemplate != null)
  855. {
  856. tuples.RemoveAt(tuples.Count - 1);
  857. }
  858. // Subscribe to collection change event
  859. INotifyCollectionChanged groupCollection = group as INotifyCollectionChanged;
  860. if (groupCollection != null && !_groupCollections.Contains(groupCollection))
  861. {
  862. groupCollection.CollectionChanged += OnCollectionChanged;
  863. _groupCollections.Add(groupCollection);
  864. }
  865. }
  866. }
  867. #endregion
  868. #region AddGroupHeadersAndFooters(...)
  869. /// <summary>
  870. /// Adds group headers or footers after their template switch from being null
  871. /// to an actual value.
  872. /// </summary>
  873. /// <param name="addHeaders">Specifies whether or not to add group headers.</param>
  874. /// <param name="addFooters">Specifies whether or not to add group footers.</param>
  875. /// <remarks>Used only when templates for group headers/footers switch from being null.
  876. /// In this case, they must be added to the lisbox if a group is not empty or DisplayAllGroups is true.
  877. /// For performance reasons, this method makes an assumption that headers/footers are not already present.
  878. /// Which is the case when its called from OnDataTemplateChanged.</remarks>
  879. private void AddGroupHeadersAndFooters(bool addHeaders, bool addFooters)
  880. {
  881. int indexInListBox = 0;
  882. if (HasListHeader && ShowListHeader)
  883. {
  884. ++indexInListBox;
  885. }
  886. IEnumerable groups = ItemsSource;
  887. ObservableCollection<LongListSelectorItem> tuples = (ObservableCollection<LongListSelectorItem>)_listBox.ItemsSource;
  888. if (groups != null)
  889. {
  890. foreach (object group in groups)
  891. {
  892. var groupAsEnumerable = group as IEnumerable;
  893. if (groupAsEnumerable != null)
  894. {
  895. int itemsCount = GetHeadersAndItemsCountFromGroup(groupAsEnumerable);
  896. // Adds the group header
  897. if (addHeaders && GroupHeaderTemplate != null && itemsCount > 0)
  898. {
  899. tuples.Insert(indexInListBox, new LongListSelectorItem
  900. {
  901. Item = group,
  902. ItemType = LongListSelectorItemType.GroupHeader
  903. });
  904. }
  905. indexInListBox += itemsCount;
  906. // Adds the group footer
  907. if (addFooters && GroupFooterTemplate != null && itemsCount > 0)
  908. {
  909. tuples.Insert(indexInListBox - 1, new LongListSelectorItem
  910. {
  911. Item = group,
  912. ItemType = LongListSelectorItemType.GroupFooter
  913. });
  914. }
  915. }
  916. }
  917. }
  918. }
  919. /// <summary>
  920. /// Adds group headers after the GroupHeaderTeamplate switch from being null
  921. /// to an actual value.
  922. /// </summary>
  923. private void AddGroupHeaders()
  924. {
  925. AddGroupHeadersAndFooters(true, false);
  926. }
  927. /// <summary>
  928. /// Adds group headers after the GroupFooterTeamplate switch from being null
  929. /// to an actual value.
  930. /// </summary>
  931. private void AddGroupFooters()
  932. {
  933. AddGroupHeadersAndFooters(false, true);
  934. }
  935. #endregion
  936. #region RemoveAllGroupHeadersAndFooters(...)
  937. /// <summary>
  938. /// Removes group headers or footers after their template becomes null.
  939. /// </summary>
  940. /// <param name="removeHeaders">Specifies whether or not to remove group headers.</param>
  941. /// <param name="removeFooters">Specifies whether or not to remove group footers.</param>
  942. private void RemoveAllGroupHeadersAndFooters(bool removeHeaders, bool removeFooters)
  943. {
  944. ObservableCollection<LongListSelectorItem> tuples = (ObservableCollection<LongListSelectorItem>)_listBox.ItemsSource;
  945. for (int i = 0; i < tuples.Count; ++i)
  946. {
  947. if ((removeHeaders && tuples[i].ItemType == LongListSelectorItemType.GroupHeader) ||
  948. (removeFooters && tuples[i].ItemType == LongListSelectorItemType.GroupFooter))
  949. {
  950. tuples.RemoveAt(i--); // the -- is there so we don't skip tuples
  951. }
  952. }
  953. }
  954. private void RemoveAllGroupHeaders()
  955. {
  956. RemoveAllGroupHeadersAndFooters(true, false);
  957. }
  958. private void RemoveAllGroupFooters()
  959. {
  960. RemoveAllGroupHeadersAndFooters(false, true);
  961. }
  962. #endregion
  963. #region UnsubscribeFromAllCollections()
  964. /// <summary>
  965. /// Unsubscrives from every collection in ItemsSource.
  966. /// </summary>
  967. private void UnsubscribeFromAllCollections()
  968. {
  969. if (_rootCollection != null)
  970. {
  971. _rootCollection.CollectionChanged -= OnCollectionChanged;
  972. }
  973. foreach (INotifyCollectionChanged collection in _groupCollections)
  974. {
  975. collection.CollectionChanged -= OnCollectionChanged;
  976. }
  977. _rootCollection = null;
  978. _groupCollections.Clear();
  979. }
  980. #endregion
  981. #region InsertNewGroups(...)
  982. /// <summary>
  983. /// Inserts new groups in the list box.
  984. /// </summary>
  985. /// <param name="newGroups">List of the new groups to insert.</param>
  986. /// <param name="newGroupsIndex">Insertion index relative to the collection.</param>
  987. private void InsertNewGroups(IList newGroups, int newGroupsIndex)
  988. {
  989. ObservableCollection<LongListSelectorItem> tuples = (ObservableCollection<LongListSelectorItem>)_listBox.ItemsSource;
  990. // 1 - Builds items tuples for the new groups
  991. List<LongListSelectorItem> newGroupsTuples = new List<LongListSelectorItem>();
  992. foreach (object group in newGroups)
  993. {
  994. AddGroup(group, newGroupsTuples);
  995. }
  996. if (newGroupsTuples.Count > 0)
  997. {
  998. // 2 - Finds insertion index in the list box
  999. int insertIndex = GetGroupIndexInListBox(newGroupsIndex);
  1000. // 3 - Inserts the new items into the list box
  1001. foreach (LongListSelectorItem tuple in newGroupsTuples)
  1002. {
  1003. tuples.Insert(insertIndex++, tuple);
  1004. }
  1005. }
  1006. }
  1007. #endregion
  1008. #region InsertNewItems(...)
  1009. /// <summary>
  1010. /// Inserts new items in the list box.
  1011. /// </summary>
  1012. /// <param name="newItems">List of new items to insert.</param>
  1013. /// <param name="newItemsIndex">Insertion index relative to the collection</param>
  1014. /// <param name="group">Group into which the items are inserted. Can be null if IsFlatList == true</param>
  1015. private void InsertNewItems(IList newItems, int newItemsIndex, IEnumerable group)
  1016. {
  1017. ObservableCollection<LongListSelectorItem> tuples = (ObservableCollection<LongListSelectorItem>)_listBox.ItemsSource;
  1018. // 1 - Builds items tuples for the new items
  1019. List<LongListSelectorItem> newItemsTuples = new List<LongListSelectorItem>();
  1020. foreach (object item in newItems)
  1021. {
  1022. newItemsTuples.Add(new LongListSelectorItem
  1023. {
  1024. Group = group,
  1025. Item = item,
  1026. ItemType = LongListSelectorItemType.Item
  1027. });
  1028. }
  1029. // 2 - Finds the insertion index in the listbox
  1030. // Since a single group might be referenced by more than one, we might need to update more than one spot in the listbox
  1031. if (group != null)
  1032. {
  1033. int i = 0;
  1034. bool groupWasNotDisplayed = ((IList)group).Count == newItems.Count && !DisplayAllGroups;
  1035. foreach (object g in ItemsSource)
  1036. {
  1037. if (g == group)
  1038. {
  1039. int insertIndex = GetGroupIndexInListBox(i);
  1040. if (GroupHeaderTemplate != null)
  1041. {
  1042. if (groupWasNotDisplayed)
  1043. {
  1044. tuples.Insert(insertIndex, new LongListSelectorItem() { ItemType = LongListSelectorItemType.GroupHeader, Item = group });
  1045. }
  1046. ++insertIndex;
  1047. }
  1048. insertIndex += newItemsIndex;
  1049. // 3 - Inserts the new items into the list box
  1050. foreach (LongListSelectorItem tuple in newItemsTuples)
  1051. {
  1052. tuples.Insert(insertIndex++, tuple);
  1053. }
  1054. if (groupWasNotDisplayed && GroupFooterTemplate != null)
  1055. {
  1056. tuples.Insert(insertIndex++, new LongListSelectorItem() { ItemType = LongListSelectorItemType.GroupFooter, Item = group });
  1057. }
  1058. }
  1059. ++i;
  1060. }
  1061. }
  1062. else
  1063. {
  1064. int insertIndex = newItemsIndex;
  1065. if (HasListHeader && ShowListHeader)
  1066. {
  1067. ++insertIndex;
  1068. }
  1069. // 3 - Inserts the new items into the list box
  1070. foreach (LongListSelectorItem tuple in newItemsTuples)
  1071. {
  1072. tuples.Insert(insertIndex++, tuple);
  1073. }
  1074. }
  1075. }
  1076. #endregion
  1077. #region RemoveOldGroups(...)
  1078. /// <summary>
  1079. /// Removes groups from the list box.
  1080. /// </summary>
  1081. /// <param name="oldGroups">List of groups to remove.</param>
  1082. /// <param name="oldGroupsIndex">Start index relative to the root collection.</param>
  1083. private void RemoveOldGroups(IList oldGroups, int oldGroupsIndex)
  1084. {
  1085. ObservableCollection<LongListSelectorItem> tuples = (ObservableCollection<LongListSelectorItem>)_listBox.ItemsSource;
  1086. // 1 - Find the index at whic…

Large files files are truncated, but you can click here to view the full file