Remove unused PLM code (#7)

Remove LayoutAwarePage, SuspensionManager, and other suspend-resume handling code. SuspensionManager::SaveAsync and related methods weren't actually called anywhere. I didn't attempt to remove the serialize/deserialize code at the ViewModel layer, although much of that is likely not needed either.

We may decide we want to persist more state through a suspend-terminate-resume cycle (as the app might have done a long time ago). But if we decide we want that, we should not use a persistence mechanism that's closely coupled to frame navigation.
This commit is contained in:
Matt Cooley
2019-02-01 15:15:48 -08:00
committed by GitHub
parent 8df88c7106
commit 4cadfb204d
11 changed files with 41 additions and 1069 deletions

View File

@@ -1,322 +0,0 @@
// Copyright (c) Microsoft Corporation. All rights reserved.
// Licensed under the MIT License.
#include "pch.h"
#include "LayoutAwarePage.h"
#include "SuspensionManager.h"
#include "CalcViewModel\Common\LocalizationService.h"
#include "App.xaml.h"
using namespace CalculatorApp::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;
/// <summary>
/// Initializes a new instance of the <see cref="LayoutAwarePage"/> class.
/// </summary>
LayoutAwarePage::LayoutAwarePage()
{
if (Windows::ApplicationModel::DesignMode::DesignModeEnabled)
{
return;
}
// Create an empty default view model
DefaultViewModel = ref new Map<String^, Object^>(std::less<String^>());
// 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);
Language = LocalizationService::GetInstance()->GetLanguage();
}
static DependencyProperty^ _defaultViewModelProperty =
DependencyProperty::Register("DefaultViewModel",
TypeName(IObservableMap<String^, Object^>::typeid), TypeName(LayoutAwarePage::typeid), nullptr);
/// <summary>
/// Identifies the <see cref="DefaultViewModel"/> dependency property.
/// </summary>
DependencyProperty^ LayoutAwarePage::DefaultViewModelProperty::get()
{
return _defaultViewModelProperty;
}
/// <summary>
/// Gets an implementation of <see cref="IObservableMap&lt;String, Object&gt;"/> designed to be
/// used as a trivial view model.
/// </summary>
IObservableMap<String^, Object^>^ LayoutAwarePage::DefaultViewModel::get()
{
return safe_cast<IObservableMap<String^, Object^>^>(GetValue(DefaultViewModelProperty));
}
/// <summary>
/// Sets an implementation of <see cref="IObservableMap&lt;String, Object&gt;"/> designed to be
/// used as a trivial view model.
/// </summary>
void LayoutAwarePage::DefaultViewModel::set(IObservableMap<String^, Object^>^ value)
{
SetValue(DefaultViewModelProperty, value);
}
/// <summary>
/// Invoked when the page is part of the visual tree
/// </summary>
/// <param name="sender">Instance that triggered the event.</param>
/// <param name="e">Event data describing the conditions that led to the event.</param>
void LayoutAwarePage::OnLoaded(Object^ sender, Windows::UI::Xaml::RoutedEventArgs^ 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<CoreDispatcher^, AcceleratorKeyEventArgs^>(this,
&LayoutAwarePage::CoreDispatcher_AcceleratorKeyActivated);
_pointerPressedEventToken = Window::Current->CoreWindow->PointerPressed +=
ref new TypedEventHandler<CoreWindow^, PointerEventArgs^>(this,
&LayoutAwarePage::CoreWindow_PointerPressed);
_navigationShortcutsRegistered = true;
}
}
/// <summary>
/// Invoked when the page is removed from visual tree
/// </summary>
/// <param name="sender">Instance that triggered the event.</param>
/// <param name="e">Event data describing the conditions that led to the event.</param>
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;
}
}
#pragma region Navigation support
/// <summary>
/// Invoked as an event handler to navigate backward in the page's associated <see cref="Frame"/>
/// until it reaches the top of the navigation stack.
/// </summary>
/// <param name="sender">Instance that triggered the event.</param>
/// <param name="e">Event data describing the conditions that led to the event.</param>
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();
}
}
}
/// <summary>
/// Invoked as an event handler to navigate backward in the navigation stack
/// associated with this page's <see cref="Frame"/>.
/// </summary>
/// <param name="sender">Instance that triggered the event.</param>
/// <param name="e">Event data describing the conditions that led to the event.</param>
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();
}
}
/// <summary>
/// Invoked as an event handler to navigate forward in the navigation stack
/// associated with this page's <see cref="Frame"/>.
/// </summary>
/// <param name="sender">Instance that triggered the event.</param>
/// <param name="e">Event data describing the conditions that led to the event.</param>
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();
}
}
/// <summary>
/// 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.
/// </summary>
/// <param name="sender">Instance that triggered the event.</param>
/// <param name="args">Event data describing the conditions that led to the event.</param>
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());
}
}
}
/// <summary>
/// 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.
/// </summary>
/// <param name="sender">Instance that triggered the event.</param>
/// <param name="args">Event data describing the conditions that led to the event.</param>
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 foward 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 Process lifetime management
/// <summary>
/// Invoked when this page is about to be displayed in a Frame.
/// </summary>
/// <param name="e">Event data that describes how this page was reached. The Parameter
/// property provides the group to be displayed.</param>
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<IMap<String^, Object^>^>(frameState->Lookup(_pageKey)));
}
}
/// <summary>
/// Invoked when this page will no longer be displayed in a Frame.
/// </summary>
/// <param name="e">Event data that describes how this page was reached. The Parameter
/// property provides the group to be displayed.</param>
void LayoutAwarePage::OnNavigatedFrom(NavigationEventArgs^ e)
{
auto frameState = SuspensionManager::SessionStateForFrame(Frame);
auto pageState = ref new Map<String^, Object^>();
SaveState(pageState);
frameState->Insert(_pageKey, pageState);
}
/// <summary>
/// Populates the page with content passed during navigation. Any saved state is also
/// provided when recreating a page from a prior session.
/// </summary>
/// <param name="navigationParameter">The parameter value passed to
/// <see cref="Frame.Navigate(Type, Object)"/> when this page was initially requested.
/// </param>
/// <param name="pageState">A map of state preserved by this page during an earlier
/// session. This will be null the first time a page is visited.</param>
void LayoutAwarePage::LoadState(Object^ navigationParameter, IMap<String^, Object^>^ pageState)
{
}
/// <summary>
/// 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 <see cref="SuspensionManager.SessionState"/>.
/// </summary>
/// <param name="pageState">An empty map to be populated with serializable state.</param>
void LayoutAwarePage::SaveState(IMap<String^, Object^>^ pageState)
{
}
#pragma endregion

