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

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

https://bitbucket.org/jeremejevs/word-steps
C# | 2415 lines | 1731 code | 392 blank | 292 comment | 316 complexity | b51609aff59f4d21e3b91106666e38f2 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.Specialized;
  9. using System.Diagnostics;
  10. using System.Windows;
  11. using System.Windows.Controls;
  12. using System.Windows.Controls.Primitives;
  13. using System.Windows.Media;
  14. using System.Windows.Media.Animation;
  15. using System.Windows.Threading;
  16. namespace Microsoft.Phone.Controls
  17. {
  18. /// <summary>
  19. /// A virtualizing list designed for grouped lists. Can also be used with flat lists.
  20. /// </summary>
  21. [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Maintainability", "CA1506:AvoidExcessiveClassCoupling"), TemplatePart(Name = ItemsPanelName, Type = typeof(Panel))]
  22. [TemplatePart(Name = PanningTransformName, Type = typeof(TranslateTransform))]
  23. [TemplatePart(Name = VerticalScrollBarName, Type = typeof(ScrollBar))]
  24. public partial class LongListSelector : Control
  25. {
  26. // The names of the template parts
  27. private const string ItemsPanelName = "ItemsPanel";
  28. private const string PanningTransformName = "PanningTransform";
  29. private const string VerticalScrollBarName = "VerticalScrollBar";
  30. private Panel _itemsPanel;
  31. private TranslateTransform _panningTransform;
  32. private ScrollBar _verticalScrollbar;
  33. private Popup _groupSelectorPopup;
  34. private Storyboard _panelStoryboard;
  35. private DoubleAnimation _panelAnimation;
  36. private DateTime _gestureStart;
  37. // Timer that controls how long it takes before a flick will be stopped on a touch down
  38. private DispatcherTimer _stopTimer = new DispatcherTimer() { Interval = TimeSpan.FromMilliseconds(50) };
  39. private bool _ignoreNextTap;
  40. private bool _firstDragDuringFlick;
  41. private double _lastFlickVelocity;
  42. // Duration used for animations while panning, instead of matching finger position exactly
  43. private static readonly Duration _panDuration = new Duration(TimeSpan.FromMilliseconds(100));
  44. private readonly IEasingFunction _panEase = new ExponentialEase();
  45. // Time that dragging will wait before deciding that a flick is not happening
  46. private static readonly TimeSpan _flickStopWaitTime = TimeSpan.FromMilliseconds(20);
  47. private int _scrollingTowards = -1;
  48. private const double BufferSizeDefault = 1.0;
  49. private double _bufferSizeCache = BufferSizeDefault;
  50. private double _minimumPanelScroll = float.MinValue;
  51. private double _maximumPanelScroll = 0;
  52. private bool _isLoaded;
  53. private bool _isPanning;
  54. private bool _isFlicking;
  55. private bool _isStretching;
  56. private double _dragTarget;
  57. private bool _isAnimating;
  58. private Size _availableSize;
  59. private Stack<ContentPresenter> _recycledGroupHeaders = new Stack<ContentPresenter>();
  60. private Stack<ContentPresenter> _recycledGroupFooters = new Stack<ContentPresenter>();
  61. private Stack<ContentPresenter> _recycledItems = new Stack<ContentPresenter>();
  62. private ContentPresenter _recycledListHeader = null;
  63. private ContentPresenter _recycledListFooter = null;
  64. private List<ItemTuple> _flattenedItems;
  65. private object _firstGroup;
  66. private INotifyCollectionChanged _rootCollection;
  67. private List<INotifyCollectionChanged> _groupCollections;
  68. private int _resolvedFirstIndex;
  69. private int _resolvedCount;
  70. private int _screenFirstIndex;
  71. private int _screenCount;
  72. private bool _balanceNeededForSizeChanged;
  73. #region ItemsSource DependencyProperty
  74. /// <summary>
  75. /// The DataSource property. Where all of the items come from.
  76. /// </summary>
  77. public IEnumerable ItemsSource
  78. {
  79. get { return (IEnumerable)GetValue(ItemsSourceProperty); }
  80. set { SetValue(ItemsSourceProperty, value); }
  81. }
  82. /// <summary>
  83. /// The DataSource DependencyProperty.
  84. /// </summary>
  85. public static readonly DependencyProperty ItemsSourceProperty =
  86. DependencyProperty.Register("ItemsSource", typeof(IEnumerable), typeof(LongListSelector), new PropertyMetadata(null, OnItemsSourceChanged));
  87. private static void OnItemsSourceChanged(DependencyObject obj, DependencyPropertyChangedEventArgs e)
  88. {
  89. ((LongListSelector)obj).OnItemsSourceChanged();
  90. }
  91. private void OnItemsSourceChanged()
  92. {
  93. _flattenedItems = null;
  94. if (_isLoaded)
  95. {
  96. EnsureData();
  97. }
  98. }
  99. #endregion
  100. #region ListHeader DependencyProperty
  101. /// <summary>
  102. /// The ListHeader property. Will be used as the first scrollItem in the list.
  103. /// </summary>
  104. public object ListHeader
  105. {
  106. get { return (object)GetValue(ListHeaderProperty); }
  107. set { SetValue(ListHeaderProperty, value); }
  108. }
  109. /// <summary>
  110. /// The ListHeader DependencyProperty.
  111. /// </summary>
  112. public static readonly DependencyProperty ListHeaderProperty =
  113. DependencyProperty.Register("ListHeader", typeof(object), typeof(LongListSelector), new PropertyMetadata(null));
  114. #endregion
  115. #region ListHeaderTemplate DependencyProperty
  116. /// <summary>
  117. /// The ListHeaderTemplate provides the template for the ListHeader.
  118. /// </summary>
  119. public DataTemplate ListHeaderTemplate
  120. {
  121. get { return (DataTemplate)GetValue(ListHeaderTemplateProperty); }
  122. set { SetValue(ListHeaderTemplateProperty, value); }
  123. }
  124. /// <summary>
  125. /// The ListHeaderTemplate DependencyProperty.
  126. /// </summary>
  127. public static readonly DependencyProperty ListHeaderTemplateProperty =
  128. DependencyProperty.Register("ListHeaderTemplate", typeof(DataTemplate), typeof(LongListSelector), new PropertyMetadata(null));
  129. #endregion
  130. #region ListFooter DependencyProperty
  131. /// <summary>
  132. /// The ListFooter property. Will be used as the first scrollItem in the list.
  133. /// </summary>
  134. public object ListFooter
  135. {
  136. get { return (object)GetValue(ListFooterProperty); }
  137. set { SetValue(ListFooterProperty, value); }
  138. }
  139. /// <summary>
  140. /// The ListFooter DependencyProperty.
  141. /// </summary>
  142. public static readonly DependencyProperty ListFooterProperty =
  143. DependencyProperty.Register("ListFooter", typeof(object), typeof(LongListSelector), new PropertyMetadata(null));
  144. #endregion
  145. #region ListFooterTemplate DependencyProperty
  146. /// <summary>
  147. /// The ListFooterTemplate provides the template for the ListFooter.
  148. /// </summary>
  149. public DataTemplate ListFooterTemplate
  150. {
  151. get { return (DataTemplate)GetValue(ListFooterTemplateProperty); }
  152. set { SetValue(ListFooterTemplateProperty, value); }
  153. }
  154. /// <summary>
  155. /// The ListFooterTemplate DependencyProperty.
  156. /// </summary>
  157. public static readonly DependencyProperty ListFooterTemplateProperty =
  158. DependencyProperty.Register("ListFooterTemplate", typeof(DataTemplate), typeof(LongListSelector), new PropertyMetadata(null));
  159. #endregion
  160. #region GroupHeaderTemplate DependencyProperty
  161. /// <summary>
  162. /// The GroupHeaderTemplate provides the template for the groups in the items view.
  163. /// </summary>
  164. public DataTemplate GroupHeaderTemplate
  165. {
  166. get { return (DataTemplate)GetValue(GroupHeaderProperty); }
  167. set { SetValue(GroupHeaderProperty, value); }
  168. }
  169. /// <summary>
  170. /// The GroupHeaderTemplate DependencyProperty.
  171. /// </summary>
  172. public static readonly DependencyProperty GroupHeaderProperty =
  173. DependencyProperty.Register("GroupHeaderTemplate", typeof(DataTemplate), typeof(LongListSelector), new PropertyMetadata(null));
  174. #endregion
  175. #region GroupFooterTemplate DependencyProperty
  176. /// <summary>
  177. /// The GroupFooterTemplate provides the template for the groups in the items view.
  178. /// </summary>
  179. public DataTemplate GroupFooterTemplate
  180. {
  181. get { return (DataTemplate)GetValue(GroupFooterProperty); }
  182. set { SetValue(GroupFooterProperty, value); }
  183. }
  184. /// <summary>
  185. /// The GroupFooterTemplate DependencyProperty.
  186. /// </summary>
  187. public static readonly DependencyProperty GroupFooterProperty =
  188. DependencyProperty.Register("GroupFooterTemplate", typeof(DataTemplate), typeof(LongListSelector), new PropertyMetadata(null));
  189. #endregion
  190. #region ItemTemplate DependencyProperty
  191. /// <summary>
  192. /// The ItemTemplate provides the template for the items in the items view.
  193. /// </summary>
  194. public DataTemplate ItemTemplate
  195. {
  196. get { return (DataTemplate)GetValue(ItemsTemplateProperty); }
  197. set { SetValue(ItemsTemplateProperty, value); }
  198. }
  199. /// <summary>
  200. /// The ItemTemplate DependencyProperty.
  201. /// </summary>
  202. public static readonly DependencyProperty ItemsTemplateProperty =
  203. DependencyProperty.Register("ItemTemplate", typeof(DataTemplate), typeof(LongListSelector), new PropertyMetadata(null));
  204. #endregion
  205. #region GroupItemTemplate DependencyProperty
  206. /// <summary>
  207. /// The GroupItemTemplate specifies the template that will be used in group view mode.
  208. /// </summary>
  209. public DataTemplate GroupItemTemplate
  210. {
  211. get { return (DataTemplate)GetValue(GroupItemTemplateProperty); }
  212. set { SetValue(GroupItemTemplateProperty, value); }
  213. }
  214. /// <summary>
  215. /// The GroupItemTemplate DependencyProperty.
  216. /// </summary>
  217. public static readonly DependencyProperty GroupItemTemplateProperty =
  218. DependencyProperty.Register("GroupItemTemplate", typeof(DataTemplate), typeof(LongListSelector), new PropertyMetadata(null));
  219. #endregion
  220. #region GroupItemsPanel DependencyProperty
  221. /// <summary>
  222. /// The GroupItemsPanel specifies the panel that will be used in group view mode.
  223. /// </summary>
  224. public ItemsPanelTemplate GroupItemsPanel
  225. {
  226. get { return (ItemsPanelTemplate)GetValue(GroupItemsPanelProperty); }
  227. set { SetValue(GroupItemsPanelProperty, value); }
  228. }
  229. /// <summary>
  230. /// The GroupItemsPanel DependencyProperty.
  231. /// </summary>
  232. public static readonly DependencyProperty GroupItemsPanelProperty =
  233. DependencyProperty.Register("GroupItemsPanel", typeof(ItemsPanelTemplate), typeof(LongListSelector), new PropertyMetadata(null));
  234. #endregion
  235. #region IsBouncy DependencyProperty
  236. /// <summary>
  237. /// Controls whether the list can be (temporarily) scrolled off of the ends.
  238. /// </summary>
  239. public bool IsBouncy
  240. {
  241. get { return (bool)GetValue(IsBouncyProperty); }
  242. set { SetValue(IsBouncyProperty, value); }
  243. }
  244. /// <summary>
  245. /// The IsBouncy DependencyProperty
  246. /// </summary>
  247. public static readonly DependencyProperty IsBouncyProperty =
  248. DependencyProperty.Register("IsBouncy", typeof(bool), typeof(LongListSelector), new PropertyMetadata(true));
  249. #endregion
  250. #region IsScrolling DependencyProperty
  251. /// <summary>
  252. /// Returns true if the user is manipulating the list, or if an inertial animation is taking place.
  253. /// </summary>
  254. public bool IsScrolling
  255. {
  256. get { return (bool)GetValue(IsScrollingProperty); }
  257. private set { SetValue(IsScrollingProperty, value); }
  258. }
  259. /// <summary>
  260. /// The IsScrolling DependencyProperty
  261. /// </summary>
  262. public static readonly DependencyProperty IsScrollingProperty =
  263. DependencyProperty.Register("IsScrolling", typeof(bool), typeof(LongListSelector), new PropertyMetadata(false, OnIsScrollingChanged));
  264. private static void OnIsScrollingChanged(DependencyObject obj, DependencyPropertyChangedEventArgs e)
  265. {
  266. LongListSelector control = (LongListSelector)obj;
  267. if ((bool) e.NewValue)
  268. {
  269. VisualStateManager.GoToState(control, "Scrolling", true);
  270. SafeRaise.Raise(control.ScrollingStarted, obj);
  271. }
  272. else
  273. {
  274. VisualStateManager.GoToState(control, "NotScrolling", true);
  275. control.BounceBack(false);
  276. SafeRaise.Raise(control.ScrollingCompleted, obj);
  277. }
  278. }
  279. #endregion
  280. #region ShowListHeader DependencyProperty
  281. /// <summary>
  282. /// Controls whether or not the ListHeader is shown.
  283. /// </summary>
  284. public bool ShowListHeader
  285. {
  286. get { return (bool)GetValue(ShowListHeaderProperty); }
  287. set { SetValue(ShowListHeaderProperty, value); }
  288. }
  289. /// <summary>
  290. /// The ShowListHeader DependencyProperty.
  291. /// </summary>
  292. public static readonly DependencyProperty ShowListHeaderProperty =
  293. DependencyProperty.Register("ShowListHeader", typeof(bool), typeof(LongListSelector), new PropertyMetadata(true, OnShowListHeaderChanged));
  294. private static void OnShowListHeaderChanged(DependencyObject obj, DependencyPropertyChangedEventArgs e)
  295. {
  296. LongListSelector control = (LongListSelector)obj;
  297. if (control.HasListHeader && control._flattenedItems != null)
  298. {
  299. if (control.ShowListHeader)
  300. {
  301. control.OnAdd(0, ItemType.ListHeader, null, new object[] {control.ListHeader});
  302. }
  303. else
  304. {
  305. control.OnRemove(0, 1);
  306. }
  307. control.StopScrolling();
  308. control.ResetMinMax();
  309. control.Balance();
  310. control.BounceBack(true);
  311. }
  312. }
  313. #endregion
  314. #region ShowListFooter DependencyProperty
  315. /// <summary>
  316. /// Controls whether or not the ListFooter is shown.
  317. /// </summary>
  318. public bool ShowListFooter
  319. {
  320. get { return (bool)GetValue(ShowListFooterProperty); }
  321. set { SetValue(ShowListFooterProperty, value); }
  322. }
  323. /// <summary>
  324. /// The ShowListFooter DependencyProperty.
  325. /// </summary>
  326. public static readonly DependencyProperty ShowListFooterProperty =
  327. DependencyProperty.Register("ShowListFooter", typeof(bool), typeof(LongListSelector), new PropertyMetadata(true, OnShowListFooterChanged));
  328. private static void OnShowListFooterChanged(DependencyObject obj, DependencyPropertyChangedEventArgs e)
  329. {
  330. LongListSelector control = (LongListSelector)obj;
  331. if (control.HasListFooter && control._flattenedItems != null)
  332. {
  333. if (control.ShowListFooter)
  334. {
  335. control.OnAdd(control._flattenedItems.Count, ItemType.ListFooter, null, new object[] { control.ListFooter });
  336. }
  337. else
  338. {
  339. control.OnRemove(control._flattenedItems.Count - 1, 1);
  340. }
  341. control.StopScrolling();
  342. control.ResetMinMax();
  343. control.Balance();
  344. control.BounceBack(true);
  345. }
  346. }
  347. #endregion
  348. #region SelectedItem DependencyProperty
  349. private bool _setSelectionInternal;
  350. private bool _selectedItemChanged;
  351. private object[] EmptyList = { };
  352. private object[] _selectionList = new Object[1];
  353. /// <summary>
  354. /// Gets or sets the selected item.
  355. /// </summary>
  356. public object SelectedItem
  357. {
  358. get { return (object)GetValue(SelectedItemProperty); }
  359. set { SetValue(SelectedItemProperty, value); }
  360. }
  361. /// <summary>
  362. /// The SelectedItem DependencyProperty.
  363. /// </summary>
  364. public static readonly DependencyProperty SelectedItemProperty =
  365. DependencyProperty.Register("SelectedItem", typeof(object), typeof(LongListSelector), new PropertyMetadata(OnSelectedItemChanged));
  366. private static void OnSelectedItemChanged(DependencyObject obj, DependencyPropertyChangedEventArgs e)
  367. {
  368. ((LongListSelector)obj).OnSelectedItemChanged(e);
  369. }
  370. private void OnSelectedItemChanged(DependencyPropertyChangedEventArgs e)
  371. {
  372. _selectedItemChanged = true;
  373. if (!_setSelectionInternal)
  374. {
  375. RaiseSelectionChangedEvent(e.NewValue);
  376. }
  377. }
  378. private void RaiseSelectionChangedEvent(object newSelection)
  379. {
  380. SelectionChangedEventHandler handler = SelectionChanged;
  381. if (handler != null)
  382. {
  383. _selectionList[0] = newSelection;
  384. handler(this, new SelectionChangedEventArgs(EmptyList, _selectionList));
  385. }
  386. }
  387. private void SetSelectedItemInternal(object newSelectedItem)
  388. {
  389. _setSelectionInternal = true;
  390. _selectedItemChanged = false;
  391. SelectedItem = newSelectedItem;
  392. if (_selectedItemChanged)
  393. {
  394. RaiseSelectionChangedEvent(newSelectedItem);
  395. }
  396. _setSelectionInternal = false;
  397. }
  398. #endregion
  399. #region BufferSize DependencyProperty
  400. /// <summary>
  401. /// The number of "screens" (as defined by the ActualHeight of the LongListSelector) above and below the visible
  402. /// items of the list that will be filled with items.
  403. /// </summary>
  404. public double BufferSize
  405. {
  406. get { return (double)GetValue(BufferSizeProperty); }
  407. set { SetValue(BufferSizeProperty, value); }
  408. }
  409. /// <summary>
  410. /// The BufferSize DependencyProperty
  411. /// </summary>
  412. public static readonly DependencyProperty BufferSizeProperty =
  413. DependencyProperty.Register("BufferSize", typeof(double), typeof(LongListSelector), new PropertyMetadata(BufferSizeDefault, OnBufferSizeChanged));
  414. [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Usage", "CA2208:InstantiateArgumentExceptionsCorrectly")]
  415. private static void OnBufferSizeChanged(DependencyObject obj, DependencyPropertyChangedEventArgs e)
  416. {
  417. double newValue = (double)e.NewValue;
  418. if (newValue < 0)
  419. {
  420. throw new ArgumentOutOfRangeException("BufferSize");
  421. }
  422. ((LongListSelector)obj)._bufferSizeCache = newValue;
  423. }
  424. #endregion
  425. #region MaximumFlickVelocity DependencyProperty
  426. /// <summary>
  427. /// The maximum velocity for flicks, in pixels per second.
  428. /// </summary>
  429. public double MaximumFlickVelocity
  430. {
  431. get { return (double)GetValue(MaximumFlickVelocityProperty); }
  432. set { SetValue(MaximumFlickVelocityProperty, value); }
  433. }
  434. /// <summary>
  435. /// The MaximumFlickVelocity DependencyProperty.
  436. /// </summary>
  437. public static readonly DependencyProperty MaximumFlickVelocityProperty =
  438. DependencyProperty.Register("MaximumFlickVelocity", typeof(double), typeof(LongListSelector), new PropertyMetadata(MotionParameters.MaximumSpeed, OnMaximumFlickVelocityChanged));
  439. [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Usage", "CA2208:InstantiateArgumentExceptionsCorrectly")]
  440. private static void OnMaximumFlickVelocityChanged(DependencyObject obj, DependencyPropertyChangedEventArgs e)
  441. {
  442. double newValue = (double)e.NewValue;
  443. double coercedNewValue = Math.Min(MotionParameters.MaximumSpeed, Math.Max(newValue, 1));
  444. if (newValue != coercedNewValue)
  445. {
  446. ((LongListSelector)obj).MaximumFlickVelocity = MotionParameters.MaximumSpeed;
  447. throw new ArgumentOutOfRangeException("MaximumFlickVelocity");
  448. }
  449. }
  450. #endregion
  451. #region DisplayAllGroups DependencyProperty
  452. /// <summary>
  453. /// Display all groups whether or not they have items.
  454. /// </summary>
  455. public bool DisplayAllGroups
  456. {
  457. get { return (bool)GetValue(DisplayAllGroupsProperty); }
  458. set { SetValue(DisplayAllGroupsProperty, value); }
  459. }
  460. /// <summary>
  461. /// DisplayAllGroups DependencyProperty
  462. /// </summary>
  463. public static readonly DependencyProperty DisplayAllGroupsProperty =
  464. DependencyProperty.Register("DisplayAllGroups", typeof(bool), typeof(LongListSelector), new PropertyMetadata(false, OnDisplayAllGroupsChanged));
  465. private static void OnDisplayAllGroupsChanged(DependencyObject obj, DependencyPropertyChangedEventArgs e)
  466. {
  467. LongListSelector lls = (LongListSelector)obj;
  468. lls._flattenedItems = null;
  469. if (lls._isLoaded)
  470. {
  471. lls.EnsureData();
  472. }
  473. }
  474. #endregion
  475. /// <summary>
  476. /// The SelectionChanged event.
  477. /// </summary>
  478. public event SelectionChangedEventHandler SelectionChanged;
  479. /// <summary>
  480. /// Create a new instance of LongListSelector.
  481. /// </summary>
  482. public LongListSelector()
  483. {
  484. DefaultStyleKey = typeof(LongListSelector);
  485. SizeChanged += OnSizeChanged;
  486. GestureListener listener = GestureService.GetGestureListener(this);
  487. listener.GestureBegin += listener_GestureBegin;
  488. listener.GestureCompleted += listener_GestureCompleted;
  489. listener.DragStarted += listener_DragStarted;
  490. listener.DragDelta += listener_DragDelta;
  491. listener.DragCompleted += listener_DragCompleted;
  492. listener.Flick += listener_Flick;
  493. listener.Tap += listener_Tap;
  494. Loaded += LongListSelector_Loaded;
  495. Unloaded += LongListSelector_Unloaded;
  496. }
  497. /// <summary>
  498. /// Raised when the user is manipulating the list.
  499. /// </summary>
  500. public event EventHandler ScrollingStarted;
  501. /// <summary>
  502. /// Raised when the user has finished a drag or a flick completes.
  503. /// </summary>
  504. public event EventHandler ScrollingCompleted;
  505. /// <summary>
  506. /// Raised when IsBouncy is true and the user has dragged the items down from the top as far as they can go.
  507. /// </summary>
  508. public event EventHandler StretchingTop;
  509. /// <summary>
  510. /// Raised when IsBouncy is true and the user has dragged the items up from the bottom as far as they can go.
  511. /// </summary>
  512. public event EventHandler StretchingBottom;
  513. /// <summary>
  514. /// Raised when the user is no longer stretching.
  515. /// </summary>
  516. public event EventHandler StretchingCompleted;
  517. /// <summary>
  518. /// Indicates that the ContentPresenter with the item is about to be "realized".
  519. /// </summary>
  520. public event EventHandler<LinkUnlinkEventArgs> Link;
  521. /// <summary>
  522. /// Indicates that the ContentPresenter with the item is being recycled and is becoming "un-realized".
  523. /// </summary>
  524. public event EventHandler<LinkUnlinkEventArgs> Unlink;
  525. /// <summary>
  526. /// Set to true when the list is flat instead of a group hierarchy.
  527. /// </summary>
  528. public bool IsFlatList { get; set; }
  529. /// <summary>
  530. /// Instantly jump to the specified item.
  531. /// </summary>
  532. /// <param name="item">The item to scroll to.</param>
  533. public void ScrollTo(object item)
  534. {
  535. EnsureData();
  536. UpdateLayout();
  537. ContentPresenter contentPresenter;
  538. int itemIndex = GetResolvedIndex(item, out contentPresenter);
  539. if (itemIndex != -1)
  540. {
  541. StopScrolling();
  542. _panningTransform.Y = -Canvas.GetTop(contentPresenter);
  543. Balance();
  544. }
  545. itemIndex = GetFlattenedIndex(item);
  546. if (itemIndex != -1)
  547. {
  548. RecycleAllItems();
  549. ResetMinMax();
  550. StopScrolling();
  551. _panningTransform.Y = 0;
  552. _resolvedFirstIndex = itemIndex;
  553. Balance();
  554. _panningTransform.Y = GetCoercedScrollPosition(_panningTransform.Y, false);
  555. }
  556. }
  557. /// <summary>
  558. /// Animate the scrolling of the list to the specified item. Scrolling speed is capped by MaximumFlickVelocity.
  559. /// </summary>
  560. /// <param name="item">The item to scroll to.</param>
  561. public void AnimateTo(object item)
  562. {
  563. EnsureData();
  564. UpdateLayout();
  565. ContentPresenter contentPresenter;
  566. int itemIndex = GetResolvedIndex(item, out contentPresenter);
  567. if (itemIndex != -1)
  568. {
  569. // The item we are scrolling to has already been resolved, so we can set up an animation directly
  570. // to it.
  571. double newPosition = GetCoercedScrollPosition(-Canvas.GetTop(contentPresenter), false);
  572. double delta = -newPosition + _panningTransform.Y;
  573. double seconds = Math.Abs(delta) / MaximumFlickVelocity;
  574. IEasingFunction ease = PhysicsConstants.GetEasingFunction(seconds);
  575. if (_scrollingTowards != -1)
  576. {
  577. _scrollingTowards = -1;
  578. // This is the termination of an AnimateTo call where the location was
  579. // not know. Use a different ease to keep the animation smooth.
  580. ease = new ExponentialEase() { EasingMode = EasingMode.EaseOut };
  581. }
  582. IsFlicking = true;
  583. AnimatePanel(new Duration(TimeSpan.FromSeconds(seconds)), ease, newPosition);
  584. return;
  585. }
  586. itemIndex = GetFlattenedIndex(item);
  587. if (itemIndex != -1)
  588. {
  589. // Since we don't know the pixel position of the item we are scrolling to, we just go
  590. // in a direction, and stop when the item is resolved.
  591. _scrollingTowards = itemIndex;
  592. ScrollTowards();
  593. }
  594. }
  595. /// <summary>
  596. /// Returns all of the items that are currently in view. This is not the same as the items that
  597. /// have associated visual elements: there are usually some visuals offscreen. This might be
  598. /// an empty list if scrolling is happening too quickly.
  599. /// </summary>
  600. /// <returns>The items in view.</returns>
  601. [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Design", "CA1024:UsePropertiesWhereAppropriate")]
  602. public ICollection<object> GetItemsInView()
  603. {
  604. return GetItemsWithContainers(true, false);
  605. }
  606. /// <summary>
  607. /// Used to return either containers or items for either all items with containers or just the
  608. /// visible ones, as specified by the parameters.
  609. /// </summary>
  610. /// <param name="onlyItemsInView">When true, will return values for only items that are in view.</param>
  611. /// <param name="getContainers">When true, will return the containers rather than the items.</param>
  612. /// <returns>A collection of values as specified above.</returns>
  613. public ICollection<object> GetItemsWithContainers(bool onlyItemsInView, bool getContainers)
  614. {
  615. int start = onlyItemsInView ? _screenFirstIndex : _resolvedFirstIndex;
  616. int count = onlyItemsInView ? _screenCount : _resolvedCount;
  617. object[] items = new object[count];
  618. for (int index = start; index < start + count; ++index)
  619. {
  620. items[index - start] = getContainers ? _flattenedItems[index].ContentPresenter : _flattenedItems[index].Item;
  621. }
  622. return items;
  623. }
  624. private bool IsPanning
  625. {
  626. get { return _isPanning; }
  627. set
  628. {
  629. _isPanning = value;
  630. IsScrolling = IsPanning || IsFlicking;
  631. }
  632. }
  633. private bool IsFlicking
  634. {
  635. get { return _isFlicking; }
  636. set
  637. {
  638. _isFlicking = value;
  639. IsScrolling = IsPanning || IsFlicking;
  640. }
  641. }
  642. private bool IsStretching
  643. {
  644. get { return _isStretching; }
  645. set
  646. {
  647. if (_isStretching != value)
  648. {
  649. _isStretching = value;
  650. if (_isStretching)
  651. {
  652. if (_dragTarget < _minimumPanelScroll)
  653. {
  654. SafeRaise.Raise(StretchingBottom, this);
  655. }
  656. else
  657. {
  658. SafeRaise.Raise(StretchingTop, this);
  659. }
  660. }
  661. else
  662. {
  663. SafeRaise.Raise(StretchingCompleted, this);
  664. }
  665. }
  666. }
  667. }
  668. private bool HasListHeader { get { return ListHeaderTemplate != null || ListHeader is UIElement; } }
  669. private bool HasListFooter { get { return ListFooterTemplate != null || ListFooter is UIElement; } }
  670. void LongListSelector_Loaded(object sender, RoutedEventArgs e)
  671. {
  672. _isLoaded = true;
  673. EnsureData();
  674. }
  675. void LongListSelector_Unloaded(object sender, RoutedEventArgs e)
  676. {
  677. _isLoaded = false;
  678. RecycleAllItems();
  679. }
  680. /// <summary>
  681. /// OnApplyTemplate override, used to locate template parts.
  682. /// </summary>
  683. public override void OnApplyTemplate()
  684. {
  685. base.OnApplyTemplate();
  686. // Find the template parts. Create dummy objects if parts are missing to avoid
  687. // null checks throughout the code (although we can't escape them completely.)
  688. _itemsPanel = GetTemplateChild(ItemsPanelName) as Panel ?? new Canvas();
  689. _panningTransform = GetTemplateChild(PanningTransformName) as TranslateTransform ?? new TranslateTransform();
  690. _verticalScrollbar = GetTemplateChild(VerticalScrollBarName) as ScrollBar ?? new ScrollBar();
  691. _panelAnimation = new DoubleAnimation();
  692. Storyboard.SetTarget(_panelAnimation, _panningTransform);
  693. Storyboard.SetTargetProperty(_panelAnimation, new PropertyPath("Y"));
  694. _panelStoryboard = new Storyboard();
  695. _panelStoryboard.Children.Add(_panelAnimation);
  696. _panelStoryboard.Completed += PanelStoryboardCompleted;
  697. Balance();
  698. }
  699. /// <summary>
  700. /// Override of the MeasureOverride function, to capture the available size.
  701. /// </summary>
  702. /// <param name="availableSize">The available size.</param>
  703. /// <returns>The desired size.</returns>
  704. protected override Size MeasureOverride(Size availableSize)
  705. {
  706. _availableSize.Width = availableSize.Width;
  707. _availableSize.Height = double.PositiveInfinity;
  708. return base.MeasureOverride(availableSize);
  709. }
  710. #region Event handlers
  711. private void OnSizeChanged(object sender, SizeChangedEventArgs e)
  712. {
  713. Clip = new RectangleGeometry() { Rect = new Rect(0, 0, e.NewSize.Width, e.NewSize.Height) };
  714. if (_isLoaded)
  715. {
  716. Balance();
  717. }
  718. }
  719. void listener_GestureBegin(object sender, GestureEventArgs e)
  720. {
  721. if (IsScrolling)
  722. {
  723. _ignoreNextTap = true;
  724. }
  725. _gestureStart = DateTime.Now;
  726. if (_isFlicking)
  727. {
  728. _stopTimer.Tick -= _stopTimer_Tick;
  729. _stopTimer.Tick += _stopTimer_Tick;
  730. _stopTimer.Start();
  731. }
  732. }
  733. void _stopTimer_Tick(object sender, EventArgs e)
  734. {
  735. StopScrolling();
  736. IsPanning = IsFlicking = false;
  737. }
  738. void listener_GestureCompleted(object sender, GestureEventArgs e)
  739. {
  740. _stopTimer.Tick -= _stopTimer_Tick;
  741. _ignoreNextTap = false;
  742. }
  743. private void listener_DragStarted(object sender, DragStartedGestureEventArgs e)
  744. {
  745. if (e.Direction != Orientation.Vertical)
  746. {
  747. return;
  748. }
  749. _stopTimer.Tick -= _stopTimer_Tick;
  750. _dragTarget = _panningTransform.Y;
  751. e.Handled = true;
  752. }
  753. private void listener_DragDelta(object sender, DragDeltaGestureEventArgs e)
  754. {
  755. if (e.Direction != Orientation.Vertical)
  756. {
  757. return;
  758. }
  759. TimeSpan elapsed = DateTime.Now - _gestureStart;
  760. e.Handled = true;
  761. if (elapsed > _flickStopWaitTime || Math.Sign(e.VerticalChange) != Math.Sign(_lastFlickVelocity))
  762. {
  763. IsPanning = true;
  764. IsFlicking = false;
  765. if (_firstDragDuringFlick)
  766. {
  767. StopScrolling();
  768. _firstDragDuringFlick = false;
  769. }
  770. else
  771. {
  772. AnimatePanel(_panDuration, _panEase, _dragTarget += e.VerticalChange);
  773. IsStretching = IsBouncy && (GetCoercedScrollPosition(_dragTarget, true) != _dragTarget);
  774. }
  775. }
  776. }
  777. private void listener_DragCompleted(object sender, DragCompletedGestureEventArgs e)
  778. {
  779. if (e.Direction != Orientation.Vertical)
  780. {
  781. return;
  782. }
  783. IsPanning = false;
  784. IsStretching = false;
  785. e.Handled = true;
  786. }
  787. private void listener_Flick(object sender, FlickGestureEventArgs e)
  788. {
  789. if (e.Direction != Orientation.Vertical)
  790. {
  791. return;
  792. }
  793. _stopTimer.Tick -= _stopTimer_Tick;
  794. double vMax = MaximumFlickVelocity;
  795. _lastFlickVelocity = Math.Min(vMax, Math.Max(e.VerticalVelocity, -vMax));
  796. Point velocity = new Point(0, _lastFlickVelocity);
  797. double flickDuration = PhysicsConstants.GetStopTime(velocity);
  798. Point flickEndPoint = PhysicsConstants.GetStopPoint(velocity);
  799. IEasingFunction flickEase = PhysicsConstants.GetEasingFunction(flickDuration);
  800. AnimatePanel(new Duration(TimeSpan.FromSeconds(flickDuration)), flickEase, _panningTransform.Y + flickEndPoint.Y);
  801. IsFlicking = true;
  802. _firstDragDuringFlick = true;
  803. _scrollingTowards = -1;
  804. e.Handled = true;
  805. }
  806. void listener_Tap(object sender, GestureEventArgs e)
  807. {
  808. StopScrolling();
  809. IsPanning = IsFlicking = false;
  810. }
  811. private void StopScrolling()
  812. {
  813. double position = Math.Round(_panningTransform.Y);
  814. StopAnimation();
  815. _panningTransform.Y = position;
  816. _stopTimer.Tick -= _stopTimer_Tick;
  817. _scrollingTowards = -1;
  818. }
  819. private void GroupHeaderTap(object sender, GestureEventArgs e)
  820. {
  821. _stopTimer.Tick -= _stopTimer_Tick;
  822. if (!_ignoreNextTap)
  823. {
  824. DisplayGroupView();
  825. }
  826. }
  827. private void OnItemTap(object sender, GestureEventArgs e)
  828. {
  829. if (!_ignoreNextTap)
  830. {
  831. ContentPresenter cp = (ContentPresenter)sender;
  832. SetSelectedItemInternal(cp.Content);
  833. }
  834. }
  835. private void HandleGesture(object sender, GestureEventArgs e)
  836. {
  837. e.Handled = true;
  838. }
  839. #endregion
  840. private bool IsReady() { return _itemsPanel != null && ItemsSource != null && ActualHeight > 0; }
  841. [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Maintainability", "CA1502:AvoidExcessiveComplexity")]
  842. private void Balance()
  843. {
  844. if (!IsReady() || _flattenedItems.Count == 0)
  845. {
  846. // See comment in the call below. Necessary here for when the last item is removed.
  847. CollapseRecycledElements();
  848. _resolvedFirstIndex = _resolvedCount = _screenFirstIndex = _screenCount = 0;
  849. return;
  850. }
  851. double actualHeight = ActualHeight;
  852. double viewportTop = -(_bufferSizeCache * actualHeight);
  853. double viewportBottom = (_bufferSizeCache + 1) * actualHeight;
  854. double scrollPosition = _panningTransform.Y;
  855. ContentPresenter cc;
  856. double top = 0, bottom = 0;
  857. if (_resolvedCount > 0)
  858. {
  859. // Remove first?
  860. cc = FirstResolved.ContentPresenter;
  861. top = Canvas.GetTop(cc);
  862. bottom = top + cc.DesiredSize.Height;
  863. while (_resolvedCount > 0 && bottom + scrollPosition < viewportTop)
  864. {
  865. RecycleFirst();
  866. if (_resolvedCount > 0)
  867. {
  868. cc = FirstResolved.ContentPresenter;
  869. top = Canvas.GetTop(cc);
  870. bottom = top + cc.DesiredSize.Height;
  871. }
  872. }
  873. // Remove last?
  874. if (_resolvedCount > 0)
  875. {
  876. cc = LastResolved.ContentPresenter;
  877. top = Canvas.GetTop(cc);
  878. bottom = top + cc.DesiredSize.Height;
  879. while (_resolvedCount > 0 && top + scrollPosition > viewportBottom)
  880. {
  881. RecycleLast();
  882. if (_resolvedCount > 0)
  883. {
  884. cc = LastResolved.ContentPresenter;
  885. top = Canvas.GetTop(cc);
  886. bottom = top + cc.DesiredSize.Height;
  887. }
  888. }
  889. }
  890. if (_resolvedCount == 0)
  891. {
  892. ResetMinMax();
  893. }
  894. }
  895. // Empty list?
  896. bool appendExtra = _resolvedCount == 0 && _resolvedFirstIndex == 0;
  897. if (_resolvedCount == 0)
  898. {
  899. //Debug.WriteLine("Adding first element");
  900. _resolvedFirstIndex = Math.Max(0, Math.Min(_resolvedFirstIndex, _flattenedItems.Count - 1));
  901. var t = _flattenedItems[_resolvedFirstIndex];
  902. cc = GetAndAddElementFor(t);
  903. cc.SetExtraData(_resolvedFirstIndex, cc.DesiredSize.Height);
  904. top = 0;
  905. Canvas.SetTop(cc, top);
  906. bottom = cc.DesiredSize.Height;
  907. if (_resolvedFirstIndex == 0)
  908. {
  909. _maximumPanelScroll = 0;
  910. }
  911. }
  912. // Prepend?
  913. cc = FirstResolved.ContentPresenter;
  914. top = Canvas.GetTop(cc);
  915. bottom = top + cc.DesiredSize.Height;
  916. if (top + scrollPosition >= viewportTop && _resolvedFirstIndex == 0)
  917. {
  918. _maximumPanelScroll = -top;
  919. BrakeIfGoingTooFar();
  920. }
  921. else
  922. {
  923. while (top + scrollPosition >= viewportTop && _resolvedFirstIndex > 0)
  924. {
  925. appendExtra = false;
  926. _resolvedFirstIndex = Math.Max(0, Math.Min(_resolvedFirstIndex, _flattenedItems.Count - 1));
  927. var t = _flattenedItems[--_resolvedFirstIndex];
  928. //Debug.WriteLine("Adding {0}", t.Item);
  929. cc = GetAndAddElementFor(t);
  930. cc.SetExtraData(_resolvedFirstIndex, cc.DesiredSize.Height);
  931. bottom = top;
  932. top = bottom - cc.DesiredSize.Height;
  933. Canvas.SetTop(cc, top);
  934. if (_resolvedFirstIndex == 0)
  935. {
  936. _maximumPanelScroll = -top;
  937. BrakeIfGoingTooFar();
  938. }
  939. }
  940. }
  941. // Append?
  942. cc = LastResolved.ContentPresenter;
  943. top = Canvas.GetTop(cc);
  944. bottom = top + cc.DesiredSize.Height;
  945. if (appendExtra)
  946. {
  947. // If we went to the top of the list, generate extra items so that we don't need to generate them
  948. // as soon as scrolling starts.
  949. viewportBottom += ActualHeight * _bufferSizeCache;
  950. }
  951. while (bottom + scrollPosition <= viewportBottom && _resolvedFirstIndex + _resolvedCount < _flattenedItems.Count)
  952. {
  953. _resolvedFirstIndex = Math.Max(0, Math.Min(_resolvedFirstIndex, _flattenedItems.Count - 1));
  954. var t = _flattenedItems[_resolvedFirstIndex + _resolvedCount];
  955. //Debug.WriteLine("Adding {0}", t.Item);
  956. cc = GetAndAddElementFor(t);
  957. cc.SetExtraData(_resolvedFirstIndex + _resolvedCount - 1, cc.DesiredSize.Height);
  958. top = bottom;
  959. Canvas.SetTop(cc, top);
  960. bottom = top + cc.DesiredSize.Height;
  961. }
  962. if (_resolvedFirstIndex + _resolvedCount == _flattenedItems.Count)
  963. {
  964. _minimumPanelScroll = ActualHeight - bottom;
  965. if (_minimumPanelScroll > _maximumPanelScroll)
  966. {
  967. _minimumPanelScroll = _maximumPanelScroll;
  968. }
  969. BrakeIfGoingTooFar();
  970. }
  971. // Determine which items are on the screen
  972. _screenFirstIndex = 0;
  973. _screenCount = 0;
  974. for (int itemIndex = _resolvedFirstIndex; itemIndex < _resolvedFirstIndex + _resolvedCount; ++itemIndex)
  975. {
  976. ContentPresenter cp = _flattenedItems[itemIndex].ContentPresenter;
  977. top = Canvas.GetTop(cp) + scrollPosition;
  978. bottom = top + cp.DesiredSize.Height;
  979. if ((top >= 0 && top <= ActualHeight) || (top < 0 && bottom > 0))
  980. {
  981. if (_screenCount == 0)
  982. {
  983. _screenFirstIndex = itemIndex;
  984. }
  985. ++_screenCount;
  986. }
  987. else if (_screenCount != 0)
  988. {
  989. break;
  990. }
  991. }
  992. // Adjust the scrollbar
  993. double max = Math.Max(1, _flattenedItems.Count - _screenCount);
  994. _verticalScrollbar.Maximum = max;
  995. _verticalScrollbar.Value = Math.Min(max, _screenFirstIndex);
  996. if (Math.Abs(_screenCount - _verticalScrollbar.Value) > 1)
  997. {
  998. _verticalScrollbar.ViewportSize = Math.Max(1, _screenCount);
  999. }
  1000. // This must be done to ensure proper functionality of controls, e.g. Button.
  1001. // It ensures that only items left in the recycle bin get collapsed, since
  1002. // collapsing items and then immediately making them visible can have strange
  1003. // effects, e.g. with a Button.Click on the stack.
  1004. CollapseRecycledElements();
  1005. // When AnimateTo is called, but the location of the item is not know (because it is not resolved) then
  1006. // the list is just scrolled in the right direction until the item is discovered to be in the resolved
  1007. // items list.
  1008. if (_scrollingTowards >= _resolvedFirstIndex && _scrollingTowards < _resolvedFirstIndex + _resolvedCount)
  1009. {
  1010. // Since the specified item now has a known location, scroll to it.
  1011. AnimateTo(_flattenedItems[_scrollingTowards].Item);
  1012. }
  1013. }
  1014. private ItemTuple FirstResolved
  1015. {
  1016. get
  1017. {
  1018. return _flattenedItems[_resolvedFirstIndex];
  1019. }
  1020. }
  1021. private ItemTuple LastResolved
  1022. {
  1023. get
  1024. {
  1025. int index = _resolvedFirstIndex + _resolvedCount - 1;
  1026. return _flattenedItems[index];
  1027. }
  1028. }
  1029. private void RecycleFirst()
  1030. {
  1031. if (_resolvedCount > 0)
  1032. {
  1033. var t = _flattenedItems[_resolvedFirstIndex++];
  1034. RemoveAndAddToRecycleBin(t);
  1035. }
  1036. }
  1037. private void RecycleLast()
  1038. {
  1039. if (_resolvedCount > 0)
  1040. {
  1041. var t = _flattenedItems[_resolvedFirstIndex + _resolvedCount - 1];
  1042. RemoveAndAddToRecycleBin(t);
  1043. }
  1044. }
  1045. private void RemoveAndAddToRecycleBin(ItemTuple tuple)
  1046. {
  1047. ContentPresenter cp = tuple.ContentPresenter;
  1048. switch (tuple.ItemType)
  1049. {
  1050. case ItemType.Item:
  1051. _recycledItems.Push(cp);
  1052. break;
  1053. case ItemType.GroupHeader:
  1054. _recycledGroupHeaders.Push(cp);
  1055. break;
  1056. case ItemType.GroupFooter:
  1057. _recycledGroupFooters.Push(cp);
  1058. break;
  1059. case ItemType.ListHeader:
  1060. Debug.Assert(_recycledListHeader == null);
  1061. _recycledListHeader = cp;
  1062. break;
  1063. case ItemType.ListFooter:
  1064. Debug.Assert(_recycledListFooter == null);
  1065. _recycledListFooter = cp;
  1066. break;
  1067. }
  1068. EventHandler<LinkUnlinkEventArgs> handler = Unlink;
  1069. if (handler != null)
  1070. {
  1071. handler(this, new LinkUnlinkEventArgs(cp));
  1072. }
  1073. tuple.ContentPresenter = null;
  1074. cp.Content = null;
  1075. cp.SetExtraData(-1, 0);
  1076. --_resolvedCount;
  1077. }
  1078. private void CollapseRecycledElements()
  1079. {
  1080. foreach (ContentPresenter cp in _recycledItems)
  1081. {
  1082. cp.Visibility = Visibility.Collapsed;
  1083. }
  1084. foreach (ContentPresenter cp in _recycledGroupHeaders)
  1085. {
  1086. cp.Visibility = Visibility.Collapsed;
  1087. }
  1088. foreach (ContentPresenter cp in _recycledGroupFooters)
  1089. {
  1090. cp.Visibility = Visibility.Collapsed;
  1091. }
  1092. if (_recycledListHeader != null)
  1093. {
  1094. _recycledListHeader.Visibility = Visibility.Collapsed;
  1095. }
  1096. if (_recycledListFooter != null)
  1097. {
  1098. _recycledListFooter.Visibility = Visibility.Collapsed;
  1099. }
  1100. }
  1101. private void EmptyRecycleBin()
  1102. {
  1103. if (_recycledItems != null)
  1104. {
  1105. _recycledItems.Clear();
  1106. }
  1107. if (_recycledGroupHeaders != null)
  1108. {
  1109. _recycledGroupHeaders.Clear();
  1110. }
  1111. if (_recycledGroupFooters != null)
  1112. {
  1113. _recyc

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