LayoutAwarePage.cpp 18 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452
  1. //*********************************************************
  2. //
  3. // Copyright (c) Microsoft. All rights reserved.
  4. // THIS CODE IS PROVIDED *AS IS* WITHOUT WARRANTY OF
  5. // ANY KIND, EITHER EXPRESS OR IMPLIED, INCLUDING ANY
  6. // IMPLIED WARRANTIES OF FITNESS FOR A PARTICULAR
  7. // PURPOSE, MERCHANTABILITY, OR NON-INFRINGEMENT.
  8. //
  9. //*********************************************************
  10. #include "pch.h"
  11. #include "LayoutAwarePage.h"
  12. #include "SuspensionManager.h"
  13. using namespace SDKSample::Common;
  14. using namespace Platform;
  15. using namespace Platform::Collections;
  16. using namespace Windows::Foundation;
  17. using namespace Windows::Foundation::Collections;
  18. using namespace Windows::System;
  19. using namespace Windows::UI::Core;
  20. using namespace Windows::UI::ViewManagement;
  21. using namespace Windows::UI::Xaml;
  22. using namespace Windows::UI::Xaml::Controls;
  23. using namespace Windows::UI::Xaml::Interop;
  24. using namespace Windows::UI::Xaml::Navigation;
  25. /// <summary>
  26. /// Initializes a new instance of the <see cref="LayoutAwarePage"/> class.
  27. /// </summary>
  28. LayoutAwarePage::LayoutAwarePage()
  29. {
  30. if (Windows::ApplicationModel::DesignMode::DesignModeEnabled)
  31. {
  32. return;
  33. }
  34. // Create an empty default view model
  35. DefaultViewModel = ref new Map<String^, Object^>(std::less<String^>());
  36. // When this page is part of the visual tree make two changes:
  37. // 1) Map application view state to visual state for the page
  38. // 2) Handle keyboard and mouse navigation requests
  39. Loaded += ref new RoutedEventHandler(this, &LayoutAwarePage::OnLoaded);
  40. // Undo the same changes when the page is no longer visible
  41. Unloaded += ref new RoutedEventHandler(this, &LayoutAwarePage::OnUnloaded);
  42. }
  43. static DependencyProperty^ _defaultViewModelProperty =
  44. DependencyProperty::Register("DefaultViewModel",
  45. TypeName(IObservableMap<String^, Object^>::typeid), TypeName(LayoutAwarePage::typeid), nullptr);
  46. /// <summary>
  47. /// Identifies the <see cref="DefaultViewModel"/> dependency property.
  48. /// </summary>
  49. DependencyProperty^ LayoutAwarePage::DefaultViewModelProperty::get()
  50. {
  51. return _defaultViewModelProperty;
  52. }
  53. /// <summary>
  54. /// Gets an implementation of <see cref="IObservableMap&lt;String, Object&gt;"/> designed to be
  55. /// used as a trivial view model.
  56. /// </summary>
  57. IObservableMap<String^, Object^>^ LayoutAwarePage::DefaultViewModel::get()
  58. {
  59. return safe_cast<IObservableMap<String^, Object^>^>(GetValue(DefaultViewModelProperty));
  60. }
  61. /// <summary>
  62. /// Sets an implementation of <see cref="IObservableMap&lt;String, Object&gt;"/> designed to be
  63. /// used as a trivial view model.
  64. /// </summary>
  65. void LayoutAwarePage::DefaultViewModel::set(IObservableMap<String^, Object^>^ value)
  66. {
  67. SetValue(DefaultViewModelProperty, value);
  68. }
  69. /// <summary>
  70. /// Invoked when the page is part of the visual tree
  71. /// </summary>
  72. /// <param name="sender">Instance that triggered the event.</param>
  73. /// <param name="e">Event data describing the conditions that led to the event.</param>
  74. void LayoutAwarePage::OnLoaded(Object^ sender, Windows::UI::Xaml::RoutedEventArgs^ e)
  75. {
  76. this->StartLayoutUpdates(sender, e);
  77. // Keyboard and mouse navigation only apply when occupying the entire window
  78. if (this->ActualHeight == Window::Current->Bounds.Height &&
  79. this->ActualWidth == Window::Current->Bounds.Width)
  80. {
  81. // Listen to the window directly so focus isn't required
  82. _acceleratorKeyEventToken = Window::Current->CoreWindow->Dispatcher->AcceleratorKeyActivated +=
  83. ref new TypedEventHandler<CoreDispatcher^, AcceleratorKeyEventArgs^>(this,
  84. &LayoutAwarePage::CoreDispatcher_AcceleratorKeyActivated);
  85. _pointerPressedEventToken = Window::Current->CoreWindow->PointerPressed +=
  86. ref new TypedEventHandler<CoreWindow^, PointerEventArgs^>(this,
  87. &LayoutAwarePage::CoreWindow_PointerPressed);
  88. _navigationShortcutsRegistered = true;
  89. }
  90. }
  91. /// <summary>
  92. /// Invoked when the page is removed from visual tree
  93. /// </summary>
  94. /// <param name="sender">Instance that triggered the event.</param>
  95. /// <param name="e">Event data describing the conditions that led to the event.</param>
  96. void LayoutAwarePage::OnUnloaded(Object^ sender, Windows::UI::Xaml::RoutedEventArgs^ e)
  97. {
  98. if (_navigationShortcutsRegistered)
  99. {
  100. Window::Current->CoreWindow->Dispatcher->AcceleratorKeyActivated -= _acceleratorKeyEventToken;
  101. Window::Current->CoreWindow->PointerPressed -= _pointerPressedEventToken;
  102. _navigationShortcutsRegistered = false;
  103. }
  104. StopLayoutUpdates(sender, e);
  105. }
  106. #pragma region Navigation support
  107. /// <summary>
  108. /// Invoked as an event handler to navigate backward in the page's associated <see cref="Frame"/>
  109. /// until it reaches the top of the navigation stack.
  110. /// </summary>
  111. /// <param name="sender">Instance that triggered the event.</param>
  112. /// <param name="e">Event data describing the conditions that led to the event.</param>
  113. void LayoutAwarePage::GoHome(Object^ sender, RoutedEventArgs^ e)
  114. {
  115. (void) sender; // Unused parameter
  116. (void) e; // Unused parameter
  117. // Use the navigation frame to return to the topmost page
  118. if (Frame != nullptr)
  119. {
  120. while (Frame->CanGoBack)
  121. {
  122. Frame->GoBack();
  123. }
  124. }
  125. }
  126. /// <summary>
  127. /// Invoked as an event handler to navigate backward in the navigation stack
  128. /// associated with this page's <see cref="Frame"/>.
  129. /// </summary>
  130. /// <param name="sender">Instance that triggered the event.</param>
  131. /// <param name="e">Event data describing the conditions that led to the event.</param>
  132. void LayoutAwarePage::GoBack(Object^ sender, RoutedEventArgs^ e)
  133. {
  134. (void) sender; // Unused parameter
  135. (void) e; // Unused parameter
  136. // Use the navigation frame to return to the previous page
  137. if (Frame != nullptr && Frame->CanGoBack)
  138. {
  139. Frame->GoBack();
  140. }
  141. }
  142. /// <summary>
  143. /// Invoked as an event handler to navigate forward in the navigation stack
  144. /// associated with this page's <see cref="Frame"/>.
  145. /// </summary>
  146. /// <param name="sender">Instance that triggered the event.</param>
  147. /// <param name="e">Event data describing the conditions that led to the event.</param>
  148. void LayoutAwarePage::GoForward(Object^ sender, RoutedEventArgs^ e)
  149. {
  150. (void) sender; // Unused parameter
  151. (void) e; // Unused parameter
  152. // Use the navigation frame to advance to the next page
  153. if (Frame != nullptr && Frame->CanGoForward)
  154. {
  155. Frame->GoForward();
  156. }
  157. }
  158. /// <summary>
  159. /// Invoked on every keystroke, including system keys such as Alt key combinations, when
  160. /// this page is active and occupies the entire window. Used to detect keyboard navigation
  161. /// between pages even when the page itself doesn't have focus.
  162. /// </summary>
  163. /// <param name="sender">Instance that triggered the event.</param>
  164. /// <param name="args">Event data describing the conditions that led to the event.</param>
  165. void LayoutAwarePage::CoreDispatcher_AcceleratorKeyActivated(CoreDispatcher^ sender, AcceleratorKeyEventArgs^ args)
  166. {
  167. auto virtualKey = args->VirtualKey;
  168. // Only investigate further when Left, Right, or the dedicated Previous or Next keys
  169. // are pressed
  170. if ((args->EventType == CoreAcceleratorKeyEventType::SystemKeyDown ||
  171. args->EventType == CoreAcceleratorKeyEventType::KeyDown) &&
  172. (virtualKey == VirtualKey::Left || virtualKey == VirtualKey::Right ||
  173. (int)virtualKey == 166 || (int)virtualKey == 167))
  174. {
  175. auto coreWindow = Window::Current->CoreWindow;
  176. auto downState = Windows::UI::Core::CoreVirtualKeyStates::Down;
  177. bool menuKey = (coreWindow->GetKeyState(VirtualKey::Menu) & downState) == downState;
  178. bool controlKey = (coreWindow->GetKeyState(VirtualKey::Control) & downState) == downState;
  179. bool shiftKey = (coreWindow->GetKeyState(VirtualKey::Shift) & downState) == downState;
  180. bool noModifiers = !menuKey && !controlKey && !shiftKey;
  181. bool onlyAlt = menuKey && !controlKey && !shiftKey;
  182. if (((int)virtualKey == 166 && noModifiers) ||
  183. (virtualKey == VirtualKey::Left && onlyAlt))
  184. {
  185. // When the previous key or Alt+Left are pressed navigate back
  186. args->Handled = true;
  187. GoBack(this, ref new RoutedEventArgs());
  188. }
  189. else if (((int)virtualKey == 167 && noModifiers) ||
  190. (virtualKey == VirtualKey::Right && onlyAlt))
  191. {
  192. // When the next key or Alt+Right are pressed navigate forward
  193. args->Handled = true;
  194. GoForward(this, ref new RoutedEventArgs());
  195. }
  196. }
  197. }
  198. /// <summary>
  199. /// Invoked on every mouse click, touch screen tap, or equivalent interaction when this
  200. /// page is active and occupies the entire window. Used to detect browser-style next and
  201. /// previous mouse button clicks to navigate between pages.
  202. /// </summary>
  203. /// <param name="sender">Instance that triggered the event.</param>
  204. /// <param name="args">Event data describing the conditions that led to the event.</param>
  205. void LayoutAwarePage::CoreWindow_PointerPressed(CoreWindow^ sender, PointerEventArgs^ args)
  206. {
  207. auto properties = args->CurrentPoint->Properties;
  208. // Ignore button chords with the left, right, and middle buttons
  209. if (properties->IsLeftButtonPressed || properties->IsRightButtonPressed ||
  210. properties->IsMiddleButtonPressed) return;
  211. // If back or forward are pressed (but not both) navigate appropriately
  212. bool backPressed = properties->IsXButton1Pressed;
  213. bool forwardPressed = properties->IsXButton2Pressed;
  214. if (backPressed ^ forwardPressed)
  215. {
  216. args->Handled = true;
  217. if (backPressed) GoBack(this, ref new RoutedEventArgs());
  218. if (forwardPressed) GoForward(this, ref new RoutedEventArgs());
  219. }
  220. }
  221. #pragma endregion
  222. #pragma region Visual state switching
  223. /// <summary>
  224. /// Invoked as an event handler, typically on the <see cref="Loaded"/> event of a
  225. /// <see cref="Control"/> within the page, to indicate that the sender should start receiving
  226. /// visual state management changes that correspond to application view state changes.
  227. /// </summary>
  228. /// <param name="sender">Instance of <see cref="Control"/> that supports visual state management
  229. /// corresponding to view states.</param>
  230. /// <param name="e">Event data that describes how the request was made.</param>
  231. /// <remarks>The current view state will immediately be used to set the corresponding visual state
  232. /// when layout updates are requested. A corresponding <see cref="Unloaded"/> event handler
  233. /// connected to <see cref="StopLayoutUpdates"/> is strongly encouraged. Instances of
  234. /// <see cref="LayoutAwarePage"/> automatically invoke these handlers in their Loaded and Unloaded
  235. /// events.</remarks>
  236. /// <seealso cref="DetermineVisualState"/>
  237. /// <seealso cref="InvalidateVisualState"/>
  238. void LayoutAwarePage::StartLayoutUpdates(Object^ sender, RoutedEventArgs^ e)
  239. {
  240. (void) e; // Unused parameter
  241. auto control = safe_cast<Control^>(sender);
  242. if (_layoutAwareControls == nullptr)
  243. {
  244. // Start listening to view state changes when there are controls interested in updates
  245. _layoutAwareControls = ref new Vector<Control^>();
  246. _windowSizeEventToken = Window::Current->SizeChanged += ref new WindowSizeChangedEventHandler(this, &LayoutAwarePage::WindowSizeChanged);
  247. // Page receives notifications for children. Protect the page until we stopped layout updates for all controls.
  248. _this = this;
  249. }
  250. _layoutAwareControls->Append(control);
  251. // Set the initial visual state of the control
  252. VisualStateManager::GoToState(control, DetermineVisualState(ApplicationView::Value), false);
  253. }
  254. void LayoutAwarePage::WindowSizeChanged(Object^ sender, Windows::UI::Core::WindowSizeChangedEventArgs^ e)
  255. {
  256. (void) sender; // Unused parameter
  257. (void) e; // Unused parameter
  258. InvalidateVisualState();
  259. }
  260. /// <summary>
  261. /// Invoked as an event handler, typically on the <see cref="Unloaded"/> event of a
  262. /// <see cref="Control"/>, to indicate that the sender should start receiving visual state
  263. /// management changes that correspond to application view state changes.
  264. /// </summary>
  265. /// <param name="sender">Instance of <see cref="Control"/> that supports visual state management
  266. /// corresponding to view states.</param>
  267. /// <param name="e">Event data that describes how the request was made.</param>
  268. /// <remarks>The current view state will immediately be used to set the corresponding visual state
  269. /// when layout updates are requested.</remarks>
  270. /// <seealso cref="StartLayoutUpdates"/>
  271. void LayoutAwarePage::StopLayoutUpdates(Object^ sender, RoutedEventArgs^ e)
  272. {
  273. (void) e; // Unused parameter
  274. auto control = safe_cast<Control^>(sender);
  275. unsigned int index;
  276. if (_layoutAwareControls != nullptr && _layoutAwareControls->IndexOf(control, &index))
  277. {
  278. _layoutAwareControls->RemoveAt(index);
  279. if (_layoutAwareControls->Size == 0)
  280. {
  281. // Stop listening to view state changes when no controls are interested in updates
  282. Window::Current->SizeChanged -= _windowSizeEventToken;
  283. _layoutAwareControls = nullptr;
  284. // Last control has received the Unload notification.
  285. _this = nullptr;
  286. }
  287. }
  288. }
  289. /// <summary>
  290. /// Translates <see cref="ApplicationViewState"/> values into strings for visual state management
  291. /// within the page. The default implementation uses the names of enum values. Subclasses may
  292. /// override this method to control the mapping scheme used.
  293. /// </summary>
  294. /// <param name="viewState">View state for which a visual state is desired.</param>
  295. /// <returns>Visual state name used to drive the <see cref="VisualStateManager"/></returns>
  296. /// <seealso cref="InvalidateVisualState"/>
  297. String^ LayoutAwarePage::DetermineVisualState(ApplicationViewState viewState)
  298. {
  299. switch (viewState)
  300. {
  301. case ApplicationViewState::Filled:
  302. return "Filled";
  303. case ApplicationViewState::Snapped:
  304. return "Snapped";
  305. case ApplicationViewState::FullScreenPortrait:
  306. return "FullScreenPortrait";
  307. case ApplicationViewState::FullScreenLandscape:
  308. default:
  309. return "FullScreenLandscape";
  310. }
  311. }
  312. /// <summary>
  313. /// Updates all controls that are listening for visual state changes with the correct visual
  314. /// state.
  315. /// </summary>
  316. /// <remarks>
  317. /// Typically used in conjunction with overriding <see cref="DetermineVisualState"/> to
  318. /// signal that a different value may be returned even though the view state has not changed.
  319. /// </remarks>
  320. void LayoutAwarePage::InvalidateVisualState()
  321. {
  322. if (_layoutAwareControls != nullptr)
  323. {
  324. String^ visualState = DetermineVisualState(ApplicationView::Value);
  325. auto controlIterator = _layoutAwareControls->First();
  326. while (controlIterator->HasCurrent)
  327. {
  328. auto control = controlIterator->Current;
  329. VisualStateManager::GoToState(control, visualState, false);
  330. controlIterator->MoveNext();
  331. }
  332. }
  333. }
  334. #pragma endregion
  335. #pragma region Process lifetime management
  336. /// <summary>
  337. /// Invoked when this page is about to be displayed in a Frame.
  338. /// </summary>
  339. /// <param name="e">Event data that describes how this page was reached. The Parameter
  340. /// property provides the group to be displayed.</param>
  341. void LayoutAwarePage::OnNavigatedTo(NavigationEventArgs^ e)
  342. {
  343. // Returning to a cached page through navigation shouldn't trigger state loading
  344. if (_pageKey != nullptr) return;
  345. auto frameState = SuspensionManager::SessionStateForFrame(Frame);
  346. _pageKey = "Page-" + Frame->BackStackDepth;
  347. if (e->NavigationMode == NavigationMode::New)
  348. {
  349. // Clear existing state for forward navigation when adding a new page to the
  350. // navigation stack
  351. auto nextPageKey = _pageKey;
  352. int nextPageIndex = Frame->BackStackDepth;
  353. while (frameState->HasKey(nextPageKey))
  354. {
  355. frameState->Remove(nextPageKey);
  356. nextPageIndex++;
  357. nextPageKey = "Page-" + nextPageIndex;
  358. }
  359. // Pass the navigation parameter to the new page
  360. LoadState(e->Parameter, nullptr);
  361. }
  362. else
  363. {
  364. // Pass the navigation parameter and preserved page state to the page, using
  365. // the same strategy for loading suspended state and recreating pages discarded
  366. // from cache
  367. LoadState(e->Parameter, safe_cast<IMap<String^, Object^>^>(frameState->Lookup(_pageKey)));
  368. }
  369. }
  370. /// <summary>
  371. /// Invoked when this page will no longer be displayed in a Frame.
  372. /// </summary>
  373. /// <param name="e">Event data that describes how this page was reached. The Parameter
  374. /// property provides the group to be displayed.</param>
  375. void LayoutAwarePage::OnNavigatedFrom(NavigationEventArgs^ e)
  376. {
  377. auto frameState = SuspensionManager::SessionStateForFrame(Frame);
  378. auto pageState = ref new Map<String^, Object^>();
  379. SaveState(pageState);
  380. frameState->Insert(_pageKey, pageState);
  381. }
  382. /// <summary>
  383. /// Populates the page with content passed during navigation. Any saved state is also
  384. /// provided when recreating a page from a prior session.
  385. /// </summary>
  386. /// <param name="navigationParameter">The parameter value passed to
  387. /// <see cref="Frame.Navigate(Type, Object)"/> when this page was initially requested.
  388. /// </param>
  389. /// <param name="pageState">A map of state preserved by this page during an earlier
  390. /// session. This will be null the first time a page is visited.</param>
  391. void LayoutAwarePage::LoadState(Object^ navigationParameter, IMap<String^, Object^>^ pageState)
  392. {
  393. }
  394. /// <summary>
  395. /// Preserves state associated with this page in case the application is suspended or the
  396. /// page is discarded from the navigation cache. Values must conform to the serialization
  397. /// requirements of <see cref="SuspensionManager.SessionState"/>.
  398. /// </summary>
  399. /// <param name="pageState">An empty map to be populated with serializable state.</param>
  400. void LayoutAwarePage::SaveState(IMap<String^, Object^>^ pageState)
  401. {
  402. }
  403. #pragma endregion