View File

@@ -1,75 +0,0 @@
// Copyright (c) Microsoft Corporation. All rights reserved.
// Licensed under the MIT License.
#pragma once
#include <collection.h>
namespace CalculatorApp
{
namespace Common
{
/// <summary>
/// Typical implementation of Page that provides several important conveniences:
/// <list type="bullet">
/// <item>
/// <description>Application view state to visual state mapping</description>
/// </item>
/// <item>
/// <description>GoBack, GoForward, and GoHome event handlers</description>
/// </item>
/// <item>
/// <description>Mouse and keyboard shortcuts for navigation</description>
/// </item>
/// <item>
/// <description>State management for navigation and process lifetime management</description>
/// </item>
/// <item>
/// <description>A default view model</description>
/// </item>
/// </list>
/// </summary>
[Windows::Foundation::Metadata::WebHostHidden]
public ref class LayoutAwarePage : Windows::UI::Xaml::Controls::Page
{
internal:
LayoutAwarePage();
public:
static property Windows::UI::Xaml::DependencyProperty^ DefaultViewModelProperty
{
Windows::UI::Xaml::DependencyProperty^ get();
};
property Windows::Foundation::Collections::IObservableMap<Platform::String^, Platform::Object^>^ DefaultViewModel
{
Windows::Foundation::Collections::IObservableMap<Platform::String^, Platform::Object^>^ get();
void set(Windows::Foundation::Collections::IObservableMap<Platform::String^, Platform::Object^>^ value);
}
protected:
virtual void GoHome(Platform::Object^ sender, Windows::UI::Xaml::RoutedEventArgs^ e);
virtual void GoBack(Platform::Object^ sender, Windows::UI::Xaml::RoutedEventArgs^ e);
virtual void GoForward(Platform::Object^ sender, Windows::UI::Xaml::RoutedEventArgs^ e);
virtual void OnNavigatedTo(Windows::UI::Xaml::Navigation::NavigationEventArgs^ e) override;
virtual void OnNavigatedFrom(Windows::UI::Xaml::Navigation::NavigationEventArgs^ e) override;
virtual void LoadState(Platform::Object^ navigationParameter,
Windows::Foundation::Collections::IMap<Platform::String^, Platform::Object^>^ pageState);
virtual void SaveState(Windows::Foundation::Collections::IMap<Platform::String^, Platform::Object^>^ pageState);
private:
Platform::String^ _pageKey;
bool _navigationShortcutsRegistered;
Platform::Collections::Map<Platform::String^, Platform::Object^>^ _defaultViewModel;
Windows::Foundation::EventRegistrationToken _windowSizeEventToken,
_acceleratorKeyEventToken, _pointerPressedEventToken;
Platform::Collections::Vector<Windows::UI::Xaml::Controls::Control^>^ _layoutAwareControls;
void OnLoaded(Object^ sender, Windows::UI::Xaml::RoutedEventArgs^ e);
void OnUnloaded(Object^ sender, Windows::UI::Xaml::RoutedEventArgs^ e);
void CoreDispatcher_AcceleratorKeyActivated(Windows::UI::Core::CoreDispatcher^ sender,
Windows::UI::Core::AcceleratorKeyEventArgs^ args);
void CoreWindow_PointerPressed(Windows::UI::Core::CoreWindow^ sender,
Windows::UI::Core::PointerEventArgs^ args);
LayoutAwarePage^ _this; // Strong reference to self, cleaned up in OnUnload
};
}
}

