PageRenderTime 47ms CodeModel.GetById 22ms RepoModel.GetById 0ms app.codeStats 0ms

/wp-toolkit/Microsoft.Phone.Controls.Toolkit/MultiselectList/MultiselectItem.cs

https://bitbucket.org/jeremejevs/milk-manager
C# | 639 lines | 333 code | 91 blank | 215 comment | 51 complexity | 472155ad70a83c2a393b5ab17af61f5e MD5 | raw 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.Diagnostics.CodeAnalysis;
  7. using System.Windows;
  8. using System.Windows.Controls;
  9. using System.Windows.Data;
  10. using System.Windows.Input;
  11. using System.Windows.Shapes;
  12. namespace Microsoft.Phone.Controls
  13. {
  14. /// <summary>
  15. /// An item container for a Multiselect List.
  16. /// </summary>
  17. /// <QualityBand>Experimental</QualityBand>
  18. [TemplateVisualState(Name = Closed, GroupName = SelectionEnabledStates)]
  19. [TemplateVisualState(Name = Exposed, GroupName = SelectionEnabledStates)]
  20. [TemplateVisualState(Name = Opened, GroupName = SelectionEnabledStates)]
  21. [TemplatePart(Name = OutterHintPanel, Type = typeof(Rectangle))]
  22. [TemplatePart(Name = InnerHintPanel, Type = typeof(Rectangle))]
  23. [TemplatePart(Name = OutterCover, Type = typeof(Grid))]
  24. [TemplatePart(Name = InfoPresenter, Type = typeof(ContentControl))]
  25. [SuppressMessage("Microsoft.Naming", "CA1704:IdentifiersShouldBeSpelledCorrectly")]
  26. public class MultiselectItem : ContentControl
  27. {
  28. /// <summary>
  29. /// Selection mode visual states.
  30. /// </summary>
  31. private const string SelectionEnabledStates = "SelectionEnabledStates";
  32. /// <summary>
  33. /// Closed visual state.
  34. /// </summary>
  35. private const string Closed = "Closed";
  36. /// <summary>
  37. /// Exposed visual state.
  38. /// </summary>
  39. private const string Exposed = "Exposed";
  40. /// <summary>
  41. /// Opened visual state.
  42. /// </summary>
  43. private const string Opened = "Opened";
  44. /// <summary>
  45. /// Select Box template part name.
  46. /// </summary>
  47. private const string SelectBox = "SelectBox";
  48. /// <summary>
  49. /// Outter Hint Panel template part name.
  50. /// </summary>
  51. private const string OutterHintPanel = "OutterHintPanel";
  52. /// <summary>
  53. /// Inner Hint Panel template part name.
  54. /// </summary>
  55. private const string InnerHintPanel = "InnerHintPanel";
  56. /// <summary>
  57. /// Outter Cover template part name.
  58. /// </summary>
  59. private const string OutterCover = "OutterCover";
  60. /// <summary>
  61. /// Item Info Presenter template part name.
  62. /// </summary>
  63. private const string InfoPresenter = "InfoPresenter";
  64. /// <summary>
  65. /// Limit for the manipulation delta in the X-axis.
  66. /// </summary>
  67. private const double _deltaLimitX = 0.0;
  68. /// <summary>
  69. /// Limit for the manipulation delta in the Y-axis.
  70. /// </summary>
  71. private const double _deltaLimitY = 0.4;
  72. /// <summary>
  73. /// Outter Hint Panel template part.
  74. /// </summary>
  75. private Rectangle _outterHintPanel;
  76. /// <summary>
  77. /// Inner Hint Panel template part.
  78. /// </summary>
  79. private Rectangle _innerHintPanel;
  80. /// <summary>
  81. /// Outter Cover template part.
  82. /// </summary>
  83. private Grid _outterCover;
  84. /// <summary>
  85. /// Item Info Presenter template part.
  86. /// </summary>
  87. private ContentControl _infoPresenter;
  88. /// <summary>
  89. /// Multiselect List that owns this Multiselect Item.
  90. /// </summary>
  91. private MultiselectList _parent;
  92. /// <summary>
  93. /// Manipulation delta in the x-axis.
  94. /// </summary>
  95. private double _manipulationDeltaX;
  96. /// <summary>
  97. /// Manipulation delta in the y-axis.
  98. /// </summary>
  99. private double _manipulationDeltaY;
  100. /// <summary>
  101. /// Indicates that this Multiselect Item is a container
  102. /// being reused for virtualization.
  103. /// </summary>
  104. internal bool _isBeingVirtualized;
  105. /// <summary>
  106. /// Flag that is used to prevent multiple selection changed
  107. /// events from being fired when all the items in the list are
  108. /// unselected. Instead, a single event is fired.
  109. /// </summary>
  110. internal bool _canTriggerSelectionChanged = true;
  111. /// <summary>
  112. /// Occurs when the multiselect item is selected.
  113. /// </summary>
  114. public event RoutedEventHandler Selected;
  115. /// <summary>
  116. /// Occurs when the multiselect item is unselected.
  117. /// </summary>
  118. public event RoutedEventHandler Unselected;
  119. #region IsSelected DependencyProperty
  120. /// <summary>
  121. /// Gets or sets the flag that indicates if the item
  122. /// is currently selected.
  123. /// </summary>
  124. public bool IsSelected
  125. {
  126. get { return (bool)GetValue(IsSelectedProperty); }
  127. set { SetValue(IsSelectedProperty, value); }
  128. }
  129. /// <summary>
  130. /// Identifies the IsSelected dependency property.
  131. /// </summary>
  132. public static readonly DependencyProperty IsSelectedProperty =
  133. DependencyProperty.Register("IsSelected", typeof(bool), typeof(MultiselectItem), new PropertyMetadata(false, OnIsSelectedPropertyChanged));
  134. /// <summary>
  135. /// Adds or removes the item to the selected items collection
  136. /// in the Multiselect List that owns it.
  137. /// </summary>
  138. /// <param name="obj">The dependency object.</param>
  139. /// <param name="e">The event information.</param>
  140. private static void OnIsSelectedPropertyChanged(DependencyObject obj, DependencyPropertyChangedEventArgs e)
  141. {
  142. MultiselectItem item = (MultiselectItem)obj;
  143. RoutedEventArgs args = new RoutedEventArgs();
  144. bool isSelected = (bool)e.NewValue;
  145. if (isSelected)
  146. {
  147. item.OnSelected(args);
  148. }
  149. else
  150. {
  151. item.OnUnselected(args);
  152. }
  153. //Ignore items being selected/unselected during parent virtualization.
  154. if (item._parent != null && !item._isBeingVirtualized)
  155. {
  156. if (isSelected)
  157. {
  158. item._parent.SelectedItems.Add(item.Content);
  159. //Trigger a selection changed event for one added item.
  160. if (item._canTriggerSelectionChanged)
  161. {
  162. item._parent.OnSelectionChanged(new object[0], new object[] { item.Content });
  163. }
  164. }
  165. else
  166. {
  167. item._parent.SelectedItems.Remove(item.Content);
  168. //Trigger a selection changed event for one removed item.
  169. if (item._canTriggerSelectionChanged)
  170. {
  171. item._parent.OnSelectionChanged(new object[] { item.Content }, new object[0]);
  172. }
  173. }
  174. }
  175. }
  176. #endregion
  177. #region State DependencyProperty
  178. /// <summary>
  179. /// Gets or sets the visual state.
  180. /// </summary>
  181. internal SelectionEnabledState State
  182. {
  183. get { return (SelectionEnabledState)GetValue(StateProperty); }
  184. set { SetValue(StateProperty, value); }
  185. }
  186. /// <summary>
  187. /// Identifies the State dependency property.
  188. /// </summary>
  189. internal static readonly DependencyProperty StateProperty =
  190. DependencyProperty.Register("State", typeof(SelectionEnabledState), typeof(MultiselectItem), new PropertyMetadata(SelectionEnabledState.Closed, null));
  191. #endregion
  192. #region HintPanelHeight DependencyProperty
  193. /// <summary>
  194. /// Gets or sets the height of the hint panel.
  195. /// </summary>
  196. public double HintPanelHeight
  197. {
  198. get { return (double)GetValue(HintPanelHeightProperty); }
  199. set { SetValue(HintPanelHeightProperty, value); }
  200. }
  201. /// <summary>
  202. /// Identifies the HintPanelHeight dependency property.
  203. /// </summary>
  204. public static readonly DependencyProperty HintPanelHeightProperty =
  205. DependencyProperty.Register("HintPanelHeight", typeof(double), typeof(MultiselectItem), new PropertyMetadata(Double.NaN, null));
  206. /// <summary>
  207. /// Sets the vertical alignment of the hint panels to stretch if the
  208. /// height is not manually set. If it is, the alignment is set to top.
  209. /// </summary>
  210. /// <param name="obj">The dependency object.</param>
  211. /// <param name="e">The event information.</param>
  212. private static void OnHintPanelHeightPropertyChanged(DependencyObject obj, DependencyPropertyChangedEventArgs e)
  213. {
  214. MultiselectItem source = (MultiselectItem)obj;
  215. if (source._outterHintPanel != null)
  216. {
  217. if (double.IsNaN((double)e.NewValue))
  218. {
  219. source._outterHintPanel.VerticalAlignment = VerticalAlignment.Stretch;
  220. }
  221. else
  222. {
  223. source._outterHintPanel.VerticalAlignment = VerticalAlignment.Top;
  224. }
  225. }
  226. if (source._innerHintPanel != null)
  227. {
  228. if (double.IsNaN(source.HintPanelHeight))
  229. {
  230. source._innerHintPanel.VerticalAlignment = VerticalAlignment.Stretch;
  231. }
  232. else
  233. {
  234. source._innerHintPanel.VerticalAlignment = VerticalAlignment.Top;
  235. }
  236. }
  237. }
  238. #endregion
  239. #region ContentInfo DependencyProperty
  240. /// <summary>
  241. /// Gets or sets the content information.
  242. /// </summary>
  243. public object ContentInfo
  244. {
  245. get { return (object)GetValue(ContentInfoProperty); }
  246. set { SetValue(ContentInfoProperty, value); }
  247. }
  248. /// <summary>
  249. /// Identifies the ContentInfo dependency property.
  250. /// </summary>
  251. public static readonly DependencyProperty ContentInfoProperty =
  252. DependencyProperty.Register("ContentInfo", typeof(object), typeof(MultiselectItem), new PropertyMetadata(null, OnContentInfoPropertyChanged));
  253. /// <summary>
  254. /// ContentInfoProperty changed handler.
  255. /// </summary>
  256. /// <param name="obj">The dependency object.</param>
  257. /// <param name="e">The event information.</param>
  258. private static void OnContentInfoPropertyChanged(DependencyObject obj, DependencyPropertyChangedEventArgs e)
  259. {
  260. MultiselectItem source = (MultiselectItem)obj;
  261. source.OnContentInfoChanged(e.OldValue, e.NewValue);
  262. }
  263. #endregion
  264. #region ContentInfoTemplate DependencyProperty
  265. /// <summary>
  266. /// Gets or sets the data template that defines
  267. /// the content information.
  268. /// </summary>
  269. public DataTemplate ContentInfoTemplate
  270. {
  271. get { return (DataTemplate)GetValue(ContentInfoTemplateProperty); }
  272. set { SetValue(ContentInfoTemplateProperty, value); }
  273. }
  274. /// <summary>
  275. /// Identifies the ContentInfoTemplate dependency property.
  276. /// </summary>
  277. public static readonly DependencyProperty ContentInfoTemplateProperty =
  278. DependencyProperty.Register("ContentInfoTemplate", typeof(DataTemplate), typeof(MultiselectItem), new PropertyMetadata(null, OnContentInfoTemplatePropertyChanged));
  279. /// <summary>
  280. /// ContentInfoTemplate changed handler.
  281. /// </summary>
  282. /// <param name="obj">The dependency object.</param>
  283. /// <param name="e">The event information.</param>
  284. private static void OnContentInfoTemplatePropertyChanged(DependencyObject obj, DependencyPropertyChangedEventArgs e)
  285. {
  286. MultiselectItem source = (MultiselectItem)obj;
  287. DataTemplate oldTemplate = e.OldValue as DataTemplate;
  288. DataTemplate newTemplate = e.NewValue as DataTemplate;
  289. source.OnContentInfoTemplateChanged(oldTemplate, newTemplate);
  290. }
  291. #endregion
  292. /// <summary>
  293. /// Gets the template parts and sets event handlers.
  294. /// </summary>
  295. public override void OnApplyTemplate()
  296. {
  297. _parent = ItemsControlExtensions.GetParentItemsControl<MultiselectList>(this);
  298. if (_innerHintPanel != null)
  299. {
  300. _innerHintPanel.ManipulationStarted -= HintPanel_ManipulationStarted;
  301. _innerHintPanel.ManipulationDelta -= HintPanel_ManipulationDelta;
  302. _innerHintPanel.ManipulationCompleted -= HintPanel_ManipulationCompleted;
  303. }
  304. if (_outterHintPanel != null)
  305. {
  306. _outterHintPanel.ManipulationStarted -= HintPanel_ManipulationStarted;
  307. _outterHintPanel.ManipulationDelta -= HintPanel_ManipulationDelta;
  308. _outterHintPanel.ManipulationCompleted -= HintPanel_ManipulationCompleted;
  309. }
  310. if (_outterCover != null)
  311. {
  312. _outterCover.Tap -= Cover_Tap;
  313. }
  314. _innerHintPanel = base.GetTemplateChild(InnerHintPanel) as Rectangle;
  315. _outterHintPanel = base.GetTemplateChild(OutterHintPanel) as Rectangle;
  316. _outterCover = base.GetTemplateChild(OutterCover) as Grid;
  317. _infoPresenter = base.GetTemplateChild(InfoPresenter) as ContentControl;
  318. base.OnApplyTemplate();
  319. if (_innerHintPanel != null)
  320. {
  321. _innerHintPanel.ManipulationStarted += HintPanel_ManipulationStarted;
  322. _innerHintPanel.ManipulationDelta += HintPanel_ManipulationDelta;
  323. _innerHintPanel.ManipulationCompleted += HintPanel_ManipulationCompleted;
  324. }
  325. if (_outterHintPanel != null)
  326. {
  327. _outterHintPanel.ManipulationStarted += HintPanel_ManipulationStarted;
  328. _outterHintPanel.ManipulationDelta += HintPanel_ManipulationDelta;
  329. _outterHintPanel.ManipulationCompleted += HintPanel_ManipulationCompleted;
  330. }
  331. if (_outterCover != null)
  332. {
  333. _outterCover.Tap += Cover_Tap;
  334. }
  335. if (ContentInfo == null && _parent != null)
  336. {
  337. if (_parent.ItemInfoTemplate != null)
  338. {
  339. _infoPresenter.ContentTemplate = _parent.ItemInfoTemplate;
  340. Binding infoBinding = new Binding();
  341. this.SetBinding(ContentInfoProperty, infoBinding);
  342. }
  343. }
  344. if (_outterHintPanel != null)
  345. {
  346. if (double.IsNaN(HintPanelHeight))
  347. {
  348. _outterHintPanel.VerticalAlignment = VerticalAlignment.Stretch;
  349. }
  350. else
  351. {
  352. _outterHintPanel.VerticalAlignment = VerticalAlignment.Top;
  353. }
  354. }
  355. if (_innerHintPanel != null)
  356. {
  357. if (double.IsNaN(HintPanelHeight))
  358. {
  359. _innerHintPanel.VerticalAlignment = VerticalAlignment.Stretch;
  360. }
  361. else
  362. {
  363. _innerHintPanel.VerticalAlignment = VerticalAlignment.Top;
  364. }
  365. }
  366. UpdateVisualState(false);
  367. }
  368. /// <summary>
  369. /// Initializes a new instance of the MultiselectItem class.
  370. /// </summary>
  371. public MultiselectItem()
  372. {
  373. DefaultStyleKey = typeof(MultiselectItem);
  374. }
  375. /// <summary>
  376. /// Updates the visual state.
  377. /// </summary>
  378. /// <param name="useTransitions">
  379. /// Indicates whether visual transitions should be used.
  380. /// </param>
  381. internal void UpdateVisualState(bool useTransitions)
  382. {
  383. string state;
  384. switch (this.State)
  385. {
  386. case SelectionEnabledState.Closed:
  387. state = Closed;
  388. break;
  389. case SelectionEnabledState.Exposed:
  390. state = Exposed;
  391. break;
  392. case SelectionEnabledState.Opened:
  393. state = Opened;
  394. break;
  395. default:
  396. state = Closed;
  397. break;
  398. }
  399. VisualStateManager.GoToState(this, state, useTransitions);
  400. }
  401. /// <summary>
  402. /// Raises a routed event.
  403. /// </summary>
  404. /// <param name="handler">Event handler.</param>
  405. /// <param name="args">Event arguments.</param>
  406. private void RaiseEvent(RoutedEventHandler handler, RoutedEventArgs args)
  407. {
  408. if (handler != null)
  409. {
  410. handler(this, args);
  411. }
  412. }
  413. #region Event overrides
  414. /// <summary>
  415. /// Raises a Selected event when the IsSelected property
  416. /// changes from false to true.
  417. /// </summary>
  418. /// <param name="e">The event information.</param>
  419. protected virtual void OnSelected(RoutedEventArgs e)
  420. {
  421. if (_parent == null)
  422. {
  423. State = SelectionEnabledState.Opened;
  424. UpdateVisualState(true);
  425. }
  426. RaiseEvent(Selected, e);
  427. }
  428. /// <summary>
  429. /// Raises an Unselected event when the IsSelected property
  430. /// changes from true to false.
  431. /// </summary>
  432. /// <param name="e">The event information.</param>
  433. protected virtual void OnUnselected(RoutedEventArgs e)
  434. {
  435. if (_parent == null)
  436. {
  437. State = SelectionEnabledState.Closed;
  438. UpdateVisualState(true);
  439. }
  440. RaiseEvent(Unselected, e);
  441. }
  442. /// <summary>
  443. /// Called when the value of the ContentInfo property changes.
  444. /// </summary>
  445. /// <param name="oldContentInfo">
  446. /// The old value of the ContentInfo property.
  447. /// </param>
  448. /// <param name="newContentInfo">
  449. /// The new value of the ContentInfo property.
  450. /// </param>
  451. protected virtual void OnContentInfoChanged(object oldContentInfo, object newContentInfo)
  452. {
  453. }
  454. /// <summary>
  455. /// Called when the value of the ContentInfoTemplate property chages.
  456. /// </summary>
  457. /// <param name="oldContentInfoTemplate">
  458. /// The old value of the ContentInfoTemplate property.
  459. /// </param>
  460. /// <param name="newContentInfoTemplate">
  461. /// The new value of the ContentInfoTemplate property.
  462. /// </param>
  463. protected virtual void OnContentInfoTemplateChanged(DataTemplate oldContentInfoTemplate, DataTemplate newContentInfoTemplate)
  464. {
  465. }
  466. #endregion
  467. #region Input events
  468. /// <summary>
  469. /// Triggers a visual transition to the Exposed visual state.
  470. /// </summary>
  471. /// <param name="sender">The Hint Panel that triggers the event.</param>
  472. /// <param name="e">The event information.</param>
  473. private void HintPanel_ManipulationStarted(object sender, ManipulationStartedEventArgs e)
  474. {
  475. this.State = SelectionEnabledState.Exposed;
  476. UpdateVisualState(true);
  477. }
  478. /// <summary>
  479. /// Triggers a visual transition to the Closed visual state
  480. /// if the manipulation delta goes out of bounds.
  481. /// </summary>
  482. /// <param name="sender">The Hint Panel that triggers the event.</param>
  483. /// <param name="e">The event information.</param>
  484. private void HintPanel_ManipulationDelta(object sender, ManipulationDeltaEventArgs e)
  485. {
  486. _manipulationDeltaX = e.DeltaManipulation.Translation.X;
  487. _manipulationDeltaY = e.DeltaManipulation.Translation.Y;
  488. if (_manipulationDeltaX < _deltaLimitX)
  489. {
  490. _manipulationDeltaX *= -1.0;
  491. }
  492. if (_manipulationDeltaY < _deltaLimitX)
  493. {
  494. _manipulationDeltaY *= -1.0;
  495. }
  496. if (_manipulationDeltaX > _deltaLimitX || _manipulationDeltaY >= _deltaLimitY)
  497. {
  498. this.State = SelectionEnabledState.Closed;
  499. UpdateVisualState(true);
  500. }
  501. }
  502. /// <summary>
  503. /// Selects this MultiselectItem if the manipulation delta
  504. /// is within limits and fires an OnSelectionChanged event accordingly.
  505. /// Resets the deltas for both axises.
  506. /// </summary>
  507. /// <param name="sender">The Hint Panel that triggers the event.</param>
  508. /// <param name="e">The event information.</param>
  509. private void HintPanel_ManipulationCompleted(object sender, ManipulationCompletedEventArgs e)
  510. {
  511. if (_manipulationDeltaX == _deltaLimitX && _manipulationDeltaY < _deltaLimitY)
  512. {
  513. this.IsSelected = true;
  514. }
  515. _manipulationDeltaX = 0.0;
  516. _manipulationDeltaY = 0.0;
  517. }
  518. /// <summary>
  519. /// Toogles the selection of a MultiselectItem
  520. /// and fires an OnSelectionChanged event accordingly.
  521. /// </summary>
  522. /// <param name="sender">The cover that triggers the event.</param>
  523. /// <param name="e">The event information.</param>
  524. private void Cover_Tap(object sender, System.Windows.Input.GestureEventArgs e)
  525. {
  526. this.IsSelected = !this.IsSelected;
  527. }
  528. #endregion
  529. }
  530. /// <summary>
  531. /// Represents the visual states of a Multiselect item.
  532. /// </summary>
  533. internal enum SelectionEnabledState
  534. {
  535. /// <summary>
  536. /// Closed visual state value.
  537. /// </summary>
  538. Closed = 0,
  539. /// <summary>
  540. /// Exposed visual state value.
  541. /// </summary>
  542. Exposed = 1,
  543. /// <summary>
  544. /// Opened visual state value.
  545. /// </summary>
  546. Opened = 2
  547. }
  548. }