PageRenderTime 44ms CodeModel.GetById 15ms RepoModel.GetById 0ms app.codeStats 1ms

/wp-toolkit/Microsoft.Phone.Controls.Toolkit.WP8/LongListMultiSelector/LongListMultiSelector.cs

https://bitbucket.org/jeremejevs/milk-manager
C# | 1031 lines | 608 code | 106 blank | 317 comment | 97 complexity | 66c5f47862d963030d7fcd38dda5ac81 MD5 | raw file
  1. // 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.ComponentModel;
  11. using System.Diagnostics.CodeAnalysis;
  12. using System.Windows;
  13. using System.Windows.Controls;
  14. using System.Windows.Controls.Primitives;
  15. using System.Windows.Data;
  16. using System.Windows.Media;
  17. namespace Microsoft.Phone.Controls
  18. {
  19. /// <summary>
  20. /// Extension of the standard LongListSelector control which allows multiple selection of items
  21. /// </summary>
  22. [StyleTypedProperty(Property = "ItemContainerStyle", StyleTargetType = typeof(LongListMultiSelectorItem))]
  23. [TemplatePart(Name = InnerSelectorName, Type = typeof(LongListSelector))]
  24. [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Naming", "CA1704:IdentifiersShouldBeSpelledCorrectly", MessageId = "Multi")]
  25. public class LongListMultiSelector : Control
  26. {
  27. #region Implementation Fields
  28. private const string InnerSelectorName = "InnerSelector";
  29. LongListSelector _innerSelector = null;
  30. HashSet<WeakReference<LongListMultiSelectorItem>> _realizedItems = new HashSet<WeakReference<LongListMultiSelectorItem>>();
  31. SelectedItemsList _selectedItems = new SelectedItemsList();
  32. // backing property as this is not a DependencyProperty in the LongListSelector
  33. LongListSelectorLayoutMode _layoutMode = LongListSelectorLayoutMode.List;
  34. /// <summary>
  35. /// Gets the state of manipulation handling on the Microsoft.Phone.Controls.LongListSelector
  36. /// control.
  37. /// </summary>
  38. public ManipulationState ManipulationState { get { return (_innerSelector == null) ? ManipulationState.Idle : _innerSelector.ManipulationState; } }
  39. #endregion
  40. #region Dependency Properties
  41. /// <summary>
  42. /// Gets or sets the size used when displaying an item in the Microsoft.Phone.Controls.LongListMultiSelector.
  43. /// </summary>
  44. public Size GridCellSize
  45. {
  46. get { return (Size)GetValue(GridCellSizeProperty); }
  47. set { SetValue(GridCellSizeProperty, value); }
  48. }
  49. /// <summary>
  50. /// Identifies the Microsoft.Phone.Controls.LongListMultiSelector.GridCellSize dependency
  51. /// property.
  52. /// </summary>
  53. public static readonly DependencyProperty GridCellSizeProperty =
  54. DependencyProperty.Register("GridCellSize", typeof(Size), typeof(LongListMultiSelector), new PropertyMetadata(Size.Empty));
  55. /// <summary>
  56. /// Gets or sets the template for the group footer in the Microsoft.Phone.Controls.LongListMultiSelector.
  57. /// </summary>
  58. public DataTemplate GroupFooterTemplate
  59. {
  60. get { return (DataTemplate)GetValue(GroupFooterTemplateProperty); }
  61. set { SetValue(GroupFooterTemplateProperty, value); }
  62. }
  63. /// <summary>
  64. /// Identifies the Microsoft.Phone.Controls.LongListMultiSelector.GroupFooterTemplate dependency property.
  65. /// </summary>
  66. public static readonly DependencyProperty GroupFooterTemplateProperty =
  67. DependencyProperty.Register("GroupFooterTemplate", typeof(DataTemplate), typeof(LongListMultiSelector), new PropertyMetadata(null));
  68. /// <summary>
  69. /// Gets or sets the template for the group header in the Microsoft.Phone.Controls.LongListMultiSelector.
  70. /// </summary>
  71. public DataTemplate GroupHeaderTemplate
  72. {
  73. get { return (DataTemplate)GetValue(GroupHeaderTemplateProperty); }
  74. set { SetValue(GroupHeaderTemplateProperty, value); }
  75. }
  76. /// <summary>
  77. /// Identifies the Microsoft.Phone.Controls.LongListMultiSelector.GroupHeaderTemplate dependency property.
  78. /// </summary>
  79. public static readonly DependencyProperty GroupHeaderTemplateProperty =
  80. DependencyProperty.Register("GroupHeaderTemplate", typeof(DataTemplate), typeof(LongListMultiSelector), new PropertyMetadata(null));
  81. /// <summary>
  82. /// Gets or sets a value that indicates whether to hide empty groups in the Microsoft.Phone.Controls.LongListMultiSelector.
  83. /// </summary>
  84. public bool HideEmptyGroups
  85. {
  86. get { return (bool)GetValue(HideEmptyGroupsProperty); }
  87. set { SetValue(HideEmptyGroupsProperty, value); }
  88. }
  89. /// <summary>
  90. /// Identifies the Microsoft.Phone.Controls.LongListMultiSelector.HideEmptyGroups dependency property.
  91. /// </summary>
  92. public static readonly DependencyProperty HideEmptyGroupsProperty =
  93. DependencyProperty.Register("HideEmptyGroups", typeof(bool), typeof(LongListMultiSelector), new PropertyMetadata(false));
  94. /// <summary>
  95. /// Gets or sets a value that indicates whether grouping is enabled in the Microsoft.Phone.Controls.LongListMultiSelector.
  96. /// </summary>
  97. public bool IsGroupingEnabled
  98. {
  99. get { return (bool)GetValue(IsGroupingEnabledProperty); }
  100. set { SetValue(IsGroupingEnabledProperty, value); }
  101. }
  102. /// <summary>
  103. /// Identifies the Microsoft.Phone.Controls.LongListMultiSelector.IsGroupingEnabled dependency property.
  104. /// </summary>
  105. public static readonly DependencyProperty IsGroupingEnabledProperty =
  106. DependencyProperty.Register("IsGroupingEnabled", typeof(bool), typeof(LongListMultiSelector), new PropertyMetadata(false));
  107. /// <summary>
  108. /// Gets or sets the template for items hosting in the Microsoft.Phone.Controls.LongListMultiSelector, targeted to customize selection highlighting
  109. /// </summary>
  110. public Style ItemContainerStyle
  111. {
  112. get { return (Style)GetValue(ItemContainerStyleProperty); }
  113. set { SetValue(ItemContainerStyleProperty, value); }
  114. }
  115. /// <summary>
  116. /// Identifies the Microsoft.Phone.Controls.LongListMultiSelector.ItemContainerStyle dependency property.
  117. /// </summary>
  118. public static readonly DependencyProperty ItemContainerStyleProperty =
  119. DependencyProperty.Register("ItemContainerStyle", typeof(Style), typeof(LongListMultiSelector), new PropertyMetadata(null, OnItemContainerStylePropertyChanged));
  120. /// <summary>
  121. /// Called when ItemContainerStyle property has been changed
  122. /// </summary>
  123. /// <param name="sender"></param>
  124. /// <param name="e"></param>
  125. static void OnItemContainerStylePropertyChanged(object sender, DependencyPropertyChangedEventArgs e)
  126. {
  127. LongListMultiSelector This = sender as LongListMultiSelector;
  128. if (This != null)
  129. {
  130. This.OnItemContainerStyleChanged();
  131. }
  132. }
  133. /// <summary>
  134. /// Gets or sets a collection used to generate the content of the Microsoft.Phone.Controls.LongListMultiSelector.
  135. /// </summary>
  136. [SuppressMessage("Microsoft.Usage", "CA2227:CollectionPropertiesShouldBeReadOnly", Justification = "Framework LongListSelector as a R/W IList ItemsSource property.")]
  137. public IList ItemsSource
  138. {
  139. get { return (IList)GetValue(ItemsSourceProperty); }
  140. set { SetValue(ItemsSourceProperty, value); }
  141. }
  142. /// <summary>
  143. /// Identifies the Microsoft.Phone.Controls.LongListMultiSelector.ItemsSource dependency property.
  144. /// </summary>
  145. public static readonly DependencyProperty ItemsSourceProperty =
  146. DependencyProperty.Register("ItemsSource", typeof(IList), typeof(LongListMultiSelector), new PropertyMetadata(null, OnItemsSourcePropertyChanged));
  147. /// <summary>
  148. /// Handles the change of ItemsSource property : disconnects event handler from old value and reconnects event handler to the new value
  149. /// </summary>
  150. /// <param name="sender"></param>
  151. /// <param name="e"></param>
  152. static void OnItemsSourcePropertyChanged(object sender, DependencyPropertyChangedEventArgs e)
  153. {
  154. LongListMultiSelector This = sender as LongListMultiSelector;
  155. if (This != null)
  156. {
  157. This.OnItemsSourceChanged(e.OldValue, e.NewValue);
  158. }
  159. }
  160. /// <summary>
  161. /// Gets or sets the template for the items in the items view
  162. /// </summary>
  163. public DataTemplate ItemTemplate
  164. {
  165. get { return (DataTemplate)GetValue(ItemTemplateProperty); }
  166. set { SetValue(ItemTemplateProperty, value); }
  167. }
  168. /// <summary>
  169. /// Identifies the Microsoft.Phone.Controls.LongListMultiSelector.ItemTemplate dependency property.
  170. /// </summary>
  171. public static readonly DependencyProperty ItemTemplateProperty =
  172. DependencyProperty.Register("ItemTemplate", typeof(DataTemplate), typeof(LongListMultiSelector), new PropertyMetadata(null));
  173. /// <summary>
  174. /// Gets or sets the data template that is to be right align in default template and will not move when selection is opened
  175. /// </summary>
  176. public DataTemplate ItemInfoTemplate
  177. {
  178. get { return (DataTemplate)GetValue(ItemInfoTemplateProperty); }
  179. set { SetValue(ItemInfoTemplateProperty, value); }
  180. }
  181. /// <summary>
  182. /// Identifies the ItemInfoTemplate dependency property.
  183. /// </summary>
  184. public static readonly DependencyProperty ItemInfoTemplateProperty =
  185. DependencyProperty.Register("ItemInfoTemplate", typeof(DataTemplate), typeof(LongListMultiSelector), new PropertyMetadata(null, OnItemInfoTemplatePropertyChanged));
  186. /// <summary>
  187. /// Called when ItemInfoTemplate property has been changed
  188. /// </summary>
  189. /// <param name="sender"></param>
  190. /// <param name="e"></param>
  191. static void OnItemInfoTemplatePropertyChanged(object sender, DependencyPropertyChangedEventArgs e)
  192. {
  193. LongListMultiSelector This = sender as LongListMultiSelector;
  194. if (This != null)
  195. {
  196. This.OnItemInfoTemplateChanged();
  197. }
  198. }
  199. /// <summary>
  200. /// Gets or sets the System.Windows.Style for jump list in the Microsoft.Phone.Controls.LongListSelector.
  201. /// </summary>
  202. public Style JumpListStyle
  203. {
  204. get { return (Style)GetValue(JumpListStyleProperty); }
  205. set { SetValue(JumpListStyleProperty, value); }
  206. }
  207. /// <summary>
  208. /// Identifies the Microsoft.Phone.Controls.LongListMultiSelector.JumpListStyle dependency property.
  209. /// </summary>
  210. public static readonly DependencyProperty JumpListStyleProperty =
  211. DependencyProperty.Register("JumpListStyle", typeof(Style), typeof(LongListMultiSelector), new PropertyMetadata(null));
  212. /// <summary>
  213. /// Gets or sets a value that specifies if the Microsoft.Phone.Controls.LongListSelector
  214. /// is in a list mode or grid mode from the Microsoft.Phone.Controls.LongListSelectorLayoutMode
  215. /// enum.
  216. /// </summary>
  217. public LongListSelectorLayoutMode LayoutMode
  218. {
  219. get { return _layoutMode; }
  220. set
  221. {
  222. _layoutMode = value;
  223. if (_innerSelector != null)
  224. {
  225. _innerSelector.LayoutMode = value;
  226. }
  227. }
  228. }
  229. /// <summary>
  230. /// Gets or sets the object that is displayed at the foot of the Microsoft.Phone.Controls.LongListSelector.
  231. /// </summary>
  232. public object ListFooter
  233. {
  234. get { return (object)GetValue(ListFooterProperty); }
  235. set { SetValue(ListFooterProperty, value); }
  236. }
  237. /// <summary>
  238. /// Identifies the Microsoft.Phone.Controls.LongListMultiSelector.ListFooter dependency property.
  239. /// </summary>
  240. public static readonly DependencyProperty ListFooterProperty =
  241. DependencyProperty.Register("ListFooter", typeof(object), typeof(LongListMultiSelector), new PropertyMetadata(null));
  242. /// <summary>
  243. /// Gets or sets the System.Windows.DataTemplatefor an item to display at the
  244. /// foot of the Microsoft.Phone.Controls.LongListSelector.
  245. /// </summary>
  246. public DataTemplate ListFooterTemplate
  247. {
  248. get { return (DataTemplate)GetValue(ListFooterTemplateProperty); }
  249. set { SetValue(ListFooterTemplateProperty, value); }
  250. }
  251. /// <summary>
  252. /// Identifies the Microsoft.Phone.Controls.LongListMultiSelector.ListFooterTemplate dependency property.
  253. /// </summary>
  254. public static readonly DependencyProperty ListFooterTemplateProperty =
  255. DependencyProperty.Register("ListFooterTemplate", typeof(DataTemplate), typeof(LongListMultiSelector), new PropertyMetadata(null));
  256. /// <summary>
  257. /// Gets or sets the object to display at the head of the Microsoft.Phone.Controls.LongListSelector.
  258. /// </summary>
  259. public object ListHeader
  260. {
  261. get { return (object)GetValue(ListHeaderProperty); }
  262. set { SetValue(ListHeaderProperty, value); }
  263. }
  264. /// <summary>
  265. /// Identifies the Microsoft.Phone.Controls.LongListMultiSelector.ListHeader dependency property.
  266. /// </summary>
  267. public static readonly DependencyProperty ListHeaderProperty =
  268. DependencyProperty.Register("ListHeader", typeof(object), typeof(LongListMultiSelector), new PropertyMetadata(null));
  269. /// <summary>
  270. /// Gets or sets the System.Windows.DataTemplatefor an item to display at the
  271. /// head of the Microsoft.Phone.Controls.LongListSelector.
  272. /// </summary>
  273. public DataTemplate ListHeaderTemplate
  274. {
  275. get { return (DataTemplate)GetValue(ListHeaderTemplateProperty); }
  276. set { SetValue(ListHeaderTemplateProperty, value); }
  277. }
  278. /// <summary>
  279. /// Identifies the Microsoft.Phone.Controls.LongListMultiSelector.ListHeaderTemplate dependency
  280. /// property.
  281. /// </summary>
  282. public static readonly DependencyProperty ListHeaderTemplateProperty =
  283. DependencyProperty.Register("ListHeaderTemplate", typeof(DataTemplate), typeof(LongListMultiSelector), new PropertyMetadata(null));
  284. /// <summary>
  285. /// Gets the currently selected items in the Microsoft.Phone.Controls.LongListSelector.
  286. /// </summary>
  287. public IList SelectedItems
  288. {
  289. get { return (IList)GetValue(SelectedItemsProperty); }
  290. }
  291. /// <summary>
  292. /// Identifies the Microsoft.Phone.Controls.LongListMultiSelector.SelectedItems dependency
  293. /// property.
  294. /// </summary>
  295. public static readonly DependencyProperty SelectedItemsProperty =
  296. DependencyProperty.Register("SelectedItems", typeof(IList), typeof(LongListMultiSelector), new PropertyMetadata(null));
  297. /// <summary>
  298. /// Gets or sets the flag that indicates if the list
  299. /// is in selection mode or not.
  300. /// </summary>
  301. public bool IsSelectionEnabled
  302. {
  303. get { return (bool)GetValue(IsSelectionEnabledProperty); }
  304. set { SetValue(IsSelectionEnabledProperty, value); }
  305. }
  306. /// <summary>
  307. /// Identifies the IsSelectionEnabled dependency property.
  308. /// </summary>
  309. public static readonly DependencyProperty IsSelectionEnabledProperty =
  310. DependencyProperty.Register("IsSelectionEnabled", typeof(bool), typeof(LongListMultiSelector), new PropertyMetadata(false, OnIsSelectionEnabledPropertyChanged));
  311. /// <summary>
  312. /// Gets or sets the EnforceIsSelectionEnabled property
  313. /// </summary>
  314. public bool EnforceIsSelectionEnabled
  315. {
  316. get { return (bool)GetValue(EnforceIsSelectionEnabledProperty); }
  317. set { SetValue(EnforceIsSelectionEnabledProperty, value); }
  318. }
  319. /// <summary>
  320. /// Identifies the EnforceIsSelectionEnabled dependency property.
  321. /// </summary>
  322. public static readonly DependencyProperty EnforceIsSelectionEnabledProperty =
  323. DependencyProperty.Register("EnforceIsSelectionEnabled", typeof(bool), typeof(LongListMultiSelector), new PropertyMetadata(false, OnEnforceIsSelectionEnabledPropertyChanged));
  324. /// <summary>
  325. /// Gets or sets the DefaultListItemContainerStyle property
  326. /// </summary>
  327. internal Style DefaultListItemContainerStyle
  328. {
  329. get { return (Style)GetValue(DefaultListItemContainerStyleProperty); }
  330. set { SetValue(DefaultListItemContainerStyleProperty, value); }
  331. }
  332. /// <summary>
  333. /// Identifies the DefaultListItemContainerStyle dependency property.
  334. /// </summary>
  335. internal static readonly DependencyProperty DefaultListItemContainerStyleProperty =
  336. DependencyProperty.Register("DefaultListItemContainerStyle", typeof(Style), typeof(LongListMultiSelector), new PropertyMetadata(null));
  337. /// <summary>
  338. /// Gets or sets the DefaultGridItemContainerStyle property
  339. /// </summary>
  340. internal Style DefaultGridItemContainerStyle
  341. {
  342. get { return (Style)GetValue(DefaultGridItemContainerStyleProperty); }
  343. set { SetValue(DefaultGridItemContainerStyleProperty, value); }
  344. }
  345. /// <summary>
  346. /// Identifies the DefaultGridItemContainerStyle dependency property.
  347. /// </summary>
  348. internal static readonly DependencyProperty DefaultGridItemContainerStyleProperty =
  349. DependencyProperty.Register("DefaultGridItemContainerStyle", typeof(Style), typeof(LongListMultiSelector), new PropertyMetadata(null));
  350. #endregion
  351. #region Events
  352. /// <summary>
  353. /// Occurs when a new item is realized.
  354. /// </summary>
  355. public event EventHandler<ItemRealizationEventArgs> ItemRealized;
  356. /// <summary>
  357. /// Occurs when an item in the Microsoft.Phone.Controls.LongListMultiSelector is unrealized.
  358. /// </summary>
  359. public event EventHandler<ItemRealizationEventArgs> ItemUnrealized;
  360. /// <summary>
  361. /// Occurs when the jump list is closed.
  362. /// </summary>
  363. public event EventHandler JumpListClosed;
  364. /// <summary>
  365. /// Occurs when a jump list is opened.
  366. /// </summary>
  367. public event EventHandler JumpListOpening;
  368. /// <summary>
  369. /// Occurs when Microsoft.Phone.Controls.ManipulationState changes.
  370. /// </summary>
  371. public event EventHandler ManipulationStateChanged;
  372. /// <summary>
  373. /// Occurs when a property value changes.
  374. /// </summary>
  375. public event PropertyChangedEventHandler PropertyChanged;
  376. /// <summary>
  377. /// Occurs when the currently selected item changes.
  378. /// </summary>
  379. public event SelectionChangedEventHandler SelectionChanged;
  380. /// <summary>
  381. /// Occurs when the selection mode is opened or closed.
  382. /// </summary>
  383. public event DependencyPropertyChangedEventHandler IsSelectionEnabledChanged;
  384. #endregion
  385. /// <summary>
  386. /// Initializes a new instance of the Microsoft.Phone.Cointrols.LongListMultiSelector
  387. /// </summary>
  388. public LongListMultiSelector()
  389. {
  390. this.DefaultStyleKey = typeof(LongListMultiSelector);
  391. this.SetValue(SelectedItemsProperty, _selectedItems);
  392. _selectedItems.CollectionCleared += OnSelectedItemsCollectionCleared;
  393. ((INotifyCollectionChanged)_selectedItems).CollectionChanged += OnSelectedItemsCollectionChanged;
  394. }
  395. /// <summary>
  396. /// Template application : gets and hooks the inner LongListSelector
  397. /// </summary>
  398. public override void OnApplyTemplate()
  399. {
  400. base.OnApplyTemplate();
  401. _realizedItems.Clear();
  402. if (_innerSelector != null)
  403. {
  404. _innerSelector.ItemRealized -= OnInnerSelectorItemRealized;
  405. _innerSelector.ItemUnrealized -= OnInnerSelectorItemUnrealized;
  406. _innerSelector.JumpListClosed -= OnInnerSelectorJumpListClosed;
  407. _innerSelector.JumpListOpening -= OnInnerSelectorJumpListOpening;
  408. _innerSelector.ManipulationStateChanged -= OnInnerSelectorManipulationStateChanged;
  409. _innerSelector.PropertyChanged -= OnInnerSelectorPropertyChanged;
  410. }
  411. _innerSelector = this.GetTemplateChild(InnerSelectorName) as LongListSelector;
  412. if (_innerSelector != null)
  413. {
  414. _innerSelector.LayoutMode = LayoutMode;
  415. _innerSelector.ItemRealized += OnInnerSelectorItemRealized;
  416. _innerSelector.ItemUnrealized += OnInnerSelectorItemUnrealized;
  417. _innerSelector.JumpListClosed += OnInnerSelectorJumpListClosed;
  418. _innerSelector.JumpListOpening += OnInnerSelectorJumpListOpening;
  419. _innerSelector.ManipulationStateChanged += OnInnerSelectorManipulationStateChanged;
  420. _innerSelector.PropertyChanged += OnInnerSelectorPropertyChanged;
  421. }
  422. }
  423. /// <summary>
  424. /// Applyies the new style to already realized items
  425. /// </summary>
  426. void OnItemContainerStyleChanged()
  427. {
  428. ApplyLiveItems(item =>
  429. {
  430. item.Style = ItemContainerStyle;
  431. });
  432. }
  433. /// <summary>
  434. /// Called when the ItemsSource property if the LLMS has been changed
  435. /// </summary>
  436. /// <param name="oldValue"></param>
  437. /// <param name="newValue"></param>
  438. protected virtual void OnItemsSourceChanged(object oldValue, object newValue)
  439. {
  440. INotifyCollectionChanged eventSource = oldValue as INotifyCollectionChanged;
  441. if (eventSource != null)
  442. {
  443. eventSource.CollectionChanged -= OnItemsSourceCollectionChanged;
  444. }
  445. eventSource = newValue as INotifyCollectionChanged;
  446. if (eventSource != null)
  447. {
  448. eventSource.CollectionChanged += OnItemsSourceCollectionChanged;
  449. }
  450. SelectedItems.Clear();
  451. }
  452. /// <summary>
  453. /// Handles changes inside the ItemSource collection : removes removed item from the selection
  454. /// </summary>
  455. /// <param name="sender"></param>
  456. /// <param name="e"></param>
  457. protected virtual void OnItemsSourceCollectionChanged(object sender, NotifyCollectionChangedEventArgs e)
  458. {
  459. if ((e != null) && (e.OldItems != null))
  460. {
  461. UnselectItems(e.OldItems);
  462. }
  463. }
  464. /// <summary>
  465. /// Applyies the new style to already realized items
  466. /// </summary>
  467. protected virtual void OnItemInfoTemplateChanged()
  468. {
  469. ApplyLiveItems(item =>
  470. {
  471. item.ContentInfoTemplate = ItemInfoTemplate;
  472. });
  473. }
  474. /// <summary>
  475. /// Relays event from the inner LongListSelector
  476. /// </summary>
  477. /// <param name="sender"></param>
  478. /// <param name="e"></param>
  479. void OnInnerSelectorPropertyChanged(object sender, PropertyChangedEventArgs e)
  480. {
  481. if (PropertyChanged != null)
  482. {
  483. PropertyChanged(sender, e);
  484. }
  485. }
  486. /// <summary>
  487. /// Relays event from the inner LongListSelector
  488. /// </summary>
  489. /// <param name="sender"></param>
  490. /// <param name="e"></param>
  491. void OnInnerSelectorManipulationStateChanged(object sender, EventArgs e)
  492. {
  493. if (ManipulationStateChanged != null)
  494. {
  495. ManipulationStateChanged(sender, e);
  496. }
  497. }
  498. /// <summary>
  499. /// Relays event from the inner LongListSelector
  500. /// </summary>
  501. /// <param name="sender"></param>
  502. /// <param name="e"></param>
  503. void OnInnerSelectorJumpListOpening(object sender, EventArgs e)
  504. {
  505. if (JumpListOpening != null)
  506. {
  507. JumpListOpening(sender, e);
  508. }
  509. }
  510. /// <summary>
  511. /// Relays event from the inner LongListSelector
  512. /// </summary>
  513. /// <param name="sender"></param>
  514. /// <param name="e"></param>
  515. void OnInnerSelectorJumpListClosed(object sender, EventArgs e)
  516. {
  517. if (JumpListClosed != null)
  518. {
  519. JumpListClosed(sender, e);
  520. }
  521. }
  522. /// <summary>
  523. /// Disconnects an item when it is unrealized
  524. /// </summary>
  525. /// <param name="sender"></param>
  526. /// <param name="e"></param>
  527. void OnInnerSelectorItemUnrealized(object sender, ItemRealizationEventArgs e)
  528. {
  529. if (e.ItemKind == LongListSelectorItemKind.Item)
  530. {
  531. int childrenCount = VisualTreeHelper.GetChildrenCount(e.Container);
  532. if (childrenCount > 0)
  533. {
  534. LongListMultiSelectorItem llItem = VisualTreeHelper.GetChild(e.Container, 0) as LongListMultiSelectorItem;
  535. if (llItem != null)
  536. {
  537. llItem.IsSelectedChanged -= OnLongListMultiSelectorItemIsSelectedChanged;
  538. _realizedItems.Remove(llItem.WR);
  539. }
  540. }
  541. }
  542. if (ItemUnrealized != null)
  543. {
  544. ItemUnrealized(sender, e);
  545. }
  546. }
  547. /// <summary>
  548. /// Configure an item's template according to the current state
  549. /// </summary>
  550. /// <param name="item"></param>
  551. internal void ConfigureItem(LongListMultiSelectorItem item)
  552. {
  553. if (item != null)
  554. {
  555. item.ContentTemplate = ItemTemplate;
  556. if (ItemContainerStyle != null)
  557. {
  558. if (item.Style != ItemContainerStyle)
  559. {
  560. item.Style = ItemContainerStyle;
  561. }
  562. }
  563. else if (LayoutMode == LongListSelectorLayoutMode.Grid)
  564. {
  565. if (item.Style != DefaultGridItemContainerStyle)
  566. {
  567. item.Style = DefaultGridItemContainerStyle;
  568. }
  569. }
  570. else
  571. {
  572. if (item.Style != DefaultListItemContainerStyle)
  573. {
  574. item.Style = DefaultListItemContainerStyle;
  575. }
  576. }
  577. if ((ItemInfoTemplate != null) && (item.ContentInfoTemplate != ItemInfoTemplate))
  578. {
  579. item.SetBinding(LongListMultiSelectorItem.ContentInfoProperty, new Binding());
  580. item.ContentInfoTemplate = ItemInfoTemplate;
  581. }
  582. }
  583. }
  584. /// <summary>
  585. /// Called when an item is realized :
  586. /// </summary>
  587. /// <param name="sender"></param>
  588. /// <param name="e"></param>
  589. void OnInnerSelectorItemRealized(object sender, ItemRealizationEventArgs e)
  590. {
  591. if (e.ItemKind == LongListSelectorItemKind.Item)
  592. {
  593. int childrenCount = VisualTreeHelper.GetChildrenCount(e.Container);
  594. if (childrenCount > 0)
  595. {
  596. LongListMultiSelectorItem llItem = VisualTreeHelper.GetChild(e.Container, 0) as LongListMultiSelectorItem;
  597. if (llItem != null)
  598. {
  599. ConfigureItem(llItem);
  600. // Check if item should be selected
  601. llItem.IsSelected = _selectedItems.Contains(llItem.Content);
  602. llItem.IsSelectedChanged += OnLongListMultiSelectorItemIsSelectedChanged;
  603. llItem.GotoState(IsSelectionEnabled ? LongListMultiSelectorItem.State.Opened : LongListMultiSelectorItem.State.Closed);
  604. _realizedItems.Add(llItem.WR);
  605. }
  606. }
  607. }
  608. if (ItemRealized != null)
  609. {
  610. ItemRealized(sender, e);
  611. }
  612. }
  613. /// <summary>
  614. /// Called when an Item's IsSelected property has changed
  615. /// </summary>
  616. /// <param name="sender"></param>
  617. /// <param name="e"></param>
  618. void OnLongListMultiSelectorItemIsSelectedChanged(object sender, EventArgs e)
  619. {
  620. LongListMultiSelectorItem item = sender as LongListMultiSelectorItem;
  621. if (item != null)
  622. {
  623. object content = item.Content;
  624. if (content != null)
  625. {
  626. if (item.IsSelected)
  627. {
  628. SelectedItems.Add(content);
  629. }
  630. else
  631. {
  632. SelectedItems.Remove(content);
  633. }
  634. }
  635. }
  636. }
  637. /// <summary>
  638. /// Changes the state of the items given the value of the property
  639. /// </summary>
  640. /// <param name="sender"></param>
  641. /// <param name="e"></param>
  642. static void OnIsSelectionEnabledPropertyChanged(DependencyObject sender, DependencyPropertyChangedEventArgs e)
  643. {
  644. LongListMultiSelector This = sender as LongListMultiSelector;
  645. if (This != null)
  646. {
  647. This.OnIsSelectionEnabledChanged(e);
  648. }
  649. }
  650. /// <summary>
  651. /// Called when the IsSelectionEnabled property is changed.
  652. /// </summary>
  653. /// <param name="e">DependencyPropertyChangedEventArgs associated to the event</param>
  654. protected virtual void OnIsSelectionEnabledChanged(DependencyPropertyChangedEventArgs e)
  655. {
  656. bool newValue = (bool)e.NewValue;
  657. if (!newValue)
  658. {
  659. SelectedItems.Clear();
  660. }
  661. ApplyItemsState(newValue ? LongListMultiSelectorItem.State.Opened : LongListMultiSelectorItem.State.Closed, true);
  662. if (IsSelectionEnabledChanged != null)
  663. {
  664. IsSelectionEnabledChanged(this, e);
  665. }
  666. }
  667. /// <summary>
  668. /// Called when the OnEnforceIsSelectionEnabled dependency property has been changed
  669. /// </summary>
  670. /// <param name="sender"></param>
  671. /// <param name="e"></param>
  672. static void OnEnforceIsSelectionEnabledPropertyChanged(DependencyObject sender, DependencyPropertyChangedEventArgs e)
  673. {
  674. LongListMultiSelector This = sender as LongListMultiSelector;
  675. if (This != null)
  676. {
  677. This.OnEnforceIsSelectionEnabledChanged();
  678. }
  679. }
  680. /// <summary>
  681. /// Called when the OnEnforceIsSelectionEnabled property has been changed
  682. /// </summary>
  683. protected virtual void OnEnforceIsSelectionEnabledChanged()
  684. {
  685. if (!EnforceIsSelectionEnabled)
  686. {
  687. SelectedItems.Clear();
  688. }
  689. UpdateIsSelectionEnabled();
  690. }
  691. /// <summary>
  692. /// Updates the IsSelectionEnabled property according to the possibly enforced value and the selected items count
  693. /// </summary>
  694. protected virtual void UpdateIsSelectionEnabled()
  695. {
  696. IsSelectionEnabled = (EnforceIsSelectionEnabled || (SelectedItems.Count > 0));
  697. }
  698. /// <summary>
  699. /// Triggers SelectionChanged event and updates the IsSelectionEnabled property
  700. /// </summary>
  701. /// <param name="removedItems"></param>
  702. /// <param name="addedItems"></param>
  703. void OnSelectionChanged(IList removedItems, IList addedItems)
  704. {
  705. UpdateIsSelectionEnabled();
  706. if (SelectionChanged != null)
  707. {
  708. SelectionChanged(this, new SelectionChangedEventArgs(removedItems ?? new List<object>(), addedItems ?? new List<object>()));
  709. }
  710. }
  711. /// <summary>
  712. /// Executes an action to all live items and cleanup dead references
  713. /// </summary>
  714. /// <param name="action"></param>
  715. protected void ApplyLiveItems(Action<LongListMultiSelectorItem> action)
  716. {
  717. if (action != null)
  718. {
  719. HashSet<WeakReference<LongListMultiSelectorItem>> liveItems = new HashSet<WeakReference<LongListMultiSelectorItem>>();
  720. foreach (var wr in _realizedItems)
  721. {
  722. LongListMultiSelectorItem item;
  723. if (wr.TryGetTarget(out item))
  724. {
  725. action(item);
  726. liveItems.Add(wr);
  727. }
  728. }
  729. _realizedItems = liveItems;
  730. }
  731. }
  732. /// <summary>
  733. /// Handles selection changes made throught the SelectedItems property
  734. /// </summary>
  735. /// <param name="sender"></param>
  736. /// <param name="e"></param>
  737. void OnSelectedItemsCollectionChanged(object sender, NotifyCollectionChangedEventArgs e)
  738. {
  739. switch (e.Action)
  740. {
  741. case NotifyCollectionChangedAction.Reset:
  742. // Do nothing as the event CollectionCleared will have the removed items
  743. break;
  744. case NotifyCollectionChangedAction.Add:
  745. SelectItems(e.NewItems);
  746. OnSelectionChanged(null, e.NewItems);
  747. break;
  748. case NotifyCollectionChangedAction.Remove:
  749. UnselectItems(e.OldItems);
  750. OnSelectionChanged(e.OldItems, null);
  751. break;
  752. case NotifyCollectionChangedAction.Replace:
  753. UnselectItems(e.OldItems);
  754. SelectItems(e.NewItems);
  755. OnSelectionChanged(e.OldItems, e.NewItems);
  756. break;
  757. }
  758. }
  759. void OnSelectedItemsCollectionCleared(object sender, LongListMultiSelector.ClearedChangedArgs e)
  760. {
  761. ApplyLiveItems(item => item.IsSelected = false);
  762. OnSelectionChanged(e.OldItems, null);
  763. }
  764. /// <summary>
  765. /// Selects the LongListMultiSelectorItems whose content matches the provided list
  766. /// </summary>
  767. /// <param name="items">List of content (i.e. from ItemsSource)</param>
  768. void SelectItems(IList items)
  769. {
  770. ApplyLiveItems(item =>
  771. {
  772. if (items.Contains(item.Content))
  773. {
  774. item.IsSelected = true;
  775. }
  776. });
  777. }
  778. /// <summary>
  779. /// Unselects the LongListMultiSelectorItems whose content matches the provided list
  780. /// </summary>
  781. /// <param name="items">List of content (i.e. from ItemsSource)</param>
  782. void UnselectItems(IList items)
  783. {
  784. ApplyLiveItems(item =>
  785. {
  786. if (items.Contains(item.Content))
  787. {
  788. item.IsSelected = false;
  789. }
  790. });
  791. }
  792. /// <summary>
  793. /// Returns the LongListMultiSelectorItem corresponding to the given item
  794. /// </summary>
  795. /// <param name="item">Item whose container has to be returned</param>
  796. /// <returns></returns>
  797. public object ContainerFromItem(object item)
  798. {
  799. object ret = null;
  800. ApplyLiveItems(llmsItem =>
  801. {
  802. if (llmsItem.Content == item)
  803. {
  804. ret = llmsItem;
  805. }
  806. });
  807. return ret;
  808. }
  809. /// <summary>
  810. /// Applies a new state to all items. Visible items will use transitions if useTransitions parameter is set, others will not
  811. /// </summary>
  812. /// <param name="state">State to apply</param>
  813. /// <param name="useTransitions">Specify whether to use transitions or not for visible items</param>
  814. private void ApplyItemsState(LongListMultiSelectorItem.State state, bool useTransitions)
  815. {
  816. // Only apply state change if the LLMS has been displayed and items realized
  817. if (_innerSelector != null)
  818. {
  819. LongListMultiSelectorItem item;
  820. if (useTransitions)
  821. {
  822. List<LongListMultiSelectorItem> invisibleItems = new List<LongListMultiSelectorItem>();
  823. GeneralTransform itemTransform;
  824. double bottom = _innerSelector.ActualHeight;
  825. foreach (var wr in _realizedItems)
  826. {
  827. if (wr.TryGetTarget(out item))
  828. {
  829. itemTransform = item.TransformToVisual(_innerSelector);
  830. Point pt = itemTransform.Transform(new Point(0.0, 0.0));
  831. bool isVisible;
  832. if (pt.Y > bottom)
  833. {
  834. // item's bottom will also be outside of inner lls
  835. isVisible = false;
  836. }
  837. else if (pt.Y >= 0)
  838. {
  839. // whatever the position of item's botton, its top is visible
  840. isVisible = true;
  841. }
  842. else
  843. {
  844. // item's bottom is visible if >= 0
  845. pt = itemTransform.Transform(new Point(item.ActualHeight, 0));
  846. isVisible = pt.Y >= 0;
  847. }
  848. if (isVisible)
  849. {
  850. item.GotoState(state, true);
  851. }
  852. else
  853. {
  854. invisibleItems.Add(item);
  855. }
  856. }
  857. }
  858. // now change asynchronously the state of invisible items
  859. Dispatcher.BeginInvoke(() =>
  860. {
  861. foreach (var invisibleItem in invisibleItems)
  862. {
  863. invisibleItem.GotoState(state, false);
  864. }
  865. });
  866. }
  867. else
  868. {
  869. foreach (var wr in _realizedItems)
  870. {
  871. if (wr.TryGetTarget(out item))
  872. {
  873. item.GotoState(state, false);
  874. }
  875. }
  876. }
  877. }
  878. }
  879. /// <summary>
  880. /// Internal event data associated to list changes
  881. /// </summary>
  882. private class ClearedChangedArgs : EventArgs
  883. {
  884. /// <summary>
  885. /// Items removed from the list
  886. /// </summary>
  887. public IList OldItems { get; private set; }
  888. /// <summary>
  889. /// Constructs a NotifyItemsChangedArgs from an action and a list of items
  890. /// </summary>
  891. /// <param name="items"></param>
  892. public ClearedChangedArgs(IList items)
  893. {
  894. this.OldItems = items;
  895. }
  896. }
  897. /// <summary>
  898. /// Collection for holding selected items
  899. /// It adds CollectionCleared event to the ObservableCollection in order to provide removed items with the event when the collection is cleared
  900. /// </summary>
  901. private class SelectedItemsList : ObservableCollection<object>
  902. {
  903. /// <summary>
  904. /// Event indicating that collection has changed
  905. /// </summary>
  906. public event EventHandler<ClearedChangedArgs> CollectionCleared;
  907. /// <summary>
  908. /// Overrides the base class ClearItems method
  909. /// </summary>
  910. protected override void ClearItems()
  911. {
  912. // Collection changes only if items were present before...
  913. if (this.Count > 0)
  914. {
  915. ClearedChangedArgs e = (CollectionCleared != null) ? new ClearedChangedArgs(new List<object>(this)) : null;
  916. base.ClearItems();
  917. if (CollectionCleared != null)
  918. {
  919. CollectionCleared(this, e);
  920. }
  921. }
  922. }
  923. }
  924. }
  925. }