Hello GitHub
This commit is contained in:
287
src/Calculator/Common/AlwaysSelectedCollectionView.h
Normal file
287
src/Calculator/Common/AlwaysSelectedCollectionView.h
Normal file
@@ -0,0 +1,287 @@
|
||||
// Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
// Licensed under the MIT License.
|
||||
|
||||
#pragma once
|
||||
|
||||
namespace CalculatorApp { namespace Common
|
||||
{
|
||||
ref class AlwaysSelectedCollectionView sealed:
|
||||
public Windows::UI::Xaml::DependencyObject,
|
||||
public Windows::UI::Xaml::Data::ICollectionView
|
||||
{
|
||||
internal:
|
||||
AlwaysSelectedCollectionView(Windows::UI::Xaml::Interop::IBindableVector^ source):
|
||||
m_currentPosition(-1)
|
||||
{
|
||||
m_source = source;
|
||||
|
||||
Windows::UI::Xaml::Interop::IBindableObservableVector^ observable = dynamic_cast<Windows::UI::Xaml::Interop::IBindableObservableVector^>(source);
|
||||
if (observable)
|
||||
{
|
||||
observable->VectorChanged +=
|
||||
ref new Windows::UI::Xaml::Interop::BindableVectorChangedEventHandler(this, &AlwaysSelectedCollectionView::OnSourceBindableVectorChanged);
|
||||
}
|
||||
}
|
||||
|
||||
private:
|
||||
// ICollectionView
|
||||
// Not implemented methods
|
||||
virtual Windows::Foundation::IAsyncOperation<Windows::UI::Xaml::Data::LoadMoreItemsResult>^ LoadMoreItemsAsync(unsigned int) = Windows::UI::Xaml::Data::ICollectionView::LoadMoreItemsAsync
|
||||
{
|
||||
throw ref new Platform::NotImplementedException();
|
||||
}
|
||||
virtual bool MoveCurrentToFirst() = Windows::UI::Xaml::Data::ICollectionView::MoveCurrentToFirst
|
||||
{
|
||||
throw ref new Platform::NotImplementedException();
|
||||
}
|
||||
virtual bool MoveCurrentToLast() = Windows::UI::Xaml::Data::ICollectionView::MoveCurrentToLast
|
||||
{
|
||||
throw ref new Platform::NotImplementedException();
|
||||
}
|
||||
virtual bool MoveCurrentToNext() = Windows::UI::Xaml::Data::ICollectionView::MoveCurrentToNext
|
||||
{
|
||||
throw ref new Platform::NotImplementedException();
|
||||
}
|
||||
virtual bool MoveCurrentToPrevious() = Windows::UI::Xaml::Data::ICollectionView::MoveCurrentToPrevious
|
||||
{
|
||||
throw ref new Platform::NotImplementedException();
|
||||
}
|
||||
property Windows::Foundation::Collections::IObservableVector<Platform::Object^>^ CollectionGroups
|
||||
{
|
||||
virtual Windows::Foundation::Collections::IObservableVector<Platform::Object^>^ get() = Windows::UI::Xaml::Data::ICollectionView::CollectionGroups::get
|
||||
{
|
||||
return ref new Platform::Collections::Vector<Platform::Object^>();
|
||||
}
|
||||
}
|
||||
property bool HasMoreItems
|
||||
{
|
||||
virtual bool get() = Windows::UI::Xaml::Data::ICollectionView::HasMoreItems::get
|
||||
{
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
// Implementented methods
|
||||
virtual bool MoveCurrentTo(Platform::Object^ item) = Windows::UI::Xaml::Data::ICollectionView::MoveCurrentTo
|
||||
{
|
||||
if (item)
|
||||
{
|
||||
unsigned int newCurrentPosition = 0;
|
||||
bool result = m_source->IndexOf(item, &newCurrentPosition);
|
||||
if (result)
|
||||
{
|
||||
m_currentPosition = newCurrentPosition;
|
||||
m_currentChanged(this, nullptr);
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
// The item is not in the collection
|
||||
// We're going to schedule a call back later so we
|
||||
// restore the selection to the way we wanted it to begin with
|
||||
if (m_currentPosition >= 0 && m_currentPosition < static_cast<int>(m_source->Size))
|
||||
{
|
||||
this->Dispatcher->RunAsync(Windows::UI::Core::CoreDispatcherPriority::Normal,
|
||||
ref new Windows::UI::Core::DispatchedHandler(
|
||||
[this]()
|
||||
{
|
||||
m_currentChanged(this, nullptr);
|
||||
}));
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
virtual bool MoveCurrentToPosition(int index) = Windows::UI::Xaml::Data::ICollectionView::MoveCurrentToPosition
|
||||
{
|
||||
if (index < 0 || index >= static_cast<int>(m_source->Size))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
m_currentPosition = index;
|
||||
m_currentChanged(this, nullptr);
|
||||
return true;
|
||||
}
|
||||
|
||||
property Platform::Object^ CurrentItem
|
||||
{
|
||||
virtual Platform::Object^ get() = Windows::UI::Xaml::Data::ICollectionView::CurrentItem::get
|
||||
{
|
||||
if (m_currentPosition >= 0 && m_currentPosition < static_cast<int>(m_source->Size))
|
||||
{
|
||||
return m_source->GetAt(m_currentPosition);
|
||||
}
|
||||
return nullptr;
|
||||
}
|
||||
}
|
||||
|
||||
property int CurrentPosition
|
||||
{
|
||||
virtual int get() = Windows::UI::Xaml::Data::ICollectionView::CurrentPosition::get
|
||||
{
|
||||
return m_currentPosition;
|
||||
}
|
||||
}
|
||||
|
||||
property bool IsCurrentAfterLast
|
||||
{
|
||||
virtual bool get() = Windows::UI::Xaml::Data::ICollectionView::IsCurrentAfterLast::get
|
||||
{
|
||||
return m_currentPosition >= static_cast<int>(m_source->Size);
|
||||
}
|
||||
}
|
||||
|
||||
property bool IsCurrentBeforeFirst
|
||||
{
|
||||
virtual bool get() = Windows::UI::Xaml::Data::ICollectionView::IsCurrentBeforeFirst::get
|
||||
{
|
||||
return m_currentPosition < 0;
|
||||
}
|
||||
}
|
||||
|
||||
event Windows::Foundation::EventHandler<Platform::Object^>^ CurrentChanged
|
||||
{
|
||||
virtual Windows::Foundation::EventRegistrationToken add(Windows::Foundation::EventHandler<Platform::Object^>^ handler) = Windows::UI::Xaml::Data::ICollectionView::CurrentChanged::add
|
||||
{
|
||||
return m_currentChanged += handler;
|
||||
}
|
||||
virtual void remove(Windows::Foundation::EventRegistrationToken token) = Windows::UI::Xaml::Data::ICollectionView::CurrentChanged::remove
|
||||
{
|
||||
m_currentChanged -= token;
|
||||
}
|
||||
}
|
||||
event Windows::UI::Xaml::Data::CurrentChangingEventHandler^ CurrentChanging
|
||||
{
|
||||
virtual Windows::Foundation::EventRegistrationToken add(Windows::UI::Xaml::Data::CurrentChangingEventHandler^ handler) = Windows::UI::Xaml::Data::ICollectionView::CurrentChanging::add
|
||||
{
|
||||
return m_currentChanging += handler;
|
||||
}
|
||||
virtual void remove(Windows::Foundation::EventRegistrationToken token) = Windows::UI::Xaml::Data::ICollectionView::CurrentChanging::remove
|
||||
{
|
||||
m_currentChanging -= token;
|
||||
}
|
||||
}
|
||||
|
||||
// IVector<Object^>
|
||||
// Not implemented methods
|
||||
virtual void Append(Platform::Object^ /*item*/) = Windows::Foundation::Collections::IVector<Platform::Object^>::Append
|
||||
{
|
||||
throw ref new Platform::NotImplementedException();
|
||||
}
|
||||
virtual void Clear() = Windows::Foundation::Collections::IVector<Platform::Object^>::Clear
|
||||
{
|
||||
throw ref new Platform::NotImplementedException();
|
||||
}
|
||||
virtual unsigned int GetMany(unsigned int /*startIndex*/, Platform::WriteOnlyArray<Platform::Object^>^ /*items*/) = Windows::Foundation::Collections::IVector<Platform::Object^>::GetMany
|
||||
{
|
||||
throw ref new Platform::NotImplementedException();
|
||||
}
|
||||
virtual Windows::Foundation::Collections::IVectorView<Platform::Object^>^ GetView() = Windows::Foundation::Collections::IVector<Platform::Object^>::GetView
|
||||
{
|
||||
throw ref new Platform::NotImplementedException();
|
||||
}
|
||||
virtual void InsertAt(unsigned int /*index*/, Platform::Object^ /*item*/) = Windows::Foundation::Collections::IVector<Platform::Object^>::InsertAt
|
||||
{
|
||||
throw ref new Platform::NotImplementedException();
|
||||
}
|
||||
virtual void RemoveAt(unsigned int /*index*/) = Windows::Foundation::Collections::IVector<Platform::Object^>::RemoveAt
|
||||
{
|
||||
throw ref new Platform::NotImplementedException();
|
||||
}
|
||||
virtual void RemoveAtEnd() = Windows::Foundation::Collections::IVector<Platform::Object^>::RemoveAtEnd
|
||||
{
|
||||
throw ref new Platform::NotImplementedException();
|
||||
}
|
||||
virtual void ReplaceAll(const Platform::Array<Platform::Object^>^ /*items*/) = Windows::Foundation::Collections::IVector<Platform::Object^>::ReplaceAll
|
||||
{
|
||||
throw ref new Platform::NotImplementedException();
|
||||
}
|
||||
virtual void SetAt(unsigned int /*index*/, Platform::Object^ /*item*/) = Windows::Foundation::Collections::IVector<Platform::Object^>::SetAt
|
||||
{
|
||||
throw ref new Platform::NotImplementedException();
|
||||
}
|
||||
|
||||
// Implemented methods
|
||||
virtual Platform::Object^ GetAt(unsigned int index) = Windows::Foundation::Collections::IVector<Platform::Object^>::GetAt
|
||||
{
|
||||
return m_source->GetAt(index);
|
||||
}
|
||||
|
||||
virtual bool IndexOf(Platform::Object^ item, unsigned int* index) = Windows::Foundation::Collections::IVector<Platform::Object^>::IndexOf
|
||||
{
|
||||
return m_source->IndexOf(item, index);
|
||||
}
|
||||
|
||||
property unsigned int Size
|
||||
{
|
||||
virtual unsigned int get() = Windows::Foundation::Collections::IVector<Platform::Object^>::Size::get
|
||||
{
|
||||
return m_source->Size;
|
||||
}
|
||||
}
|
||||
|
||||
// IObservableVector<Object^>
|
||||
event Windows::Foundation::Collections::VectorChangedEventHandler<Platform::Object^>^ VectorChanged
|
||||
{
|
||||
virtual Windows::Foundation::EventRegistrationToken add(Windows::Foundation::Collections::VectorChangedEventHandler<Platform::Object^>^ handler) = Windows::Foundation::Collections::IObservableVector<Platform::Object^>::VectorChanged::add
|
||||
{
|
||||
return m_vectorChanged += handler;
|
||||
}
|
||||
virtual void remove(Windows::Foundation::EventRegistrationToken token) = Windows::Foundation::Collections::IObservableVector<Platform::Object^>::VectorChanged::remove
|
||||
{
|
||||
m_vectorChanged -= token;
|
||||
}
|
||||
}
|
||||
|
||||
// IIterable<Object^>
|
||||
// Not implemented
|
||||
virtual Windows::Foundation::Collections::IIterator<Platform::Object^>^ First() = Windows::Foundation::Collections::IIterable<Platform::Object^>::First
|
||||
{
|
||||
throw ref new Platform::NotImplementedException();
|
||||
}
|
||||
|
||||
// Event handlers
|
||||
void OnSourceBindableVectorChanged(Windows::UI::Xaml::Interop::IBindableObservableVector^ source, Platform::Object^ e)
|
||||
{
|
||||
Windows::Foundation::Collections::IVectorChangedEventArgs^ args = safe_cast<Windows::Foundation::Collections::IVectorChangedEventArgs^>(e);
|
||||
m_vectorChanged(this, args);
|
||||
}
|
||||
|
||||
Windows::UI::Xaml::Interop::IBindableVector^ m_source;
|
||||
int m_currentPosition;
|
||||
event Windows::Foundation::EventHandler<Platform::Object^>^ m_currentChanged;
|
||||
event Windows::UI::Xaml::Data::CurrentChangingEventHandler^ m_currentChanging;
|
||||
event Windows::Foundation::Collections::VectorChangedEventHandler<Platform::Object^>^ m_vectorChanged;
|
||||
};
|
||||
|
||||
public ref class AlwaysSelectedCollectionViewConverter sealed: public Windows::UI::Xaml::Data::IValueConverter
|
||||
{
|
||||
public:
|
||||
AlwaysSelectedCollectionViewConverter()
|
||||
{ }
|
||||
|
||||
private:
|
||||
virtual Platform::Object^ Convert(
|
||||
Platform::Object^ value,
|
||||
Windows::UI::Xaml::Interop::TypeName /*targetType*/,
|
||||
Platform::Object^ /*parameter*/,
|
||||
Platform::String^ /*language*/) = Windows::UI::Xaml::Data::IValueConverter::Convert
|
||||
{
|
||||
auto result = dynamic_cast<Windows::UI::Xaml::Interop::IBindableVector^>(value);
|
||||
if (result)
|
||||
{
|
||||
return ref new AlwaysSelectedCollectionView(result);
|
||||
}
|
||||
return Windows::UI::Xaml::DependencyProperty::UnsetValue; // Can't convert
|
||||
}
|
||||
|
||||
virtual Platform::Object^ ConverBack(
|
||||
Platform::Object^ /*value*/,
|
||||
Windows::UI::Xaml::Interop::TypeName /*targetType*/,
|
||||
Platform::Object^ /*parameter*/,
|
||||
Platform::String^ /*language*/) = Windows::UI::Xaml::Data::IValueConverter::ConvertBack
|
||||
{
|
||||
return Windows::UI::Xaml::DependencyProperty::UnsetValue;
|
||||
}
|
||||
};
|
||||
}}
|
137
src/Calculator/Common/AppLifecycleLogger.cpp
Normal file
137
src/Calculator/Common/AppLifecycleLogger.cpp
Normal file
@@ -0,0 +1,137 @@
|
||||
// Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
// Licensed under the MIT License.
|
||||
|
||||
#include "pch.h"
|
||||
#include "AppLifecycleLogger.h"
|
||||
#include "CalcViewModel\Common\TraceActivity.h"
|
||||
|
||||
using namespace std;
|
||||
using namespace winrt;
|
||||
using namespace winrt::Windows::ApplicationModel;
|
||||
using namespace winrt::Windows::ApplicationModel::Background;
|
||||
using namespace winrt::Windows::ApplicationModel::Core;
|
||||
using namespace winrt::Windows::Foundation::Diagnostics;
|
||||
using namespace winrt::Windows::UI::ViewManagement;
|
||||
|
||||
namespace CalculatorApp
|
||||
{
|
||||
// c.f. WINEVENT_KEYWORD_RESERVED_63-56 0xFF00000000000000 // Bits 63-56 - channel keywords
|
||||
// c.f. WINEVENT_KEYWORD_* 0x00FF000000000000 // Bits 55-48 - system-reserved keywords
|
||||
constexpr int64_t MICROSOFT_KEYWORD_CRITICAL_DATA = 0x0000800000000000; // Bit 47
|
||||
constexpr int64_t MICROSOFT_KEYWORD_MEASURES = 0x0000400000000000; // Bit 46
|
||||
constexpr int64_t MICROSOFT_KEYWORD_TELEMETRY = 0x0000200000000000; // Bit 45
|
||||
constexpr int64_t MICROSOFT_KEYWORD_RESERVED_44 = 0x0000100000000000; // Bit 44 (reserved for future assignment)
|
||||
|
||||
#pragma region TraceLogger setup and cleanup
|
||||
|
||||
AppLifecycleLogger::AppLifecycleLogger() :
|
||||
m_appLifecycleProvider(
|
||||
L"Microsoft.Windows.AppLifeCycle",
|
||||
LoggingChannelOptions(GUID{ 0x4f50731a, 0x89cf, 0x4782, 0xb3, 0xe0, 0xdc, 0xe8, 0xc9, 0x4, 0x76, 0xba }), // Microsoft Telemetry group
|
||||
GUID{ 0xef00584a, 0x2655, 0x462c, 0xbc, 0x24, 0xe7, 0xde, 0x63, 0xe, 0x7f, 0xbf }) //Unique provider ID {EF00584A-2655-462C-BC24-E7DE630E7FBF}
|
||||
{
|
||||
}
|
||||
|
||||
AppLifecycleLogger::~AppLifecycleLogger()
|
||||
{
|
||||
}
|
||||
|
||||
AppLifecycleLogger& AppLifecycleLogger::GetInstance()
|
||||
{
|
||||
static AppLifecycleLogger s_selfInstance;
|
||||
return s_selfInstance;
|
||||
}
|
||||
|
||||
bool AppLifecycleLogger::GetTraceLoggingProviderEnabled() const
|
||||
{
|
||||
return m_appLifecycleProvider.Enabled();
|
||||
}
|
||||
|
||||
#pragma region Tracing methods
|
||||
void AppLifecycleLogger::LogAppLifecycleEvent(hstring const& eventName, LoggingFields const& fields) const
|
||||
{
|
||||
m_appLifecycleProvider.LogEvent(eventName, fields, LoggingLevel::Information, LoggingOptions(MICROSOFT_KEYWORD_TELEMETRY | WINEVENT_KEYWORD_RESPONSE_TIME));
|
||||
}
|
||||
#pragma endregion
|
||||
|
||||
void AppLifecycleLogger::LaunchUIResponsive() const
|
||||
{
|
||||
if (!GetTraceLoggingProviderEnabled()) return;
|
||||
|
||||
LoggingFields fields{};
|
||||
PopulateAppInfo(fields);
|
||||
LogAppLifecycleEvent(L"ModernAppLaunch_UIResponsive", fields);
|
||||
}
|
||||
|
||||
void AppLifecycleLogger::LaunchVisibleComplete() const
|
||||
{
|
||||
if (!GetTraceLoggingProviderEnabled()) return;
|
||||
|
||||
LoggingFields fields{};
|
||||
PopulateAppInfo(fields);
|
||||
LogAppLifecycleEvent(L"ModernAppLaunch_VisibleComplete", fields);
|
||||
}
|
||||
|
||||
void AppLifecycleLogger::ResumeUIResponsive() const
|
||||
{
|
||||
if (!GetTraceLoggingProviderEnabled()) return;
|
||||
|
||||
LoggingFields fields{};
|
||||
PopulateAppInfo(fields);
|
||||
LogAppLifecycleEvent(L"ModernAppResume_UIResponsive", fields);
|
||||
}
|
||||
|
||||
void AppLifecycleLogger::ResumeVisibleComplete() const
|
||||
{
|
||||
if (!GetTraceLoggingProviderEnabled()) return;
|
||||
|
||||
LoggingFields fields{};
|
||||
PopulateAppInfo(fields);
|
||||
LogAppLifecycleEvent(L"ModernAppResume_VisibleComplete", fields);
|
||||
}
|
||||
|
||||
void AppLifecycleLogger::ResizeUIResponsive() const
|
||||
{
|
||||
ResizeUIResponsive(ApplicationView::GetForCurrentView().Id());
|
||||
}
|
||||
|
||||
void AppLifecycleLogger::ResizeUIResponsive(int32_t viewId) const
|
||||
{
|
||||
if (!GetTraceLoggingProviderEnabled()) return;
|
||||
|
||||
LoggingFields fields{};
|
||||
PopulateAppInfo(fields);
|
||||
fields.AddInt32(L"ViewId", viewId);
|
||||
LogAppLifecycleEvent(L"ModernAppResize_UIResponsive", fields);
|
||||
}
|
||||
|
||||
void AppLifecycleLogger::ResizeVisibleComplete() const
|
||||
{
|
||||
ResizeVisibleComplete(ApplicationView::GetForCurrentView().Id());
|
||||
}
|
||||
|
||||
void AppLifecycleLogger::ResizeVisibleComplete(int32_t viewId) const
|
||||
{
|
||||
if (!GetTraceLoggingProviderEnabled()) return;
|
||||
|
||||
LoggingFields fields{};
|
||||
PopulateAppInfo(fields);
|
||||
fields.AddInt32(L"ViewId", viewId);
|
||||
LogAppLifecycleEvent(L"ModernAppResize_VisibleComplete", fields);
|
||||
}
|
||||
|
||||
void AppLifecycleLogger::PopulateAppInfo(LoggingFields& fields) const
|
||||
{
|
||||
auto appId = CoreApplication::Id();
|
||||
auto aumId = Package::Current().Id().FamilyName() + L"!" + appId;
|
||||
auto packageFullName = Package::Current().Id().FullName();
|
||||
auto psmKey = Package::Current().Id().FullName() + L"+" + appId;
|
||||
|
||||
fields.AddString(L"AumId", aumId);
|
||||
fields.AddString(L"PackageFullName", packageFullName);
|
||||
fields.AddString(L"PsmKey", psmKey);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
41
src/Calculator/Common/AppLifecycleLogger.h
Normal file
41
src/Calculator/Common/AppLifecycleLogger.h
Normal file
@@ -0,0 +1,41 @@
|
||||
// Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
// Licensed under the MIT License.
|
||||
|
||||
#pragma once
|
||||
|
||||
namespace CalculatorApp
|
||||
{
|
||||
class AppLifecycleLogger
|
||||
{
|
||||
public:
|
||||
AppLifecycleLogger(AppLifecycleLogger const&) = delete;
|
||||
AppLifecycleLogger const & operator= (AppLifecycleLogger const&) = delete;
|
||||
~AppLifecycleLogger();
|
||||
static AppLifecycleLogger& GetInstance();
|
||||
bool GetTraceLoggingProviderEnabled() const;
|
||||
|
||||
void LaunchUIResponsive() const;
|
||||
void LaunchVisibleComplete() const;
|
||||
void ResumeUIResponsive() const;
|
||||
void ResumeVisibleComplete() const;
|
||||
|
||||
void ResizeUIResponsive() const;
|
||||
void ResizeVisibleComplete() const;
|
||||
void ResizeUIResponsive(int32_t viewId) const;
|
||||
void ResizeVisibleComplete(int32_t viewId) const;
|
||||
|
||||
private:
|
||||
// Make the object construction private to allow singleton access to this class
|
||||
AppLifecycleLogger();
|
||||
|
||||
// Any new Log method should
|
||||
// a) decide the level of logging. This will help us in limiting recording of events only upto a certain level. See this link for guidance https://msdn.microsoft.com/en-us/library/windows/desktop/aa363742(v=vs.85).aspx
|
||||
// We're using Verbose level for events that are called frequently and needed only for debugging or capturing perf for specific scenarios
|
||||
// b) should decide whether or not to log to telemetry and pass TraceLoggingKeyword(MICROSOFT_KEYWORD_TELEMETRY) accordingly
|
||||
// c) Should accept a variable number of additional data arguments if needed
|
||||
void LogAppLifecycleEvent(winrt::hstring const& eventName, winrt::Windows::Foundation::Diagnostics::LoggingFields const& fields) const;
|
||||
void PopulateAppInfo(winrt::Windows::Foundation::Diagnostics::LoggingFields& fields) const;
|
||||
|
||||
winrt::Windows::Foundation::Diagnostics::LoggingChannel m_appLifecycleProvider;
|
||||
};
|
||||
}
|
34
src/Calculator/Common/BindableBase.cpp
Normal file
34
src/Calculator/Common/BindableBase.cpp
Normal file
@@ -0,0 +1,34 @@
|
||||
// Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
// Licensed under the MIT License.
|
||||
|
||||
#include "pch.h"
|
||||
#include "BindableBase.h"
|
||||
|
||||
using namespace CalculatorApp::Common;
|
||||
|
||||
using namespace Platform;
|
||||
using namespace Windows::UI::Xaml::Data;
|
||||
|
||||
/// <summary>
|
||||
/// Notifies listeners that a property value has changed.
|
||||
/// </summary>
|
||||
/// <param name="propertyName">Name of the property used to notify listeners.</param>
|
||||
void BindableBase::OnPropertyChanged(String^ propertyName)
|
||||
{
|
||||
PropertyChanged(this, ref new PropertyChangedEventArgs(propertyName));
|
||||
}
|
||||
|
||||
Windows::UI::Xaml::Data::ICustomProperty^ BindableBase::GetCustomProperty(Platform::String^ name)
|
||||
{
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
Windows::UI::Xaml::Data::ICustomProperty^ BindableBase::GetIndexedProperty(Platform::String^ name, Windows::UI::Xaml::Interop::TypeName type)
|
||||
{
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
Platform::String^ BindableBase::GetStringRepresentation()
|
||||
{
|
||||
return this->ToString();
|
||||
}
|
35
src/Calculator/Common/BindableBase.h
Normal file
35
src/Calculator/Common/BindableBase.h
Normal file
@@ -0,0 +1,35 @@
|
||||
// Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
// Licensed under the MIT License.
|
||||
|
||||
#pragma once
|
||||
|
||||
namespace CalculatorApp
|
||||
{
|
||||
namespace Common
|
||||
{
|
||||
/// <summary>
|
||||
/// Implementation of <see cref="INotifyPropertyChanged"/> to simplify models.
|
||||
/// </summary>
|
||||
[Windows::Foundation::Metadata::WebHostHidden]
|
||||
public ref class BindableBase : Windows::UI::Xaml::DependencyObject, Windows::UI::Xaml::Data::INotifyPropertyChanged, Windows::UI::Xaml::Data::ICustomPropertyProvider
|
||||
{
|
||||
public:
|
||||
virtual event Windows::UI::Xaml::Data::PropertyChangedEventHandler^ PropertyChanged;
|
||||
|
||||
public:
|
||||
// ICustomPropertyProvider
|
||||
virtual Windows::UI::Xaml::Data::ICustomProperty^ GetCustomProperty(Platform::String^ name);
|
||||
virtual Windows::UI::Xaml::Data::ICustomProperty^ GetIndexedProperty(Platform::String^ name, Windows::UI::Xaml::Interop::TypeName type);
|
||||
virtual Platform::String^ GetStringRepresentation();
|
||||
|
||||
property Windows::UI::Xaml::Interop::TypeName Type
|
||||
{
|
||||
virtual Windows::UI::Xaml::Interop::TypeName get() { return this->GetType(); }
|
||||
}
|
||||
|
||||
|
||||
protected:
|
||||
virtual void OnPropertyChanged(Platform::String^ propertyName);
|
||||
};
|
||||
}
|
||||
}
|
322
src/Calculator/Common/LayoutAwarePage.cpp
Normal file
322
src/Calculator/Common/LayoutAwarePage.cpp
Normal file
@@ -0,0 +1,322 @@
|
||||
// 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<String, Object>"/> 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<String, Object>"/> 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
|
75
src/Calculator/Common/LayoutAwarePage.h
Normal file
75
src/Calculator/Common/LayoutAwarePage.h
Normal file
@@ -0,0 +1,75 @@
|
||||
// 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
|
||||
};
|
||||
}
|
||||
}
|
494
src/Calculator/Common/SuspensionManager.cpp
Normal file
494
src/Calculator/Common/SuspensionManager.cpp
Normal file
@@ -0,0 +1,494 @@
|
||||
// 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
|
43
src/Calculator/Common/SuspensionManager.h
Normal file
43
src/Calculator/Common/SuspensionManager.h
Normal file
@@ -0,0 +1,43 @@
|
||||
// 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);
|
||||
};
|
||||
}
|
||||
}
|
94
src/Calculator/Common/TitleBarHelper.cpp
Normal file
94
src/Calculator/Common/TitleBarHelper.cpp
Normal file
@@ -0,0 +1,94 @@
|
||||
// Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
// Licensed under the MIT License.
|
||||
|
||||
#include "pch.h"
|
||||
#include "TitleBarHelper.h"
|
||||
#include "Converters\BooleanToVisibilityConverter.h"
|
||||
#include "CalcViewModel\ViewState.h"
|
||||
|
||||
using namespace CalculatorApp::Common;
|
||||
using namespace CalculatorApp::Converters;
|
||||
using namespace Platform;
|
||||
using namespace std;
|
||||
using namespace Windows::ApplicationModel::Core;
|
||||
using namespace Windows::Foundation;
|
||||
using namespace Windows::UI::Xaml;
|
||||
|
||||
unique_ptr<TitleBarHelper> TitleBarHelper::CreateTitleBarHelperIfNotDocked(FrameworkElement^ customTitleBar)
|
||||
{
|
||||
return (App::GetAppViewState() == ViewState::DockedView)
|
||||
? nullptr
|
||||
: CalculatorApp::Common::TitleBarHelper::CreateTitleBarHelper(customTitleBar);
|
||||
}
|
||||
|
||||
unique_ptr<TitleBarHelper> TitleBarHelper::CreateTitleBarHelper(_In_ FrameworkElement^ customTitleBar)
|
||||
{
|
||||
assert(customTitleBar != nullptr);
|
||||
if (customTitleBar != nullptr)
|
||||
{
|
||||
CoreApplicationViewTitleBar^ coreTitleBar = CoreApplication::GetCurrentView()->TitleBar;
|
||||
assert(coreTitleBar != nullptr);
|
||||
if (coreTitleBar != nullptr)
|
||||
{
|
||||
return make_unique<TitleBarHelper>(coreTitleBar, customTitleBar);
|
||||
}
|
||||
}
|
||||
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
TitleBarHelper::TitleBarHelper(_In_ CoreApplicationViewTitleBar^ coreTitleBar, _In_ FrameworkElement^ customTitleBar) :
|
||||
m_coreTitleBar(coreTitleBar),
|
||||
m_customTitleBar(customTitleBar)
|
||||
{
|
||||
RegisterForLayoutChanged();
|
||||
RegisterForVisibilityChanged();
|
||||
SetCustomTitleBar();
|
||||
}
|
||||
|
||||
TitleBarHelper::~TitleBarHelper()
|
||||
{
|
||||
m_coreTitleBar->LayoutMetricsChanged -= m_layoutChangedToken;
|
||||
m_coreTitleBar->IsVisibleChanged -= m_visibilityChangedToken;
|
||||
}
|
||||
|
||||
void TitleBarHelper::SetTitleBarHeight(double height)
|
||||
{
|
||||
m_customTitleBar->Height = height;
|
||||
}
|
||||
|
||||
void TitleBarHelper::SetTitleBarVisibility(bool isVisible)
|
||||
{
|
||||
m_customTitleBar->Visibility = BooleanToVisibilityConverter::Convert(isVisible);
|
||||
}
|
||||
|
||||
void TitleBarHelper::RegisterForLayoutChanged()
|
||||
{
|
||||
m_layoutChangedToken =
|
||||
m_coreTitleBar->LayoutMetricsChanged += ref new TypedEventHandler<CoreApplicationViewTitleBar^, Object^>(
|
||||
[this](CoreApplicationViewTitleBar^ cTitleBar, Object^)
|
||||
{
|
||||
// Update title bar control size as needed to account for system size changes
|
||||
SetTitleBarHeight(cTitleBar->Height);
|
||||
});
|
||||
}
|
||||
|
||||
void TitleBarHelper::RegisterForVisibilityChanged()
|
||||
{
|
||||
m_visibilityChangedToken =
|
||||
m_coreTitleBar->IsVisibleChanged += ref new TypedEventHandler<CoreApplicationViewTitleBar^, Object^>(
|
||||
[this](CoreApplicationViewTitleBar^ cTitleBar, Object^)
|
||||
{
|
||||
// Update title bar visibility
|
||||
SetTitleBarVisibility(cTitleBar->IsVisible);
|
||||
});
|
||||
}
|
||||
|
||||
void TitleBarHelper::SetCustomTitleBar()
|
||||
{
|
||||
// Set custom XAML Title Bar
|
||||
m_coreTitleBar->ExtendViewIntoTitleBar = true;
|
||||
SetTitleBarHeight(m_coreTitleBar->Height);
|
||||
SetTitleBarVisibility(m_coreTitleBar->IsVisible);
|
||||
Window::Current->SetTitleBar(m_customTitleBar);
|
||||
}
|
40
src/Calculator/Common/TitleBarHelper.h
Normal file
40
src/Calculator/Common/TitleBarHelper.h
Normal file
@@ -0,0 +1,40 @@
|
||||
// Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
// Licensed under the MIT License.
|
||||
|
||||
#pragma once
|
||||
|
||||
namespace CalculatorApp
|
||||
{
|
||||
namespace Common
|
||||
{
|
||||
class TitleBarHelper
|
||||
{
|
||||
public:
|
||||
static std::unique_ptr<TitleBarHelper> CreateTitleBarHelperIfNotDocked(
|
||||
_In_ Windows::UI::Xaml::FrameworkElement^ customTitleBar);
|
||||
|
||||
// Prefer CreateTitleBarHelper over constructing your own instance,
|
||||
// because Create* will nullcheck the parameters.
|
||||
static std::unique_ptr<TitleBarHelper> CreateTitleBarHelper(
|
||||
_In_ Windows::UI::Xaml::FrameworkElement^ customTitleBar);
|
||||
|
||||
TitleBarHelper(
|
||||
_In_ Windows::ApplicationModel::Core::CoreApplicationViewTitleBar^ coreTitleBar,
|
||||
_In_ Windows::UI::Xaml::FrameworkElement^ customTitleBar);
|
||||
~TitleBarHelper();
|
||||
|
||||
void SetTitleBarHeight(double height);
|
||||
void SetTitleBarVisibility(bool isVisible);
|
||||
|
||||
private:
|
||||
void RegisterForLayoutChanged();
|
||||
void RegisterForVisibilityChanged();
|
||||
void SetCustomTitleBar();
|
||||
|
||||
Platform::Agile<Windows::ApplicationModel::Core::CoreApplicationViewTitleBar^> m_coreTitleBar;
|
||||
Windows::UI::Xaml::FrameworkElement^ m_customTitleBar;
|
||||
Windows::Foundation::EventRegistrationToken m_layoutChangedToken;
|
||||
Windows::Foundation::EventRegistrationToken m_visibilityChangedToken;
|
||||
};
|
||||
}
|
||||
}
|
Reference in New Issue
Block a user