Path: blob/master/samples/winrt/ImageManipulations/common/LayoutAwarePage.cpp
16344 views
//*********************************************************1//2// Copyright (c) Microsoft. All rights reserved.3// THIS CODE IS PROVIDED *AS IS* WITHOUT WARRANTY OF4// ANY KIND, EITHER EXPRESS OR IMPLIED, INCLUDING ANY5// IMPLIED WARRANTIES OF FITNESS FOR A PARTICULAR6// PURPOSE, MERCHANTABILITY, OR NON-INFRINGEMENT.7//8//*********************************************************910#include "pch.h"11#include "LayoutAwarePage.h"12#include "SuspensionManager.h"1314using namespace SDKSample::Common;1516using namespace Platform;17using namespace Platform::Collections;18using namespace Windows::Foundation;19using namespace Windows::Foundation::Collections;20using namespace Windows::System;21using namespace Windows::UI::Core;22using namespace Windows::UI::ViewManagement;23using namespace Windows::UI::Xaml;24using namespace Windows::UI::Xaml::Controls;25using namespace Windows::UI::Xaml::Interop;26using namespace Windows::UI::Xaml::Navigation;2728/// <summary>29/// Initializes a new instance of the <see cref="LayoutAwarePage"/> class.30/// </summary>31LayoutAwarePage::LayoutAwarePage()32{33if (Windows::ApplicationModel::DesignMode::DesignModeEnabled)34{35return;36}3738// Create an empty default view model39DefaultViewModel = ref new Map<String^, Object^>(std::less<String^>());4041// When this page is part of the visual tree make two changes:42// 1) Map application view state to visual state for the page43// 2) Handle keyboard and mouse navigation requests44Loaded += ref new RoutedEventHandler(this, &LayoutAwarePage::OnLoaded);4546// Undo the same changes when the page is no longer visible47Unloaded += ref new RoutedEventHandler(this, &LayoutAwarePage::OnUnloaded);48}4950static DependencyProperty^ _defaultViewModelProperty =51DependencyProperty::Register("DefaultViewModel",52TypeName(IObservableMap<String^, Object^>::typeid), TypeName(LayoutAwarePage::typeid), nullptr);5354/// <summary>55/// Identifies the <see cref="DefaultViewModel"/> dependency property.56/// </summary>57DependencyProperty^ LayoutAwarePage::DefaultViewModelProperty::get()58{59return _defaultViewModelProperty;60}6162/// <summary>63/// Gets an implementation of <see cref="IObservableMap<String, Object>"/> designed to be64/// used as a trivial view model.65/// </summary>66IObservableMap<String^, Object^>^ LayoutAwarePage::DefaultViewModel::get()67{68return safe_cast<IObservableMap<String^, Object^>^>(GetValue(DefaultViewModelProperty));69}7071/// <summary>72/// Sets an implementation of <see cref="IObservableMap<String, Object>"/> designed to be73/// used as a trivial view model.74/// </summary>75void LayoutAwarePage::DefaultViewModel::set(IObservableMap<String^, Object^>^ value)76{77SetValue(DefaultViewModelProperty, value);78}7980/// <summary>81/// Invoked when the page is part of the visual tree82/// </summary>83/// <param name="sender">Instance that triggered the event.</param>84/// <param name="e">Event data describing the conditions that led to the event.</param>85void LayoutAwarePage::OnLoaded(Object^ sender, Windows::UI::Xaml::RoutedEventArgs^ e)86{87this->StartLayoutUpdates(sender, e);8889// Keyboard and mouse navigation only apply when occupying the entire window90if (this->ActualHeight == Window::Current->Bounds.Height &&91this->ActualWidth == Window::Current->Bounds.Width)92{93// Listen to the window directly so focus isn't required94_acceleratorKeyEventToken = Window::Current->CoreWindow->Dispatcher->AcceleratorKeyActivated +=95ref new TypedEventHandler<CoreDispatcher^, AcceleratorKeyEventArgs^>(this,96&LayoutAwarePage::CoreDispatcher_AcceleratorKeyActivated);97_pointerPressedEventToken = Window::Current->CoreWindow->PointerPressed +=98ref new TypedEventHandler<CoreWindow^, PointerEventArgs^>(this,99&LayoutAwarePage::CoreWindow_PointerPressed);100_navigationShortcutsRegistered = true;101}102}103104/// <summary>105/// Invoked when the page is removed from visual tree106/// </summary>107/// <param name="sender">Instance that triggered the event.</param>108/// <param name="e">Event data describing the conditions that led to the event.</param>109void LayoutAwarePage::OnUnloaded(Object^ sender, Windows::UI::Xaml::RoutedEventArgs^ e)110{111if (_navigationShortcutsRegistered)112{113Window::Current->CoreWindow->Dispatcher->AcceleratorKeyActivated -= _acceleratorKeyEventToken;114Window::Current->CoreWindow->PointerPressed -= _pointerPressedEventToken;115_navigationShortcutsRegistered = false;116}117StopLayoutUpdates(sender, e);118}119120#pragma region Navigation support121122/// <summary>123/// Invoked as an event handler to navigate backward in the page's associated <see cref="Frame"/>124/// until it reaches the top of the navigation stack.125/// </summary>126/// <param name="sender">Instance that triggered the event.</param>127/// <param name="e">Event data describing the conditions that led to the event.</param>128void LayoutAwarePage::GoHome(Object^ sender, RoutedEventArgs^ e)129{130(void) sender; // Unused parameter131(void) e; // Unused parameter132133// Use the navigation frame to return to the topmost page134if (Frame != nullptr)135{136while (Frame->CanGoBack)137{138Frame->GoBack();139}140}141}142143/// <summary>144/// Invoked as an event handler to navigate backward in the navigation stack145/// associated with this page's <see cref="Frame"/>.146/// </summary>147/// <param name="sender">Instance that triggered the event.</param>148/// <param name="e">Event data describing the conditions that led to the event.</param>149void LayoutAwarePage::GoBack(Object^ sender, RoutedEventArgs^ e)150{151(void) sender; // Unused parameter152(void) e; // Unused parameter153154// Use the navigation frame to return to the previous page155if (Frame != nullptr && Frame->CanGoBack)156{157Frame->GoBack();158}159}160161/// <summary>162/// Invoked as an event handler to navigate forward in the navigation stack163/// associated with this page's <see cref="Frame"/>.164/// </summary>165/// <param name="sender">Instance that triggered the event.</param>166/// <param name="e">Event data describing the conditions that led to the event.</param>167void LayoutAwarePage::GoForward(Object^ sender, RoutedEventArgs^ e)168{169(void) sender; // Unused parameter170(void) e; // Unused parameter171172// Use the navigation frame to advance to the next page173if (Frame != nullptr && Frame->CanGoForward)174{175Frame->GoForward();176}177}178179/// <summary>180/// Invoked on every keystroke, including system keys such as Alt key combinations, when181/// this page is active and occupies the entire window. Used to detect keyboard navigation182/// between pages even when the page itself doesn't have focus.183/// </summary>184/// <param name="sender">Instance that triggered the event.</param>185/// <param name="args">Event data describing the conditions that led to the event.</param>186void LayoutAwarePage::CoreDispatcher_AcceleratorKeyActivated(CoreDispatcher^ sender, AcceleratorKeyEventArgs^ args)187{188auto virtualKey = args->VirtualKey;189190// Only investigate further when Left, Right, or the dedicated Previous or Next keys191// are pressed192if ((args->EventType == CoreAcceleratorKeyEventType::SystemKeyDown ||193args->EventType == CoreAcceleratorKeyEventType::KeyDown) &&194(virtualKey == VirtualKey::Left || virtualKey == VirtualKey::Right ||195(int)virtualKey == 166 || (int)virtualKey == 167))196{197auto coreWindow = Window::Current->CoreWindow;198auto downState = Windows::UI::Core::CoreVirtualKeyStates::Down;199bool menuKey = (coreWindow->GetKeyState(VirtualKey::Menu) & downState) == downState;200bool controlKey = (coreWindow->GetKeyState(VirtualKey::Control) & downState) == downState;201bool shiftKey = (coreWindow->GetKeyState(VirtualKey::Shift) & downState) == downState;202bool noModifiers = !menuKey && !controlKey && !shiftKey;203bool onlyAlt = menuKey && !controlKey && !shiftKey;204205if (((int)virtualKey == 166 && noModifiers) ||206(virtualKey == VirtualKey::Left && onlyAlt))207{208// When the previous key or Alt+Left are pressed navigate back209args->Handled = true;210GoBack(this, ref new RoutedEventArgs());211}212else if (((int)virtualKey == 167 && noModifiers) ||213(virtualKey == VirtualKey::Right && onlyAlt))214{215// When the next key or Alt+Right are pressed navigate forward216args->Handled = true;217GoForward(this, ref new RoutedEventArgs());218}219}220}221222/// <summary>223/// Invoked on every mouse click, touch screen tap, or equivalent interaction when this224/// page is active and occupies the entire window. Used to detect browser-style next and225/// previous mouse button clicks to navigate between pages.226/// </summary>227/// <param name="sender">Instance that triggered the event.</param>228/// <param name="args">Event data describing the conditions that led to the event.</param>229void LayoutAwarePage::CoreWindow_PointerPressed(CoreWindow^ sender, PointerEventArgs^ args)230{231auto properties = args->CurrentPoint->Properties;232233// Ignore button chords with the left, right, and middle buttons234if (properties->IsLeftButtonPressed || properties->IsRightButtonPressed ||235properties->IsMiddleButtonPressed) return;236237// If back or forward are pressed (but not both) navigate appropriately238bool backPressed = properties->IsXButton1Pressed;239bool forwardPressed = properties->IsXButton2Pressed;240if (backPressed ^ forwardPressed)241{242args->Handled = true;243if (backPressed) GoBack(this, ref new RoutedEventArgs());244if (forwardPressed) GoForward(this, ref new RoutedEventArgs());245}246}247248#pragma endregion249250#pragma region Visual state switching251252/// <summary>253/// Invoked as an event handler, typically on the <see cref="Loaded"/> event of a254/// <see cref="Control"/> within the page, to indicate that the sender should start receiving255/// visual state management changes that correspond to application view state changes.256/// </summary>257/// <param name="sender">Instance of <see cref="Control"/> that supports visual state management258/// corresponding to view states.</param>259/// <param name="e">Event data that describes how the request was made.</param>260/// <remarks>The current view state will immediately be used to set the corresponding visual state261/// when layout updates are requested. A corresponding <see cref="Unloaded"/> event handler262/// connected to <see cref="StopLayoutUpdates"/> is strongly encouraged. Instances of263/// <see cref="LayoutAwarePage"/> automatically invoke these handlers in their Loaded and Unloaded264/// events.</remarks>265/// <seealso cref="DetermineVisualState"/>266/// <seealso cref="InvalidateVisualState"/>267void LayoutAwarePage::StartLayoutUpdates(Object^ sender, RoutedEventArgs^ e)268{269(void) e; // Unused parameter270271auto control = safe_cast<Control^>(sender);272if (_layoutAwareControls == nullptr)273{274// Start listening to view state changes when there are controls interested in updates275_layoutAwareControls = ref new Vector<Control^>();276_windowSizeEventToken = Window::Current->SizeChanged += ref new WindowSizeChangedEventHandler(this, &LayoutAwarePage::WindowSizeChanged);277278// Page receives notifications for children. Protect the page until we stopped layout updates for all controls.279_this = this;280}281_layoutAwareControls->Append(control);282283// Set the initial visual state of the control284VisualStateManager::GoToState(control, DetermineVisualState(ApplicationView::Value), false);285}286287void LayoutAwarePage::WindowSizeChanged(Object^ sender, Windows::UI::Core::WindowSizeChangedEventArgs^ e)288{289(void) sender; // Unused parameter290(void) e; // Unused parameter291292InvalidateVisualState();293}294295/// <summary>296/// Invoked as an event handler, typically on the <see cref="Unloaded"/> event of a297/// <see cref="Control"/>, to indicate that the sender should start receiving visual state298/// management changes that correspond to application view state changes.299/// </summary>300/// <param name="sender">Instance of <see cref="Control"/> that supports visual state management301/// corresponding to view states.</param>302/// <param name="e">Event data that describes how the request was made.</param>303/// <remarks>The current view state will immediately be used to set the corresponding visual state304/// when layout updates are requested.</remarks>305/// <seealso cref="StartLayoutUpdates"/>306void LayoutAwarePage::StopLayoutUpdates(Object^ sender, RoutedEventArgs^ e)307{308(void) e; // Unused parameter309310auto control = safe_cast<Control^>(sender);311unsigned int index;312if (_layoutAwareControls != nullptr && _layoutAwareControls->IndexOf(control, &index))313{314_layoutAwareControls->RemoveAt(index);315if (_layoutAwareControls->Size == 0)316{317// Stop listening to view state changes when no controls are interested in updates318Window::Current->SizeChanged -= _windowSizeEventToken;319_layoutAwareControls = nullptr;320// Last control has received the Unload notification.321_this = nullptr;322}323}324}325326/// <summary>327/// Translates <see cref="ApplicationViewState"/> values into strings for visual state management328/// within the page. The default implementation uses the names of enum values. Subclasses may329/// override this method to control the mapping scheme used.330/// </summary>331/// <param name="viewState">View state for which a visual state is desired.</param>332/// <returns>Visual state name used to drive the <see cref="VisualStateManager"/></returns>333/// <seealso cref="InvalidateVisualState"/>334String^ LayoutAwarePage::DetermineVisualState(ApplicationViewState viewState)335{336switch (viewState)337{338case ApplicationViewState::Filled:339return "Filled";340case ApplicationViewState::Snapped:341return "Snapped";342case ApplicationViewState::FullScreenPortrait:343return "FullScreenPortrait";344case ApplicationViewState::FullScreenLandscape:345default:346return "FullScreenLandscape";347}348}349350/// <summary>351/// Updates all controls that are listening for visual state changes with the correct visual352/// state.353/// </summary>354/// <remarks>355/// Typically used in conjunction with overriding <see cref="DetermineVisualState"/> to356/// signal that a different value may be returned even though the view state has not changed.357/// </remarks>358void LayoutAwarePage::InvalidateVisualState()359{360if (_layoutAwareControls != nullptr)361{362String^ visualState = DetermineVisualState(ApplicationView::Value);363auto controlIterator = _layoutAwareControls->First();364while (controlIterator->HasCurrent)365{366auto control = controlIterator->Current;367VisualStateManager::GoToState(control, visualState, false);368controlIterator->MoveNext();369}370}371}372373#pragma endregion374375#pragma region Process lifetime management376377/// <summary>378/// Invoked when this page is about to be displayed in a Frame.379/// </summary>380/// <param name="e">Event data that describes how this page was reached. The Parameter381/// property provides the group to be displayed.</param>382void LayoutAwarePage::OnNavigatedTo(NavigationEventArgs^ e)383{384// Returning to a cached page through navigation shouldn't trigger state loading385if (_pageKey != nullptr) return;386387auto frameState = SuspensionManager::SessionStateForFrame(Frame);388_pageKey = "Page-" + Frame->BackStackDepth;389390if (e->NavigationMode == NavigationMode::New)391{392// Clear existing state for forward navigation when adding a new page to the393// navigation stack394auto nextPageKey = _pageKey;395int nextPageIndex = Frame->BackStackDepth;396while (frameState->HasKey(nextPageKey))397{398frameState->Remove(nextPageKey);399nextPageIndex++;400nextPageKey = "Page-" + nextPageIndex;401}402403// Pass the navigation parameter to the new page404LoadState(e->Parameter, nullptr);405}406else407{408// Pass the navigation parameter and preserved page state to the page, using409// the same strategy for loading suspended state and recreating pages discarded410// from cache411LoadState(e->Parameter, safe_cast<IMap<String^, Object^>^>(frameState->Lookup(_pageKey)));412}413}414415/// <summary>416/// Invoked when this page will no longer be displayed in a Frame.417/// </summary>418/// <param name="e">Event data that describes how this page was reached. The Parameter419/// property provides the group to be displayed.</param>420void LayoutAwarePage::OnNavigatedFrom(NavigationEventArgs^ e)421{422auto frameState = SuspensionManager::SessionStateForFrame(Frame);423auto pageState = ref new Map<String^, Object^>();424SaveState(pageState);425frameState->Insert(_pageKey, pageState);426}427428/// <summary>429/// Populates the page with content passed during navigation. Any saved state is also430/// provided when recreating a page from a prior session.431/// </summary>432/// <param name="navigationParameter">The parameter value passed to433/// <see cref="Frame.Navigate(Type, Object)"/> when this page was initially requested.434/// </param>435/// <param name="pageState">A map of state preserved by this page during an earlier436/// session. This will be null the first time a page is visited.</param>437void LayoutAwarePage::LoadState(Object^ navigationParameter, IMap<String^, Object^>^ pageState)438{439}440441/// <summary>442/// Preserves state associated with this page in case the application is suspended or the443/// page is discarded from the navigation cache. Values must conform to the serialization444/// requirements of <see cref="SuspensionManager.SessionState"/>.445/// </summary>446/// <param name="pageState">An empty map to be populated with serializable state.</param>447void LayoutAwarePage::SaveState(IMap<String^, Object^>^ pageState)448{449}450451#pragma endregion452453454