//*********************************************************
//
// 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