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

/wp-toolkit/Microsoft.Phone.Controls.Toolkit.WP7/ListPicker/ListPickerPage.xaml.cs

https://bitbucket.org/jeremejevs/milk-manager
C# | 444 lines | 309 code | 71 blank | 64 comment | 51 complexity | 7031177debdf0412c66b0bfbd90ca5b5 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.Collections;
  7. using System.Collections.Generic;
  8. using System.ComponentModel;
  9. using System.Globalization;
  10. using System.Linq;
  11. using System.Windows;
  12. using System.Windows.Controls;
  13. using System.Windows.Media;
  14. using System.Windows.Media.Animation;
  15. using System.Windows.Navigation;
  16. using Microsoft.Phone.Shell;
  17. namespace Microsoft.Phone.Controls
  18. {
  19. /// <summary>
  20. /// Displays the list of items and allows single or multiple selection.
  21. /// </summary>
  22. public partial class ListPickerPage : PhoneApplicationPage
  23. {
  24. private const string StateKey_Value = "ListPickerPage_State_Value";
  25. private PageOrientation _lastOrientation;
  26. private IList<WeakReference> _itemsToAnimate;
  27. /// <summary>
  28. /// Gets or sets the string of text to display as the header of the page.
  29. /// </summary>
  30. public string HeaderText { get; set; }
  31. /// <summary>
  32. /// Gets or sets the list of items to display.
  33. /// </summary>
  34. public IList Items { get; private set; }
  35. /// <summary>
  36. /// Gets or sets the selection mode.
  37. /// </summary>
  38. public SelectionMode SelectionMode { get; set; }
  39. /// <summary>
  40. /// Gets or sets the selected item.
  41. /// </summary>
  42. public object SelectedItem { get; set; }
  43. /// <summary>
  44. /// Gets or sets the list of items to select.
  45. /// </summary>
  46. public IList SelectedItems { get; private set; }
  47. /// <summary>
  48. /// Gets or sets the item template
  49. /// </summary>
  50. public DataTemplate FullModeItemTemplate { get; set; }
  51. /// <summary>
  52. /// Whether the picker page is open or not.
  53. /// </summary>
  54. private bool IsOpen
  55. {
  56. get { return (bool)GetValue(IsOpenProperty); }
  57. set { SetValue(IsOpenProperty, value); }
  58. }
  59. private static readonly DependencyProperty IsOpenProperty =
  60. DependencyProperty.Register("IsOpen",
  61. typeof(bool),
  62. typeof(ListPickerPage),
  63. new PropertyMetadata(false, new PropertyChangedCallback(OnIsOpenChanged)));
  64. private static void OnIsOpenChanged(DependencyObject o, DependencyPropertyChangedEventArgs e)
  65. {
  66. (o as ListPickerPage).OnIsOpenChanged();
  67. }
  68. private void OnIsOpenChanged()
  69. {
  70. UpdateVisualState(true);
  71. }
  72. /// <summary>
  73. /// Creates a list picker page.
  74. /// </summary>
  75. public ListPickerPage()
  76. {
  77. InitializeComponent();
  78. Items = new List<object>();
  79. SelectedItems = new List<object>();
  80. Loaded += OnLoaded;
  81. Unloaded += OnUnloaded;
  82. }
  83. private void OnLoaded(object sender, RoutedEventArgs e)
  84. {
  85. OrientationChanged += OnOrientationChanged;
  86. _lastOrientation = Orientation;
  87. // Customize the ApplicationBar Buttons by providing the right text
  88. if (null != ApplicationBar)
  89. {
  90. foreach (object obj in ApplicationBar.Buttons)
  91. {
  92. IApplicationBarIconButton button = obj as IApplicationBarIconButton;
  93. if (null != button)
  94. {
  95. if ("DONE" == button.Text)
  96. {
  97. button.Text = LocalizedResources.ControlResources.DateTimePickerDoneText;
  98. button.Click += OnDoneButtonClick;
  99. }
  100. else if ("CANCEL" == button.Text)
  101. {
  102. button.Text = LocalizedResources.ControlResources.DateTimePickerCancelText;
  103. button.Click += OnCancelButtonClick;
  104. }
  105. }
  106. }
  107. }
  108. // Add a projection for each list item and turn it to -90
  109. // (rotationX) so it is hidden.
  110. SetupListItems(-90);
  111. PlaneProjection headerProjection = (PlaneProjection)HeaderTitle.Projection;
  112. if (null == headerProjection)
  113. {
  114. headerProjection = new PlaneProjection();
  115. HeaderTitle.Projection = headerProjection;
  116. }
  117. headerProjection.RotationX = -90;
  118. Picker.Opacity = 1;
  119. Dispatcher.BeginInvoke(() =>
  120. {
  121. IsOpen = true;
  122. });
  123. }
  124. private void OnUnloaded(object sender, RoutedEventArgs e)
  125. {
  126. OrientationChanged -= OnOrientationChanged;
  127. }
  128. private void SetupListItems(double degree)
  129. {
  130. _itemsToAnimate = ItemsControlExtensions.GetItemsInViewPort(Picker);
  131. for (int i = 0; i < _itemsToAnimate.Count; i++)
  132. {
  133. FrameworkElement item = (FrameworkElement)_itemsToAnimate[i].Target;
  134. if (null != item)
  135. {
  136. PlaneProjection p = (PlaneProjection)item.Projection;
  137. if (null == p)
  138. {
  139. p = new PlaneProjection();
  140. item.Projection = p;
  141. }
  142. p.RotationX = degree;
  143. }
  144. }
  145. }
  146. /// <summary>
  147. /// Called when a page becomes the active page in a frame.
  148. /// </summary>
  149. /// <param name="e">An object that contains the event data.</param>
  150. protected override void OnNavigatedTo(NavigationEventArgs e)
  151. {
  152. if (null == e)
  153. {
  154. throw new ArgumentNullException("e");
  155. }
  156. base.OnNavigatedTo(e);
  157. // Restore Value if returning to application (to avoid inconsistent state)
  158. if (State.ContainsKey(StateKey_Value))
  159. {
  160. State.Remove(StateKey_Value);
  161. // Back out from picker page for consistency with behavior of core pickers in this scenario
  162. if (NavigationService.CanGoBack)
  163. {
  164. NavigationService.GoBack();
  165. return;
  166. }
  167. }
  168. // Automatically uppercase the text for the header.
  169. if (null != HeaderText)
  170. {
  171. HeaderTitle.Text = HeaderText.ToUpper(CultureInfo.CurrentCulture);
  172. }
  173. Picker.DataContext = Items;
  174. Picker.SelectionMode = SelectionMode;
  175. if (null != FullModeItemTemplate)
  176. {
  177. Picker.ItemTemplate = FullModeItemTemplate;
  178. }
  179. if (SelectionMode == SelectionMode.Single)
  180. {
  181. ApplicationBar.IsVisible = false;
  182. Picker.SelectedItem = SelectedItem;
  183. }
  184. else
  185. {
  186. ApplicationBar.IsVisible = true;
  187. Picker.ItemContainerStyle = (Style)Resources["ListBoxItemCheckedStyle"];
  188. foreach (object item in Items)
  189. {
  190. if (null != SelectedItems && SelectedItems.Contains(item))
  191. {
  192. Picker.SelectedItems.Add(item);
  193. }
  194. }
  195. }
  196. }
  197. private void OnDoneButtonClick(object sender, EventArgs e)
  198. {
  199. // Commit the value and close
  200. SelectedItem = Picker.SelectedItem;
  201. SelectedItems = Picker.SelectedItems;
  202. ClosePickerPage();
  203. }
  204. private void OnCancelButtonClick(object sender, EventArgs e)
  205. {
  206. // Close without committing a value
  207. SelectedItem = null;
  208. SelectedItems = null;
  209. ClosePickerPage();
  210. }
  211. /// <summary>
  212. /// Called when the Back key is pressed.
  213. /// </summary>
  214. /// <param name="e">Event arguments.</param>
  215. protected override void OnBackKeyPress(CancelEventArgs e)
  216. {
  217. if (null == e)
  218. {
  219. throw new ArgumentNullException("e");
  220. }
  221. // Cancel back action so we can play the Close state animation (then go back)
  222. e.Cancel = true;
  223. SelectedItem = null;
  224. SelectedItems = null;
  225. ClosePickerPage();
  226. }
  227. private void ClosePickerPage()
  228. {
  229. // Prevent user from selecting an item as the picker is closing,
  230. // disabling the control would cause the UI to change so instead
  231. // it's hidden from hittesting.
  232. Picker.IsHitTestVisible = false;
  233. IsOpen = false;
  234. }
  235. private void OnClosedStoryboardCompleted(object sender, EventArgs e)
  236. {
  237. // Close the picker page
  238. if (NavigationService.CanGoBack)
  239. {
  240. NavigationService.GoBack();
  241. }
  242. }
  243. /// <summary>
  244. /// Called when a page is no longer the active page in a frame.
  245. /// </summary>
  246. /// <param name="e">An object that contains the event data.</param>
  247. protected override void OnNavigatedFrom(NavigationEventArgs e)
  248. {
  249. if (null == e)
  250. {
  251. throw new ArgumentNullException("e");
  252. }
  253. base.OnNavigatedFrom(e);
  254. // Save Value if navigating away from application
  255. if (e.Uri.IsExternalNavigation())
  256. {
  257. State[StateKey_Value] = StateKey_Value;
  258. }
  259. }
  260. private void OnOrientationChanged(object sender, OrientationChangedEventArgs e)
  261. {
  262. PageOrientation newOrientation = e.Orientation;
  263. RotateTransition transitionElement = new RotateTransition();
  264. // Adjust padding if possible
  265. if (null != MainGrid)
  266. {
  267. switch (newOrientation)
  268. {
  269. case PageOrientation.Portrait:
  270. case PageOrientation.PortraitUp:
  271. HeaderTitle.Margin = new Thickness(24, 12, 12, 12);
  272. Picker.Margin = new Thickness(24, 12, 0, 0);
  273. transitionElement.Mode = (_lastOrientation == PageOrientation.LandscapeLeft) ?
  274. RotateTransitionMode.In90Counterclockwise : RotateTransitionMode.In90Clockwise;
  275. break;
  276. case PageOrientation.Landscape:
  277. case PageOrientation.LandscapeLeft:
  278. HeaderTitle.Margin = new Thickness(24, 24, 0, 0);
  279. Picker.Margin = new Thickness(24, 24, 0, 0);
  280. transitionElement.Mode = (_lastOrientation == PageOrientation.LandscapeRight) ?
  281. RotateTransitionMode.In180Counterclockwise : RotateTransitionMode.In90Clockwise;
  282. break;
  283. case PageOrientation.LandscapeRight:
  284. HeaderTitle.Margin = new Thickness(24, 24, 0, 0);
  285. Picker.Margin = new Thickness(24, 24, 0, 0);
  286. transitionElement.Mode = (_lastOrientation == PageOrientation.PortraitUp) ?
  287. RotateTransitionMode.In90Counterclockwise : RotateTransitionMode.In180Clockwise;
  288. break;
  289. }
  290. }
  291. PhoneApplicationPage phoneApplicationPage = (PhoneApplicationPage)(((PhoneApplicationFrame)Application.Current.RootVisual)).Content;
  292. ITransition transition = transitionElement.GetTransition(phoneApplicationPage);
  293. transition.Completed += delegate
  294. {
  295. transition.Stop();
  296. };
  297. transition.Begin();
  298. _lastOrientation = newOrientation;
  299. }
  300. private void UpdateVisualState(bool useTransitions)
  301. {
  302. if (useTransitions)
  303. {
  304. // If the Picker is scrolling stop it from moving, this is both
  305. // consistant with Metro and allows for attaching the animations
  306. // to the correct, in view items.
  307. ScrollViewer scrollViewer = Picker.GetVisualChildren().OfType<ScrollViewer>().FirstOrDefault();
  308. if (scrollViewer != null)
  309. {
  310. scrollViewer.ScrollToVerticalOffset(scrollViewer.VerticalOffset);
  311. }
  312. if (!IsOpen)
  313. {
  314. SetupListItems(0);
  315. }
  316. Storyboard mainBoard = new Storyboard();
  317. Storyboard headerBoard = AnimationForElement(HeaderTitle, 0);
  318. mainBoard.Children.Add(headerBoard);
  319. for (int i = 0; i < _itemsToAnimate.Count; i++)
  320. {
  321. FrameworkElement element = (FrameworkElement)_itemsToAnimate[i].Target;
  322. Storyboard board = AnimationForElement(element, i + 1);
  323. mainBoard.Children.Add(board);
  324. }
  325. if (!IsOpen)
  326. {
  327. mainBoard.Completed += OnClosedStoryboardCompleted;
  328. }
  329. mainBoard.Begin();
  330. }
  331. else if (!IsOpen)
  332. {
  333. OnClosedStoryboardCompleted(null, null);
  334. }
  335. }
  336. private Storyboard AnimationForElement(FrameworkElement element, int index)
  337. {
  338. double delay = 30;
  339. double duration = (IsOpen) ? 350 : 250;
  340. double from = (IsOpen) ? -45 : 0;
  341. double to = (IsOpen) ? 0 : 90;
  342. ExponentialEase ee = new ExponentialEase()
  343. {
  344. EasingMode = (IsOpen) ? EasingMode.EaseOut : EasingMode.EaseIn,
  345. Exponent = 5,
  346. };
  347. DoubleAnimation anim = new DoubleAnimation()
  348. {
  349. Duration = new Duration(TimeSpan.FromMilliseconds(duration)),
  350. From = from,
  351. To = to,
  352. EasingFunction = ee,
  353. };
  354. Storyboard.SetTarget(anim, element);
  355. Storyboard.SetTargetProperty(anim, new PropertyPath("(UIElement.Projection).(PlaneProjection.RotationX)"));
  356. Storyboard board = new Storyboard();
  357. board.BeginTime = TimeSpan.FromMilliseconds(delay * index);
  358. board.Children.Add(anim);
  359. return board;
  360. }
  361. private void OnPickerTapped(object sender, System.Windows.Input.GestureEventArgs e)
  362. {
  363. // We listen to the tap event because SelectionChanged does not fire if the user picks the already selected item.
  364. // Only close the page in Single Selection mode.
  365. if (SelectionMode == SelectionMode.Single)
  366. {
  367. // Commit the value and close
  368. SelectedItem = Picker.SelectedItem;
  369. ClosePickerPage();
  370. }
  371. }
  372. }
  373. }