//********************************************************* // // Copyright (c) Microsoft. All rights reserved. // THIS CODE IS PROVIDED *AS IS* WITHOUT WARRANTY OF // ANY KIND, EITHER EXPRESS OR IMPLIED, INCLUDING ANY // IMPLIED WARRANTIES OF FITNESS FOR A PARTICULAR // PURPOSE, MERCHANTABILITY, OR NON-INFRINGEMENT. // //********************************************************* #include "pch.h" #include "LayoutAwarePage.h" #include "SuspensionManager.h" using namespace SDKSample::Common; using namespace Platform; using namespace Platform::Collections; using namespace Windows::Foundation; using namespace Windows::Foundation::Collections; using namespace Windows::System; using namespace Windows::UI::Core; using namespace Windows::UI::ViewManagement; using namespace Windows::UI::Xaml; using namespace Windows::UI::Xaml::Controls; using namespace Windows::UI::Xaml::Interop; using namespace Windows::UI::Xaml::Navigation; /// /// Initializes a new instance of the class. /// LayoutAwarePage::LayoutAwarePage() { if (Windows::ApplicationModel::DesignMode::DesignModeEnabled) { return; } // Create an empty default view model DefaultViewModel = ref new Map(std::less()); // When this page is part of the visual tree make two changes: // 1) Map application view state to visual state for the page // 2) Handle keyboard and mouse navigation requests Loaded += ref new RoutedEventHandler(this, &LayoutAwarePage::OnLoaded); // Undo the same changes when the page is no longer visible Unloaded += ref new RoutedEventHandler(this, &LayoutAwarePage::OnUnloaded); } static DependencyProperty^ _defaultViewModelProperty = DependencyProperty::Register("DefaultViewModel", TypeName(IObservableMap::typeid), TypeName(LayoutAwarePage::typeid), nullptr); /// /// Identifies the dependency property. /// DependencyProperty^ LayoutAwarePage::DefaultViewModelProperty::get() { return _defaultViewModelProperty; } /// /// Gets an implementation of designed to be /// used as a trivial view model. /// IObservableMap^ LayoutAwarePage::DefaultViewModel::get() { return safe_cast^>(GetValue(DefaultViewModelProperty)); } /// /// Sets an implementation of designed to be /// used as a trivial view model. /// void LayoutAwarePage::DefaultViewModel::set(IObservableMap^ value) { SetValue(DefaultViewModelProperty, value); } /// /// Invoked when the page is part of the visual tree /// /// Instance that triggered the event. /// Event data describing the conditions that led to the event. void LayoutAwarePage::OnLoaded(Object^ sender, Windows::UI::Xaml::RoutedEventArgs^ e) { this->StartLayoutUpdates(sender, e); // Keyboard and mouse navigation only apply when occupying the entire window if (this->ActualHeight == Window::Current->Bounds.Height && this->ActualWidth == Window::Current->Bounds.Width) { // Listen to the window directly so focus isn't required _acceleratorKeyEventToken = Window::Current->CoreWindow->Dispatcher->AcceleratorKeyActivated += ref new TypedEventHandler(this, &LayoutAwarePage::CoreDispatcher_AcceleratorKeyActivated); _pointerPressedEventToken = Window::Current->CoreWindow->PointerPressed += ref new TypedEventHandler(this, &LayoutAwarePage::CoreWindow_PointerPressed); _navigationShortcutsRegistered = true; } } /// /// Invoked when the page is removed from visual tree /// /// Instance that triggered the event. /// Event data describing the conditions that led to the event. void LayoutAwarePage::OnUnloaded(Object^ sender, Windows::UI::Xaml::RoutedEventArgs^ e) { if (_navigationShortcutsRegistered) { Window::Current->CoreWindow->Dispatcher->AcceleratorKeyActivated -= _acceleratorKeyEventToken; Window::Current->CoreWindow->PointerPressed -= _pointerPressedEventToken; _navigationShortcutsRegistered = false; } StopLayoutUpdates(sender, e); } #pragma region Navigation support /// /// Invoked as an event handler to navigate backward in the page's associated /// until it reaches the top of the navigation stack. /// /// Instance that triggered the event. /// Event data describing the conditions that led to the event. void LayoutAwarePage::GoHome(Object^ sender, RoutedEventArgs^ e) { (void) sender; // Unused parameter (void) e; // Unused parameter // Use the navigation frame to return to the topmost page if (Frame != nullptr) { while (Frame->CanGoBack) { Frame->GoBack(); } } } /// /// Invoked as an event handler to navigate backward in the navigation stack /// associated with this page's . /// /// Instance that triggered the event. /// Event data describing the conditions that led to the event. void LayoutAwarePage::GoBack(Object^ sender, RoutedEventArgs^ e) { (void) sender; // Unused parameter (void) e; // Unused parameter // Use the navigation frame to return to the previous page if (Frame != nullptr && Frame->CanGoBack) { Frame->GoBack(); } } /// /// Invoked as an event handler to navigate forward in the navigation stack /// associated with this page's . /// /// Instance that triggered the event. /// Event data describing the conditions that led to the event. void LayoutAwarePage::GoForward(Object^ sender, RoutedEventArgs^ e) { (void) sender; // Unused parameter (void) e; // Unused parameter // Use the navigation frame to advance to the next page if (Frame != nullptr && Frame->CanGoForward) { Frame->GoForward(); } } /// /// Invoked on every keystroke, including system keys such as Alt key combinations, when /// this page is active and occupies the entire window. Used to detect keyboard navigation /// between pages even when the page itself doesn't have focus. /// /// Instance that triggered the event. /// Event data describing the conditions that led to the event. void LayoutAwarePage::CoreDispatcher_AcceleratorKeyActivated(CoreDispatcher^ sender, AcceleratorKeyEventArgs^ args) { auto virtualKey = args->VirtualKey; // Only investigate further when Left, Right, or the dedicated Previous or Next keys // are pressed if ((args->EventType == CoreAcceleratorKeyEventType::SystemKeyDown || args->EventType == CoreAcceleratorKeyEventType::KeyDown) && (virtualKey == VirtualKey::Left || virtualKey == VirtualKey::Right || (int)virtualKey == 166 || (int)virtualKey == 167)) { auto coreWindow = Window::Current->CoreWindow; auto downState = Windows::UI::Core::CoreVirtualKeyStates::Down; bool menuKey = (coreWindow->GetKeyState(VirtualKey::Menu) & downState) == downState; bool controlKey = (coreWindow->GetKeyState(VirtualKey::Control) & downState) == downState; bool shiftKey = (coreWindow->GetKeyState(VirtualKey::Shift) & downState) == downState; bool noModifiers = !menuKey && !controlKey && !shiftKey; bool onlyAlt = menuKey && !controlKey && !shiftKey; if (((int)virtualKey == 166 && noModifiers) || (virtualKey == VirtualKey::Left && onlyAlt)) { // When the previous key or Alt+Left are pressed navigate back args->Handled = true; GoBack(this, ref new RoutedEventArgs()); } else if (((int)virtualKey == 167 && noModifiers) || (virtualKey == VirtualKey::Right && onlyAlt)) { // When the next key or Alt+Right are pressed navigate forward args->Handled = true; GoForward(this, ref new RoutedEventArgs()); } } } /// /// Invoked on every mouse click, touch screen tap, or equivalent interaction when this /// page is active and occupies the entire window. Used to detect browser-style next and /// previous mouse button clicks to navigate between pages. /// /// Instance that triggered the event. /// Event data describing the conditions that led to the event. void LayoutAwarePage::CoreWindow_PointerPressed(CoreWindow^ sender, PointerEventArgs^ args) { auto properties = args->CurrentPoint->Properties; // Ignore button chords with the left, right, and middle buttons if (properties->IsLeftButtonPressed || properties->IsRightButtonPressed || properties->IsMiddleButtonPressed) return; // If back or forward are pressed (but not both) navigate appropriately bool backPressed = properties->IsXButton1Pressed; bool forwardPressed = properties->IsXButton2Pressed; if (backPressed ^ forwardPressed) { args->Handled = true; if (backPressed) GoBack(this, ref new RoutedEventArgs()); if (forwardPressed) GoForward(this, ref new RoutedEventArgs()); } } #pragma endregion #pragma region Visual state switching /// /// Invoked as an event handler, typically on the event of a /// within the page, to indicate that the sender should start receiving /// visual state management changes that correspond to application view state changes. /// /// Instance of that supports visual state management /// corresponding to view states. /// Event data that describes how the request was made. /// The current view state will immediately be used to set the corresponding visual state /// when layout updates are requested. A corresponding event handler /// connected to is strongly encouraged. Instances of /// automatically invoke these handlers in their Loaded and Unloaded /// events. /// /// void LayoutAwarePage::StartLayoutUpdates(Object^ sender, RoutedEventArgs^ e) { (void) e; // Unused parameter auto control = safe_cast(sender); if (_layoutAwareControls == nullptr) { // Start listening to view state changes when there are controls interested in updates _layoutAwareControls = ref new Vector(); _windowSizeEventToken = Window::Current->SizeChanged += ref new WindowSizeChangedEventHandler(this, &LayoutAwarePage::WindowSizeChanged); // Page receives notifications for children. Protect the page until we stopped layout updates for all controls. _this = this; } _layoutAwareControls->Append(control); // Set the initial visual state of the control VisualStateManager::GoToState(control, DetermineVisualState(ApplicationView::Value), false); } void LayoutAwarePage::WindowSizeChanged(Object^ sender, Windows::UI::Core::WindowSizeChangedEventArgs^ e) { (void) sender; // Unused parameter (void) e; // Unused parameter InvalidateVisualState(); } /// /// Invoked as an event handler, typically on the event of a /// , to indicate that the sender should start receiving visual state /// management changes that correspond to application view state changes. /// /// Instance of that supports visual state management /// corresponding to view states. /// Event data that describes how the request was made. /// The current view state will immediately be used to set the corresponding visual state /// when layout updates are requested. /// void LayoutAwarePage::StopLayoutUpdates(Object^ sender, RoutedEventArgs^ e) { (void) e; // Unused parameter auto control = safe_cast(sender); unsigned int index; if (_layoutAwareControls != nullptr && _layoutAwareControls->IndexOf(control, &index)) { _layoutAwareControls->RemoveAt(index); if (_layoutAwareControls->Size == 0) { // Stop listening to view state changes when no controls are interested in updates Window::Current->SizeChanged -= _windowSizeEventToken; _layoutAwareControls = nullptr; // Last control has received the Unload notification. _this = nullptr; } } } /// /// Translates values into strings for visual state management /// within the page. The default implementation uses the names of enum values. Subclasses may /// override this method to control the mapping scheme used. /// /// View state for which a visual state is desired. /// Visual state name used to drive the /// String^ LayoutAwarePage::DetermineVisualState(ApplicationViewState viewState) { switch (viewState) { case ApplicationViewState::Filled: return "Filled"; case ApplicationViewState::Snapped: return "Snapped"; case ApplicationViewState::FullScreenPortrait: return "FullScreenPortrait"; case ApplicationViewState::FullScreenLandscape: default: return "FullScreenLandscape"; } } /// /// Updates all controls that are listening for visual state changes with the correct visual /// state. /// /// /// Typically used in conjunction with overriding to /// signal that a different value may be returned even though the view state has not changed. /// void LayoutAwarePage::InvalidateVisualState() { if (_layoutAwareControls != nullptr) { String^ visualState = DetermineVisualState(ApplicationView::Value); auto controlIterator = _layoutAwareControls->First(); while (controlIterator->HasCurrent) { auto control = controlIterator->Current; VisualStateManager::GoToState(control, visualState, false); controlIterator->MoveNext(); } } } #pragma endregion #pragma region Process lifetime management /// /// Invoked when this page is about to be displayed in a Frame. /// /// Event data that describes how this page was reached. The Parameter /// property provides the group to be displayed. void LayoutAwarePage::OnNavigatedTo(NavigationEventArgs^ e) { // Returning to a cached page through navigation shouldn't trigger state loading if (_pageKey != nullptr) return; auto frameState = SuspensionManager::SessionStateForFrame(Frame); _pageKey = "Page-" + Frame->BackStackDepth; if (e->NavigationMode == NavigationMode::New) { // Clear existing state for forward navigation when adding a new page to the // navigation stack auto nextPageKey = _pageKey; int nextPageIndex = Frame->BackStackDepth; while (frameState->HasKey(nextPageKey)) { frameState->Remove(nextPageKey); nextPageIndex++; nextPageKey = "Page-" + nextPageIndex; } // Pass the navigation parameter to the new page LoadState(e->Parameter, nullptr); } else { // Pass the navigation parameter and preserved page state to the page, using // the same strategy for loading suspended state and recreating pages discarded // from cache LoadState(e->Parameter, safe_cast^>(frameState->Lookup(_pageKey))); } } /// /// Invoked when this page will no longer be displayed in a Frame. /// /// Event data that describes how this page was reached. The Parameter /// property provides the group to be displayed. void LayoutAwarePage::OnNavigatedFrom(NavigationEventArgs^ e) { auto frameState = SuspensionManager::SessionStateForFrame(Frame); auto pageState = ref new Map(); SaveState(pageState); frameState->Insert(_pageKey, pageState); } /// /// Populates the page with content passed during navigation. Any saved state is also /// provided when recreating a page from a prior session. /// /// The parameter value passed to /// when this page was initially requested. /// /// A map of state preserved by this page during an earlier /// session. This will be null the first time a page is visited. void LayoutAwarePage::LoadState(Object^ navigationParameter, IMap^ pageState) { } /// /// Preserves state associated with this page in case the application is suspended or the /// page is discarded from the navigation cache. Values must conform to the serialization /// requirements of . /// /// An empty map to be populated with serializable state. void LayoutAwarePage::SaveState(IMap^ pageState) { } #pragma endregion