View File

@@ -1,494 +0,0 @@
// Copyright (c) Microsoft Corporation. All rights reserved.
// Licensed under the MIT License.
//
// SuspensionManager.cpp
// Implementation of the SuspensionManager class
//
#include "pch.h"
#include "SuspensionManager.h"
using namespace CalculatorApp::Common;
using namespace Concurrency;
using namespace Platform;
using namespace Platform::Collections;
using namespace Windows::Foundation;
using namespace Windows::Foundation::Collections;
using namespace Windows::Storage;
using namespace Windows::Storage::FileProperties;
using namespace Windows::Storage::Streams;
using namespace Windows::UI::Xaml;
using namespace Windows::UI::Xaml::Controls;
using namespace Windows::UI::Xaml::Interop;
namespace
{
Map<String^, Object^>^ _sessionState = ref new Map<String^, Object^>();
String^ sessionStateFilename = "_sessionState.dat";
// Forward declarations for object object read / write support
void WriteObject(Windows::Storage::Streams::DataWriter^ writer, Platform::Object^ object);
Platform::Object^ ReadObject(Windows::Storage::Streams::DataReader^ reader);
}
/// <summary>
/// Provides access to global session state for the current session. This state is serialized by
/// <see cref="SaveAsync"/> and restored by <see cref="RestoreAsync"/> which require values to be
/// one of the following: boxed values including integers, floating-point singles and doubles,
/// wide characters, boolean, Strings and Guids, or Map<String^, Object^> where map values are
/// subject to the same constraints. Session state should be as compact as possible.
/// </summary>
IMap<String^, Object^>^ SuspensionManager::SessionState::get(void)
{
return _sessionState;
}
/// <summary>
/// Wrap a WeakReference as a reference object for use in a collection.
/// </summary>
private ref class WeakFrame sealed
{
private:
WeakReference _frameReference;
internal:
WeakFrame(Frame^ frame) { _frameReference = frame; }
property Frame^ ResolvedFrame
{
Frame^ get(void) { return _frameReference.Resolve<Frame>(); }
};
};
namespace
{
std::vector<WeakFrame^> _registeredFrames;
DependencyProperty^ FrameSessionStateKeyProperty =
DependencyProperty::RegisterAttached("_FrameSessionStateKeyProperty",
TypeName(String::typeid), TypeName(SuspensionManager::typeid), nullptr);
DependencyProperty^ FrameSessionStateProperty =
DependencyProperty::RegisterAttached("_FrameSessionStateProperty",
TypeName(IMap<String^, Object^>::typeid), TypeName(SuspensionManager::typeid), nullptr);
}
/// <summary>
/// Registers a <see cref="Frame"/> instance to allow its navigation history to be saved to
/// and restored from <see cref="SessionState"/>. Frames should be registered once
/// immediately after creation if they will participate in session state management. Upon
/// registration if state has already been restored for the specified key
/// the navigation history will immediately be restored. Subsequent invocations of
/// <see cref="RestoreAsync(String)"/> will also restore navigation history.
/// </summary>
/// <param name="frame">An instance whose navigation history should be managed by
/// <see cref="SuspensionManager"/></param>
/// <param name="sessionStateKey">A unique key into <see cref="SessionState"/> used to
/// store navigation-related information.</param>
void SuspensionManager::RegisterFrame(Frame^ frame, String^ sessionStateKey)
{
if (frame->GetValue(FrameSessionStateKeyProperty) != nullptr)
{
throw ref new FailureException("Frames can only be registered to one session state key");
}
if (frame->GetValue(FrameSessionStateProperty) != nullptr)
{
throw ref new FailureException("Frames must be either be registered before accessing frame session state, or not registered at all");
}
// Use a dependency property to associate the session key with a frame, and keep a list of frames whose
// navigation state should be managed
frame->SetValue(FrameSessionStateKeyProperty, sessionStateKey);
_registeredFrames.insert(_registeredFrames.begin(), ref new WeakFrame(frame));
// Check to see if navigation state can be restored
RestoreFrameNavigationState(frame);
}
/// <summary>
/// Disassociates a <see cref="Frame"/> previously registered by <see cref="RegisterFrame"/>
/// from <see cref="SessionState"/>. Any navigation state previously captured will be
/// removed.
/// </summary>
/// <param name="frame">An instance whose navigation history should no longer be
/// managed.</param>
void SuspensionManager::UnregisterFrame(Frame^ frame)
{
// Remove session state and remove the frame from the list of frames whose navigation
// state will be saved (along with any weak references that are no longer reachable)
auto key = safe_cast<String^>(frame->GetValue(FrameSessionStateKeyProperty));
if (SessionState->HasKey(key)) SessionState->Remove(key);
_registeredFrames.erase(
std::remove_if(_registeredFrames.begin(), _registeredFrames.end(), [=](WeakFrame^& e)
{
auto testFrame = e->ResolvedFrame;
return testFrame == nullptr || testFrame == frame;
}),
_registeredFrames.end()
);
}
/// <summary>
/// Provides storage for session state associated with the specified <see cref="Frame"/>.
/// Frames that have been previously registered with <see cref="RegisterFrame"/> have
/// their session state saved and restored automatically as a part of the global
/// <see cref="SessionState"/>. Frames that are not registered have transient state
/// that can still be useful when restoring pages that have been discarded from the
/// navigation cache.
/// </summary>
/// <remarks>Apps may choose to rely on <see cref="LayoutAwarePage"/> to manage
/// page-specific state instead of working with frame session state directly.</remarks>
/// <param name="frame">The instance for which session state is desired.</param>
/// <returns>A collection of state subject to the same serialization mechanism as
/// <see cref="SessionState"/>.</returns>
IMap<String^, Object^>^ SuspensionManager::SessionStateForFrame(Frame^ frame)
{
auto frameState = safe_cast<IMap<String^, Object^>^>(frame->GetValue(FrameSessionStateProperty));
if (frameState == nullptr)
{
auto frameSessionKey = safe_cast<String^>(frame->GetValue(FrameSessionStateKeyProperty));
if (frameSessionKey != nullptr)
{
// Registered frames reflect the corresponding session state
if (!_sessionState->HasKey(frameSessionKey))
{
_sessionState->Insert(frameSessionKey, ref new Map<String^, Object^>());
}
frameState = safe_cast<IMap<String^, Object^>^>(_sessionState->Lookup(frameSessionKey));
}
else
{
// Frames that aren't registered have transient state
frameState = ref new Map<String^, Object^>();
}
frame->SetValue(FrameSessionStateProperty, frameState);
}
return frameState;
}
void SuspensionManager::RestoreFrameNavigationState(Frame^ frame)
{
auto frameState = SessionStateForFrame(frame);
if (frameState->HasKey("Navigation"))
{
frame->SetNavigationState(safe_cast<String^>(frameState->Lookup("Navigation")));
}
}
void SuspensionManager::SaveFrameNavigationState(Frame^ frame)
{
auto frameState = SessionStateForFrame(frame);
frameState->Insert("Navigation", frame->GetNavigationState());
}
/// <summary>
/// Save the current <see cref="SessionState"/>. Any <see cref="Frame"/> instances
/// registered with <see cref="RegisterFrame"/> will also preserve their current
/// navigation stack, which in turn gives their active <see cref="Page"/> an opportunity
/// to save its state.
/// </summary>
/// <returns>An asynchronous task that reflects when session state has been saved.</returns>
task<void> SuspensionManager::SaveAsync(void)
{
// Save the navigation state for all registered frames
for (auto&& weakFrame : _registeredFrames)
{
auto frame = weakFrame->ResolvedFrame;
if (frame != nullptr) SaveFrameNavigationState(frame);
}
// Serialize the session state synchronously to avoid asynchronous access to shared
// state
auto sessionData = ref new InMemoryRandomAccessStream();
auto sessionDataWriter = ref new DataWriter(sessionData->GetOutputStreamAt(0));
WriteObject(sessionDataWriter, _sessionState);
// Once session state has been captured synchronously, begin the asynchronous process
// of writing the result to disk
return task<unsigned int>(sessionDataWriter->StoreAsync()).then([=](unsigned int)
{
return ApplicationData::Current->LocalFolder->CreateFileAsync(sessionStateFilename,
CreationCollisionOption::ReplaceExisting);
}).then([=](StorageFile^ createdFile)
{
return createdFile->OpenAsync(FileAccessMode::ReadWrite);
}).then([=](IRandomAccessStream^ newStream)
{
return RandomAccessStream::CopyAsync(
sessionData->GetInputStreamAt(0), newStream->GetOutputStreamAt(0));
}).then([=](UINT64 copiedBytes)
{
(void)copiedBytes; // Unused parameter
return;
});
}
/// <summary>
/// Restores previously saved <see cref="SessionState"/>. Any <see cref="Frame"/> instances
/// registered with <see cref="RegisterFrame"/> will also restore their prior navigation
/// state, which in turn gives their active <see cref="Page"/> an opportunity restore its
/// state.
/// </summary>
/// <param name="version">A version identifer compared to the session state to prevent
/// incompatible versions of session state from reaching app code. Saved state with a
/// different version will be ignored, resulting in an empty <see cref="SessionState"/>
/// dictionary.</param>
/// <returns>An asynchronous task that reflects when session state has been read. The
/// content of <see cref="SessionState"/> should not be relied upon until this task
/// completes.</returns>
task<void> SuspensionManager::RestoreAsync(void)
{
_sessionState->Clear();
task<StorageFile^> getFileTask(ApplicationData::Current->LocalFolder->GetFileAsync(sessionStateFilename));
return getFileTask.then([=](StorageFile^ stateFile)
{
task<BasicProperties^> getBasicPropertiesTask(stateFile->GetBasicPropertiesAsync());
return getBasicPropertiesTask.then([=](BasicProperties^ stateFileProperties)
{
auto size = unsigned int(stateFileProperties->Size);
if (size != stateFileProperties->Size) throw ref new FailureException("Session state larger than 4GB");
task<IRandomAccessStreamWithContentType^> openReadTask(stateFile->OpenReadAsync());
return openReadTask.then([=](IRandomAccessStreamWithContentType^ stateFileStream)
{
auto stateReader = ref new DataReader(stateFileStream);
return task<unsigned int>(stateReader->LoadAsync(size)).then([=](unsigned int bytesRead)
{
(void)bytesRead; // Unused parameter
// Deserialize the Session State
Object^ content = ReadObject(stateReader);
_sessionState = (Map<String^, Object^>^)content;
// Restore any registered frames to their saved state
for (auto&& weakFrame : _registeredFrames)
{
auto frame = weakFrame->ResolvedFrame;
if (frame != nullptr)
{
frame->ClearValue(FrameSessionStateProperty);
RestoreFrameNavigationState(frame);
}
}
}, task_continuation_context::use_current());
});
});
});
}
#pragma region Object serialization for a known set of types
namespace
{
// Codes used for identifying serialized types
enum StreamTypes {
NullPtrType = 0,
// Supported IPropertyValue types
UInt8Type, UInt16Type, UInt32Type, UInt64Type, Int16Type, Int32Type, Int64Type,
SingleType, DoubleType, BooleanType, Char16Type, GuidType, StringType,
// Array types
UInt8ArrayType,
// Additional supported types
StringToObjectMapType,
// Marker values used to ensure stream integrity
MapEndMarker
};
void WriteString(DataWriter^ writer, String^ string)
{
writer->WriteByte(StringType);
writer->WriteUInt32(writer->MeasureString(string));
writer->WriteString(string);
}
void WriteByteArray(DataWriter^ writer, Platform::Array<unsigned char>^ data)
{
writer->WriteByte(UInt8ArrayType);
writer->WriteUInt32(data->Length);
writer->WriteBytes(data);
}
void WriteProperty(DataWriter^ writer, IPropertyValue^ propertyValue)
{
switch (propertyValue->Type)
{
case PropertyType::UInt8:
writer->WriteByte(UInt8Type);
writer->WriteByte(propertyValue->GetUInt8());
return;
case PropertyType::UInt8Array:
{
Array<unsigned char>^ data;
propertyValue->GetUInt8Array(&data);
WriteByteArray(writer, data);
}
return;
case PropertyType::UInt16:
writer->WriteByte(UInt16Type);
writer->WriteUInt16(propertyValue->GetUInt16());
return;
case PropertyType::UInt32:
writer->WriteByte(UInt32Type);
writer->WriteUInt32(propertyValue->GetUInt32());
return;
case PropertyType::UInt64:
writer->WriteByte(UInt64Type);
writer->WriteUInt64(propertyValue->GetUInt64());
return;
case PropertyType::Int16:
writer->WriteByte(Int16Type);
writer->WriteUInt16(propertyValue->GetInt16());
return;
case PropertyType::Int32:
writer->WriteByte(Int32Type);
writer->WriteUInt32(propertyValue->GetInt32());
return;
case PropertyType::Int64:
writer->WriteByte(Int64Type);
writer->WriteUInt64(propertyValue->GetInt64());
return;
case PropertyType::Single:
writer->WriteByte(SingleType);
writer->WriteSingle(propertyValue->GetSingle());
return;
case PropertyType::Double:
writer->WriteByte(DoubleType);
writer->WriteDouble(propertyValue->GetDouble());
return;
case PropertyType::Boolean:
writer->WriteByte(BooleanType);
writer->WriteBoolean(propertyValue->GetBoolean());
return;
case PropertyType::Char16:
writer->WriteByte(Char16Type);
writer->WriteUInt16(propertyValue->GetChar16());
return;
case PropertyType::Guid:
writer->WriteByte(GuidType);
writer->WriteGuid(propertyValue->GetGuid());
return;
case PropertyType::String:
WriteString(writer, propertyValue->GetString());
return;
default:
throw ref new InvalidArgumentException("Unsupported property type");
}
}
void WriteStringToObjectMap(DataWriter^ writer, IMap<String^, Object^>^ map)
{
writer->WriteByte(StringToObjectMapType);
writer->WriteUInt32(map->Size);
for (auto&& pair : map)
{
WriteObject(writer, pair->Key);
WriteObject(writer, pair->Value);
}
writer->WriteByte(MapEndMarker);
}
void WriteObject(DataWriter^ writer, Object^ object)
{
if (object == nullptr)
{
writer->WriteByte(NullPtrType);
return;
}
auto propertyObject = dynamic_cast<IPropertyValue^>(object);
if (propertyObject != nullptr)
{
WriteProperty(writer, propertyObject);
return;
}
auto mapObject = dynamic_cast<IMap<String^, Object^>^>(object);
if (mapObject != nullptr)
{
WriteStringToObjectMap(writer, mapObject);
return;
}
throw ref new InvalidArgumentException("Unsupported data type");
}
String^ ReadString(DataReader^ reader)
{
int length = reader->ReadUInt32();
String^ string = reader->ReadString(length);
return string;
}
Object^ ReadByteArray(DataReader^ reader)
{
unsigned int length = reader->ReadUInt32();
Array<unsigned char>^ data = ref new Array<unsigned char>(length);
reader->ReadBytes(data);
return data;
}
IMap<String^, Object^>^ ReadStringToObjectMap(DataReader^ reader)
{
auto map = ref new Map<String^, Object^>();
auto size = reader->ReadUInt32();
for (unsigned int index = 0; index < size; index++)
{
auto key = safe_cast<String^>(ReadObject(reader));
auto value = ReadObject(reader);
map->Insert(key, value);
}
if (reader->ReadByte() != MapEndMarker)
{
throw ref new InvalidArgumentException("Invalid stream");
}
return map;
}
Object^ ReadObject(DataReader^ reader)
{
auto type = reader->ReadByte();
switch (type)
{
case NullPtrType:
return nullptr;
case UInt8Type:
return reader->ReadByte();
case UInt8ArrayType:
return ReadByteArray(reader);
case UInt16Type:
return reader->ReadUInt16();
case UInt32Type:
return reader->ReadUInt32();
case UInt64Type:
return reader->ReadUInt64();
case Int16Type:
return reader->ReadInt16();
case Int32Type:
return reader->ReadInt32();
case Int64Type:
return reader->ReadInt64();
case SingleType:
return reader->ReadSingle();
case DoubleType:
return reader->ReadDouble();
case BooleanType:
return reader->ReadBoolean();
case Char16Type:
return static_cast<wchar_t>(reader->ReadUInt16());
case GuidType:
return reader->ReadGuid();
case StringType:
return ReadString(reader);
case StringToObjectMapType:
return ReadStringToObjectMap(reader);
default:
throw ref new InvalidArgumentException("Unsupported property type");
}
}
}
#pragma endregion

View File

@@ -1,43 +0,0 @@
// Copyright (c) Microsoft Corporation. All rights reserved.
// Licensed under the MIT License.
//
// SuspensionManager.h
// Declaration of the SuspensionManager class
//
#pragma once
#include <ppltasks.h>
namespace CalculatorApp
{
namespace Common
{
/// <summary>
/// SuspensionManager captures global session state to simplify process lifetime management
/// for an application. Note that session state will be automatically cleared under a variety
/// of conditions and should only be used to store information that would be convenient to
/// carry across sessions, but that should be disacarded when an application crashes or is
/// upgraded.
/// </summary>
ref class SuspensionManager sealed
{
internal:
static void RegisterFrame(Windows::UI::Xaml::Controls::Frame^ frame, Platform::String^ sessionStateKey);
static void UnregisterFrame(Windows::UI::Xaml::Controls::Frame^ frame);
static Concurrency::task<void> SaveAsync(void);
static Concurrency::task<void> RestoreAsync(void);
static property Windows::Foundation::Collections::IMap<Platform::String^, Platform::Object^>^ SessionState
{
Windows::Foundation::Collections::IMap<Platform::String^, Platform::Object^>^ get(void);
};
static Windows::Foundation::Collections::IMap<Platform::String^, Platform::Object^>^ SessionStateForFrame(
Windows::UI::Xaml::Controls::Frame^ frame);
private:
static void RestoreFrameNavigationState(Windows::UI::Xaml::Controls::Frame^ frame);
static void SaveFrameNavigationState(Windows::UI::Xaml::Controls::Frame^ frame);
};
}
}