mirror of
https://github.com/Microsoft/calculator.git
synced 2025-07-06 05:01:07 -07:00
Cherry-pick for 2403 (#2166)
* Fix #2146: SWIPE DELETE FUNCTION FREEZES APP (#2157) * fix stackoverflow * space * Multi-instance (#2161) * launch multi instances * trace error * simplify the graphing test * remove an assertion * simplify some code * remove some trivial code. * fix runtime cast * Resize on the first launch (#2165)
This commit is contained in:
parent
fcbda43c12
commit
f96b7a56ee
7 changed files with 85 additions and 595 deletions
|
@ -50,34 +50,19 @@ static constexpr int GRAPHING_ID = 17;
|
|||
namespace // put the utils within this TU
|
||||
{
|
||||
Platform::String^ CurrentUserId;
|
||||
std::mutex GraphingModeCheckMutex;
|
||||
|
||||
bool IsGraphingModeEnabled()
|
||||
{
|
||||
static bool isChecked = false;
|
||||
static bool isEnabled = false;
|
||||
|
||||
std::scoped_lock<std::mutex> lock(GraphingModeCheckMutex);
|
||||
if (isChecked)
|
||||
{
|
||||
return isEnabled;
|
||||
}
|
||||
else
|
||||
static auto enabled = []
|
||||
{
|
||||
auto user = User::GetFromId(CurrentUserId);
|
||||
if (user == nullptr)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
auto namedPolicyData = NamedPolicy::GetPolicyFromPathForUser(
|
||||
user,
|
||||
L"Education",
|
||||
L"AllowGraphingCalculator");
|
||||
isEnabled = namedPolicyData->GetBoolean();
|
||||
isChecked = true;
|
||||
return isEnabled;
|
||||
}
|
||||
return NamedPolicy::GetPolicyFromPathForUser(user, L"Education", L"AllowGraphingCalculator")->GetBoolean();
|
||||
}();
|
||||
return enabled;
|
||||
}
|
||||
|
||||
// The order of items in this list determines the order of items in the menu.
|
||||
|
@ -318,7 +303,6 @@ NavCategoryGroup::NavCategoryGroup(const NavCategoryGroupInitializer& groupIniti
|
|||
|
||||
void NavCategoryStates::SetCurrentUser(Platform::String^ userId)
|
||||
{
|
||||
std::scoped_lock<std::mutex> lock(GraphingModeCheckMutex);
|
||||
CurrentUserId = userId;
|
||||
}
|
||||
|
||||
|
@ -518,13 +502,6 @@ bool NavCategoryStates::IsValidViewMode(ViewMode mode)
|
|||
|
||||
bool NavCategoryStates::IsViewModeEnabled(ViewMode mode)
|
||||
{
|
||||
if (mode != ViewMode::Graphing)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
else
|
||||
{
|
||||
return IsGraphingModeEnabled();
|
||||
}
|
||||
return mode != ViewMode::Graphing ? true : IsGraphingModeEnabled();
|
||||
}
|
||||
|
||||
|
|
|
@ -6,37 +6,24 @@
|
|||
// Declaration of the App class.
|
||||
//
|
||||
|
||||
using CalculatorApp.ViewModel.Common;
|
||||
using CalculatorApp.ViewModel.Common.Automation;
|
||||
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Diagnostics;
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
using Windows.ApplicationModel;
|
||||
using Windows.ApplicationModel.Activation;
|
||||
using Windows.ApplicationModel.Core;
|
||||
using Windows.Foundation;
|
||||
using Windows.Storage;
|
||||
using Windows.UI.Core;
|
||||
using Windows.UI.StartScreen;
|
||||
using Windows.UI.ViewManagement;
|
||||
using Windows.UI.Xaml;
|
||||
using Windows.UI.Xaml.Controls;
|
||||
|
||||
using CalculatorApp.Utils;
|
||||
using CalculatorApp.ViewModel.Common;
|
||||
using CalculatorApp.ViewModel.Common.Automation;
|
||||
|
||||
namespace CalculatorApp
|
||||
{
|
||||
namespace ApplicationResourceKeys
|
||||
{
|
||||
public static class Globals
|
||||
{
|
||||
public static readonly string AppMinWindowHeight = "AppMinWindowHeight";
|
||||
public static readonly string AppMinWindowWidth = "AppMinWindowWidth";
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Provides application-specific behavior to supplement the default Application class.
|
||||
/// </summary>
|
||||
|
@ -49,17 +36,12 @@ namespace CalculatorApp
|
|||
public App()
|
||||
{
|
||||
InitializeComponent();
|
||||
|
||||
m_preLaunched = false;
|
||||
|
||||
RegisterDependencyProperties();
|
||||
NarratorNotifier.RegisterDependencyProperties();
|
||||
|
||||
// TODO: MSFT 14645325: Set this directly from XAML.
|
||||
// Currently this is bugged so the property is only respected from code-behind.
|
||||
HighContrastAdjustment = ApplicationHighContrastAdjustment.None;
|
||||
|
||||
Suspending += OnSuspending;
|
||||
|
||||
#if DEBUG
|
||||
DebugSettings.IsBindingTracingEnabled = true;
|
||||
DebugSettings.BindingFailed += (sender, args) =>
|
||||
|
@ -81,18 +63,12 @@ namespace CalculatorApp
|
|||
/// <param name="e">Details about the launch request and process.</param>
|
||||
protected override void OnLaunched(LaunchActivatedEventArgs args)
|
||||
{
|
||||
if (args.PrelaunchActivated)
|
||||
{
|
||||
// If the app got pre-launch activated, then save that state in a flag
|
||||
m_preLaunched = true;
|
||||
}
|
||||
|
||||
NavCategoryStates.SetCurrentUser(args.User.NonRoamableId);
|
||||
|
||||
// It takes time to check GraphingMode at the 1st time. So, do it in a background thread
|
||||
// It takes time to check GraphingMode at the very first time. Warm up in a background thread.
|
||||
Task.Run(() => NavCategoryStates.IsViewModeEnabled(ViewMode.Graphing));
|
||||
|
||||
OnAppLaunch(args, args.Arguments);
|
||||
OnAppLaunch(args, args.Arguments, args.PrelaunchActivated);
|
||||
}
|
||||
|
||||
protected override void OnActivated(IActivatedEventArgs args)
|
||||
|
@ -101,76 +77,36 @@ namespace CalculatorApp
|
|||
{
|
||||
// We currently don't pass the uri as an argument,
|
||||
// and handle any protocol launch as a normal app launch.
|
||||
OnAppLaunch(args, null);
|
||||
OnAppLaunch(args, null, false);
|
||||
}
|
||||
}
|
||||
|
||||
internal void RemoveWindow(WindowFrameService frameService)
|
||||
{
|
||||
// Shell does not allow killing the main window.
|
||||
if (m_mainViewId != frameService.GetViewId())
|
||||
{
|
||||
_ = HandleViewReleaseAndRemoveWindowFromMap(frameService);
|
||||
}
|
||||
}
|
||||
|
||||
internal void RemoveSecondaryWindow(WindowFrameService frameService)
|
||||
{
|
||||
// Shell does not allow killing the main window.
|
||||
if (m_mainViewId != frameService.GetViewId())
|
||||
{
|
||||
RemoveWindowFromMap(frameService.GetViewId());
|
||||
}
|
||||
}
|
||||
|
||||
private static Frame CreateFrame()
|
||||
{
|
||||
var frame = new Frame
|
||||
{
|
||||
FlowDirection = LocalizationService.GetInstance().GetFlowDirection()
|
||||
};
|
||||
return frame;
|
||||
}
|
||||
|
||||
private static void SetMinWindowSizeAndThemeAndActivate(Frame rootFrame, Size minWindowSize)
|
||||
{
|
||||
// SetPreferredMinSize should always be called before Window.Activate
|
||||
ApplicationView appView = ApplicationView.GetForCurrentView();
|
||||
appView.SetPreferredMinSize(minWindowSize);
|
||||
|
||||
// Place the frame in the current Window
|
||||
Window.Current.Content = rootFrame;
|
||||
CalculatorApp.Utils.ThemeHelper.InitializeAppTheme();
|
||||
Window.Current.Activate();
|
||||
}
|
||||
|
||||
private void OnAppLaunch(IActivatedEventArgs args, string argument)
|
||||
private void OnAppLaunch(IActivatedEventArgs args, string argument, bool isPreLaunch)
|
||||
{
|
||||
// Uncomment the following lines to display frame-rate and per-frame CPU usage info.
|
||||
//#if _DEBUG
|
||||
//#if DEBUG
|
||||
// if (IsDebuggerPresent())
|
||||
// {
|
||||
// DebugSettings->EnableFrameRateCounter = true;
|
||||
// DebugSettings.EnableFrameRateCounter = true;
|
||||
// }
|
||||
//#endif
|
||||
|
||||
args.SplashScreen.Dismissed += DismissedEventHandler;
|
||||
args.SplashScreen.Dismissed += async (_, __) => await SetupJumpListAsync();
|
||||
|
||||
var rootFrame = (Window.Current.Content as Frame);
|
||||
WeakReference weak = new WeakReference(this);
|
||||
var minWindowWidth = Convert.ToSingle(Resources["AppMinWindowWidth"]);
|
||||
var minWindowHeight = Convert.ToSingle(Resources["AppMinWindowHeight"]);
|
||||
var minWindowSize = SizeHelper.FromDimensions(minWindowWidth, minWindowHeight);
|
||||
var appView = ApplicationView.GetForCurrentView();
|
||||
var localSettings = ApplicationData.Current.LocalSettings;
|
||||
|
||||
float minWindowWidth = (float)((double)Resources[ApplicationResourceKeys.Globals.AppMinWindowWidth]);
|
||||
float minWindowHeight = (float)((double)Resources[ApplicationResourceKeys.Globals.AppMinWindowHeight]);
|
||||
Size minWindowSize = SizeHelper.FromDimensions(minWindowWidth, minWindowHeight);
|
||||
// SetPreferredMinSize should always be called before Window.Activate
|
||||
appView.SetPreferredMinSize(minWindowSize);
|
||||
|
||||
ApplicationView appView = ApplicationView.GetForCurrentView();
|
||||
ApplicationDataContainer localSettings = ApplicationData.Current.LocalSettings;
|
||||
// For very first launch, set the size of the calc as size of the default standard mode
|
||||
if (!localSettings.Values.ContainsKey("VeryFirstLaunch"))
|
||||
{
|
||||
localSettings.Values["VeryFirstLaunch"] = false;
|
||||
appView.SetPreferredMinSize(minWindowSize);
|
||||
appView.TryResizeView(minWindowSize);
|
||||
appView.TryResizeView(minWindowSize); // the requested size must not be less than the min size.
|
||||
}
|
||||
else
|
||||
{
|
||||
|
@ -179,234 +115,42 @@ namespace CalculatorApp
|
|||
|
||||
// Do not repeat app initialization when the Window already has content,
|
||||
// just ensure that the window is active
|
||||
var rootFrame = Window.Current.Content as Frame;
|
||||
if (rootFrame == null)
|
||||
{
|
||||
if (!Windows.Foundation.Metadata.ApiInformation.IsTypePresent("Windows.Phone.UI.Input.HardwareButtons")) // PC Family
|
||||
rootFrame = new Frame
|
||||
{
|
||||
// Disable the system view activation policy during the first launch of the app
|
||||
// only for PC family devices and not for phone family devices
|
||||
try
|
||||
{
|
||||
ApplicationViewSwitcher.DisableSystemViewActivationPolicy();
|
||||
}
|
||||
catch (Exception)
|
||||
{
|
||||
// Log that DisableSystemViewActionPolicy didn't work
|
||||
}
|
||||
FlowDirection = LocalizationService.GetInstance().GetFlowDirection()
|
||||
};
|
||||
}
|
||||
|
||||
// Create a Frame to act as the navigation context
|
||||
rootFrame = App.CreateFrame();
|
||||
if (isPreLaunch)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
// When the navigation stack isn't restored navigate to the first page,
|
||||
// configuring the new page by passing required information as a navigation
|
||||
// parameter
|
||||
if (!rootFrame.Navigate(typeof(MainPage), argument))
|
||||
if (rootFrame.Content == null && !rootFrame.Navigate(typeof(MainPage), argument))
|
||||
{
|
||||
// We couldn't navigate to the main page, kill the app so we have a good
|
||||
// stack to debug
|
||||
throw new SystemException();
|
||||
throw new SystemException("6d430286-eb5d-4f8d-95d2-3d1059552968");
|
||||
}
|
||||
|
||||
SetMinWindowSizeAndThemeAndActivate(rootFrame, minWindowSize);
|
||||
m_mainViewId = ApplicationView.GetForCurrentView().Id;
|
||||
AddWindowToMap(WindowFrameService.CreateNewWindowFrameService(rootFrame, false, weak));
|
||||
}
|
||||
else
|
||||
{
|
||||
// For first launch, LaunchStart is logged in constructor, this is for subsequent launches.
|
||||
|
||||
// !Phone check is required because even in continuum mode user interaction mode is Mouse not Touch
|
||||
if ((UIViewSettings.GetForCurrentView().UserInteractionMode == UserInteractionMode.Mouse)
|
||||
&& (!Windows.Foundation.Metadata.ApiInformation.IsTypePresent("Windows.Phone.UI.Input.HardwareButtons")))
|
||||
{
|
||||
// If the pre-launch hasn't happened then allow for the new window/view creation
|
||||
if (!m_preLaunched)
|
||||
{
|
||||
var newCoreAppView = CoreApplication.CreateNewView();
|
||||
_ = newCoreAppView.Dispatcher.RunAsync(
|
||||
CoreDispatcherPriority.Normal, async () =>
|
||||
{
|
||||
if (weak.Target is App that)
|
||||
{
|
||||
var newRootFrame = App.CreateFrame();
|
||||
|
||||
SetMinWindowSizeAndThemeAndActivate(newRootFrame, minWindowSize);
|
||||
|
||||
if (!newRootFrame.Navigate(typeof(MainPage), argument))
|
||||
{
|
||||
// We couldn't navigate to the main page, kill the app so we have a good
|
||||
// stack to debug
|
||||
throw new SystemException();
|
||||
}
|
||||
|
||||
var frameService = WindowFrameService.CreateNewWindowFrameService(newRootFrame, true, weak);
|
||||
that.AddWindowToMap(frameService);
|
||||
|
||||
var dispatcher = CoreWindow.GetForCurrentThread().Dispatcher;
|
||||
|
||||
// CSHARP_MIGRATION_ANNOTATION:
|
||||
// class SafeFrameWindowCreation is being interpreted into a IDisposable class
|
||||
// in order to enhance its RAII capability that was written in C++/CX
|
||||
using (var safeFrameServiceCreation = new SafeFrameWindowCreation(frameService, that))
|
||||
{
|
||||
int newWindowId = ApplicationView.GetApplicationViewIdForWindow(CoreWindow.GetForCurrentThread());
|
||||
|
||||
ActivationViewSwitcher activationViewSwitcher = null;
|
||||
var activateEventArgs = (args as IViewSwitcherProvider);
|
||||
if (activateEventArgs != null)
|
||||
{
|
||||
activationViewSwitcher = activateEventArgs.ViewSwitcher;
|
||||
}
|
||||
|
||||
if (activationViewSwitcher != null)
|
||||
{
|
||||
_ = activationViewSwitcher.ShowAsStandaloneAsync(newWindowId, ViewSizePreference.Default);
|
||||
safeFrameServiceCreation.SetOperationSuccess(true);
|
||||
}
|
||||
else
|
||||
{
|
||||
var activatedEventArgs = (args as IApplicationViewActivatedEventArgs);
|
||||
if ((activatedEventArgs != null) && (activatedEventArgs.CurrentlyShownApplicationViewId != 0))
|
||||
{
|
||||
// CSHARP_MIGRATION_ANNOTATION:
|
||||
// here we don't use ContinueWith() to interpret origin code because we would like to
|
||||
// pursue the design of class SafeFrameWindowCreate whichi was using RAII to ensure
|
||||
// some states get handled properly when its instance is being destructed.
|
||||
//
|
||||
// To achieve that, SafeFrameWindowCreate has been reinterpreted using IDisposable
|
||||
// pattern, which forces we use below way to keep async works being controlled within
|
||||
// a same code block.
|
||||
var viewShown = await ApplicationViewSwitcher.TryShowAsStandaloneAsync(
|
||||
frameService.GetViewId(),
|
||||
ViewSizePreference.Default,
|
||||
activatedEventArgs.CurrentlyShownApplicationViewId,
|
||||
ViewSizePreference.Default);
|
||||
// SafeFrameServiceCreation is used to automatically remove the frame
|
||||
// from the list of frames if something goes bad.
|
||||
safeFrameServiceCreation.SetOperationSuccess(viewShown);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
else
|
||||
{
|
||||
ActivationViewSwitcher activationViewSwitcher = null;
|
||||
var activateEventArgs = (args as IViewSwitcherProvider);
|
||||
if (activateEventArgs != null)
|
||||
{
|
||||
activationViewSwitcher = activateEventArgs.ViewSwitcher;
|
||||
}
|
||||
|
||||
if (activationViewSwitcher != null)
|
||||
{
|
||||
_ = activationViewSwitcher.ShowAsStandaloneAsync(
|
||||
ApplicationView.GetApplicationViewIdForWindow(CoreWindow.GetForCurrentThread()), ViewSizePreference.Default);
|
||||
}
|
||||
else
|
||||
{
|
||||
TraceLogger.GetInstance().LogError(ViewMode.None, "App.OnAppLaunch", "Null_ActivationViewSwitcher");
|
||||
}
|
||||
}
|
||||
// Set the preLaunched flag to false
|
||||
m_preLaunched = false;
|
||||
}
|
||||
else // for touch devices
|
||||
{
|
||||
if (rootFrame.Content == null)
|
||||
{
|
||||
// When the navigation stack isn't restored navigate to the first page,
|
||||
// configuring the new page by passing required information as a navigation
|
||||
// parameter
|
||||
if (!rootFrame.Navigate(typeof(MainPage), argument))
|
||||
{
|
||||
// We couldn't navigate to the main page,
|
||||
// kill the app so we have a good stack to debug
|
||||
throw new SystemException();
|
||||
}
|
||||
}
|
||||
if (ApplicationView.GetForCurrentView().ViewMode != ApplicationViewMode.CompactOverlay)
|
||||
{
|
||||
if (!Windows.Foundation.Metadata.ApiInformation.IsTypePresent("Windows.Phone.UI.Input.HardwareButtons"))
|
||||
{
|
||||
// for tablet mode: since system view activation policy is disabled so do ShowAsStandaloneAsync if activationViewSwitcher exists in
|
||||
// activationArgs
|
||||
ActivationViewSwitcher activationViewSwitcher = null;
|
||||
var activateEventArgs = (args as IViewSwitcherProvider);
|
||||
if (activateEventArgs != null)
|
||||
{
|
||||
activationViewSwitcher = activateEventArgs.ViewSwitcher;
|
||||
}
|
||||
if (activationViewSwitcher != null)
|
||||
{
|
||||
var viewId = (args as IApplicationViewActivatedEventArgs).CurrentlyShownApplicationViewId;
|
||||
if (viewId != 0)
|
||||
{
|
||||
_ = activationViewSwitcher.ShowAsStandaloneAsync(viewId);
|
||||
}
|
||||
}
|
||||
}
|
||||
// Ensure the current window is active
|
||||
// Place the frame in the current Window
|
||||
Window.Current.Content = rootFrame;
|
||||
ThemeHelper.InitializeAppTheme();
|
||||
Window.Current.Activate();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void DismissedEventHandler(SplashScreen sender, object e)
|
||||
{
|
||||
_ = SetupJumpList();
|
||||
}
|
||||
|
||||
private void RegisterDependencyProperties()
|
||||
{
|
||||
NarratorNotifier.RegisterDependencyProperties();
|
||||
}
|
||||
|
||||
private void OnSuspending(object sender, SuspendingEventArgs args)
|
||||
{
|
||||
TraceLogger.GetInstance().LogButtonUsage();
|
||||
}
|
||||
|
||||
private sealed class SafeFrameWindowCreation : IDisposable
|
||||
{
|
||||
public SafeFrameWindowCreation(WindowFrameService frameService, App parent)
|
||||
{
|
||||
m_frameService = frameService;
|
||||
m_frameOpenedInWindow = false;
|
||||
m_parent = parent;
|
||||
}
|
||||
|
||||
public void SetOperationSuccess(bool success)
|
||||
{
|
||||
m_frameOpenedInWindow = success;
|
||||
}
|
||||
|
||||
public void Dispose()
|
||||
{
|
||||
if (!m_frameOpenedInWindow)
|
||||
{
|
||||
// Close the window as the navigation to the window didn't succeed
|
||||
// and this is not visible to the user.
|
||||
m_parent.RemoveWindowFromMap(m_frameService.GetViewId());
|
||||
}
|
||||
|
||||
GC.SuppressFinalize(this);
|
||||
}
|
||||
|
||||
~SafeFrameWindowCreation()
|
||||
{
|
||||
Dispose();
|
||||
}
|
||||
|
||||
private readonly WindowFrameService m_frameService;
|
||||
private bool m_frameOpenedInWindow;
|
||||
private readonly App m_parent;
|
||||
};
|
||||
|
||||
private async Task SetupJumpList()
|
||||
private async Task SetupJumpListAsync()
|
||||
{
|
||||
try
|
||||
{
|
||||
|
@ -426,87 +170,19 @@ namespace CalculatorApp
|
|||
var item = JumpListItem.CreateWithArguments(((int)mode).ToString(), "ms-resource:///Resources/" + NavCategoryStates.GetNameResourceKey(mode));
|
||||
item.Description = "ms-resource:///Resources/" + NavCategoryStates.GetNameResourceKey(mode);
|
||||
item.Logo = new Uri("ms-appx:///Assets/" + mode + ".png");
|
||||
|
||||
jumpList.Items.Add(item);
|
||||
}
|
||||
|
||||
await jumpList.SaveAsync();
|
||||
}
|
||||
catch
|
||||
catch (Exception ex)
|
||||
{
|
||||
}
|
||||
}
|
||||
|
||||
private async Task HandleViewReleaseAndRemoveWindowFromMap(WindowFrameService frameService)
|
||||
{
|
||||
WeakReference weak = new WeakReference(this);
|
||||
|
||||
// Unregister the event handler of the Main Page
|
||||
var frame = (Window.Current.Content as Frame);
|
||||
var mainPage = (frame.Content as MainPage);
|
||||
|
||||
mainPage.UnregisterEventHandlers();
|
||||
|
||||
await frameService.HandleViewRelease();
|
||||
await Task.Run(() =>
|
||||
{
|
||||
var that = weak.Target as App;
|
||||
that.RemoveWindowFromMap(frameService.GetViewId());
|
||||
}).ConfigureAwait(false /* task_continuation_context::use_arbitrary() */);
|
||||
}
|
||||
|
||||
private void AddWindowToMap(WindowFrameService frameService)
|
||||
{
|
||||
m_windowsMapLock.EnterWriteLock();
|
||||
try
|
||||
{
|
||||
m_secondaryWindows[frameService.GetViewId()] = frameService;
|
||||
TraceLogger.GetInstance().UpdateWindowCount(Convert.ToUInt64(m_secondaryWindows.Count));
|
||||
}
|
||||
finally
|
||||
{
|
||||
m_windowsMapLock.ExitWriteLock();
|
||||
}
|
||||
}
|
||||
|
||||
private WindowFrameService GetWindowFromMap(int viewId)
|
||||
{
|
||||
m_windowsMapLock.EnterReadLock();
|
||||
try
|
||||
{
|
||||
if (m_secondaryWindows.TryGetValue(viewId, out var windowMapEntry))
|
||||
{
|
||||
return windowMapEntry;
|
||||
}
|
||||
else
|
||||
{
|
||||
return null;
|
||||
}
|
||||
}
|
||||
finally
|
||||
{
|
||||
m_windowsMapLock.ExitReadLock();
|
||||
}
|
||||
}
|
||||
|
||||
private void RemoveWindowFromMap(int viewId)
|
||||
{
|
||||
m_windowsMapLock.EnterWriteLock();
|
||||
try
|
||||
{
|
||||
bool removed = m_secondaryWindows.Remove(viewId);
|
||||
Debug.Assert(removed != false, "Window does not exist in the list");
|
||||
}
|
||||
finally
|
||||
{
|
||||
m_windowsMapLock.ExitWriteLock();
|
||||
}
|
||||
}
|
||||
|
||||
private readonly ReaderWriterLockSlim m_windowsMapLock = new ReaderWriterLockSlim();
|
||||
private readonly Dictionary<int, WindowFrameService> m_secondaryWindows = new Dictionary<int, WindowFrameService>();
|
||||
private int m_mainViewId;
|
||||
private bool m_preLaunched;
|
||||
TraceLogger.GetInstance().LogError(ViewMode.None, nameof(SetupJumpListAsync), ex.ToString());
|
||||
#if DEBUG
|
||||
throw;
|
||||
#endif
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -250,7 +250,6 @@
|
|||
<Compile Include="Views\UnitConverter.xaml.cs">
|
||||
<DependentUpon>UnitConverter.xaml</DependentUpon>
|
||||
</Compile>
|
||||
<Compile Include="WindowFrameService.cs" />
|
||||
</ItemGroup>
|
||||
<ItemGroup Condition="'$(UseReleaseAppxManifest)' == 'True'">
|
||||
<AppxManifest Include="Package.Release.appxmanifest">
|
||||
|
|
|
@ -1,5 +1,12 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<Package xmlns="http://schemas.microsoft.com/appx/manifest/foundation/windows10" xmlns:mp="http://schemas.microsoft.com/appx/2014/phone/manifest" xmlns:uap="http://schemas.microsoft.com/appx/manifest/uap/windows10" xmlns:uap3="http://schemas.microsoft.com/appx/manifest/uap/windows10/3" xmlns:uap5="http://schemas.microsoft.com/appx/manifest/uap/windows10/5" IgnorableNamespaces="uap uap3 uap5 mp">
|
||||
<Package xmlns="http://schemas.microsoft.com/appx/manifest/foundation/windows10"
|
||||
xmlns:mp="http://schemas.microsoft.com/appx/2014/phone/manifest"
|
||||
xmlns:uap="http://schemas.microsoft.com/appx/manifest/uap/windows10"
|
||||
xmlns:uap3="http://schemas.microsoft.com/appx/manifest/uap/windows10/3"
|
||||
xmlns:uap5="http://schemas.microsoft.com/appx/manifest/uap/windows10/5"
|
||||
xmlns:desktop4="http://schemas.microsoft.com/appx/manifest/desktop/windows10/4"
|
||||
xmlns:iot2="http://schemas.microsoft.com/appx/manifest/iot/windows10/2"
|
||||
IgnorableNamespaces="uap uap3 uap5 mp desktop4 iot2">
|
||||
<Identity Name="Microsoft.WindowsCalculator" Publisher="CN=Microsoft Corporation, O=Microsoft Corporation, L=Redmond, S=Washington, C=US" Version="10.1604.27012.0" />
|
||||
<mp:PhoneIdentity PhoneProductId="b58171c6-c70c-4266-a2e8-8f9c994f4456" PhonePublisherId="95d94207-0c7c-47ed-82db-d75c81153c35" />
|
||||
<Properties>
|
||||
|
@ -14,7 +21,11 @@
|
|||
<Resource Language="x-generate" />
|
||||
</Resources>
|
||||
<Applications>
|
||||
<Application Id="App" Executable="$targetnametoken$.exe" EntryPoint="Calculator.App">
|
||||
<Application Id="App"
|
||||
Executable="$targetnametoken$.exe"
|
||||
EntryPoint="Calculator.App"
|
||||
desktop4:SupportsMultipleInstances="true"
|
||||
iot2:SupportsMultipleInstances="true">
|
||||
<uap:VisualElements DisplayName="ms-resource:AppName" Square150x150Logo="Assets\CalculatorMedTile.png" Square44x44Logo="Assets\CalculatorAppList.png" Description="ms-resource:AppDescription" BackgroundColor="#0078D4">
|
||||
<uap:DefaultTile ShortName="ms-resource:AppName" Square310x310Logo="Assets\CalculatorLargeTile.png" Wide310x150Logo="Assets\CalculatorWideTile.png" Square71x71Logo="Assets\CalculatorSmallTile.png">
|
||||
<uap:ShowNameOnTiles>
|
||||
|
|
|
@ -1,5 +1,12 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<Package xmlns="http://schemas.microsoft.com/appx/manifest/foundation/windows10" xmlns:mp="http://schemas.microsoft.com/appx/2014/phone/manifest" xmlns:uap="http://schemas.microsoft.com/appx/manifest/uap/windows10" xmlns:uap3="http://schemas.microsoft.com/appx/manifest/uap/windows10/3" xmlns:uap5="http://schemas.microsoft.com/appx/manifest/uap/windows10/5" IgnorableNamespaces="uap uap3 uap5 mp">
|
||||
<Package xmlns="http://schemas.microsoft.com/appx/manifest/foundation/windows10"
|
||||
xmlns:mp="http://schemas.microsoft.com/appx/2014/phone/manifest"
|
||||
xmlns:uap="http://schemas.microsoft.com/appx/manifest/uap/windows10"
|
||||
xmlns:uap3="http://schemas.microsoft.com/appx/manifest/uap/windows10/3"
|
||||
xmlns:uap5="http://schemas.microsoft.com/appx/manifest/uap/windows10/5"
|
||||
xmlns:desktop4="http://schemas.microsoft.com/appx/manifest/desktop/windows10/4"
|
||||
xmlns:iot2="http://schemas.microsoft.com/appx/manifest/iot/windows10/2"
|
||||
IgnorableNamespaces="uap uap3 uap5 mp desktop4 iot2">
|
||||
<Identity Name="Microsoft.WindowsCalculator.Dev" Publisher="CN=Microsoft Corporation, O=Microsoft Corporation, L=Redmond, S=Washington, C=US" Version="0.0.1.0" />
|
||||
<mp:PhoneIdentity PhoneProductId="b58171c6-c70c-4266-a2e8-8f9c994f4456" PhonePublisherId="95d94207-0c7c-47ed-82db-d75c81153c35" />
|
||||
<Properties>
|
||||
|
@ -14,7 +21,11 @@
|
|||
<Resource Language="x-generate" />
|
||||
</Resources>
|
||||
<Applications>
|
||||
<Application Id="App" Executable="$targetnametoken$.exe" EntryPoint="Calculator.App">
|
||||
<Application Id="App"
|
||||
Executable="$targetnametoken$.exe"
|
||||
EntryPoint="Calculator.App"
|
||||
desktop4:SupportsMultipleInstances="true"
|
||||
iot2:SupportsMultipleInstances="true">
|
||||
<uap:VisualElements DisplayName="ms-resource:DevAppName" Square150x150Logo="Assets\CalculatorMedTile.png" Square44x44Logo="Assets\CalculatorAppList.png" Description="ms-resource:DevAppDescription" BackgroundColor="transparent">
|
||||
<uap:DefaultTile ShortName="ms-resource:DevAppName" Square310x310Logo="Assets\CalculatorLargeTile.png" Wide310x150Logo="Assets\CalculatorWideTile.png" Square71x71Logo="Assets\CalculatorSmallTile.png">
|
||||
<uap:ShowNameOnTiles>
|
||||
|
|
|
@ -189,7 +189,6 @@ namespace CalculatorApp
|
|||
}
|
||||
}
|
||||
|
||||
|
||||
public void UpdatePanelViewState()
|
||||
{
|
||||
UpdateHistoryState();
|
||||
|
@ -771,9 +770,12 @@ namespace CalculatorApp
|
|||
}
|
||||
|
||||
private void SetChildAsMemory()
|
||||
{
|
||||
if (DockMemoryHolder.Child != GetMemory())
|
||||
{
|
||||
DockMemoryHolder.Child = GetMemory();
|
||||
}
|
||||
}
|
||||
|
||||
private void SetChildAsHistory()
|
||||
{
|
||||
|
@ -782,8 +784,11 @@ namespace CalculatorApp
|
|||
InitializeHistoryView(Model.HistoryVM);
|
||||
}
|
||||
|
||||
if (DockHistoryHolder.Child != m_historyList)
|
||||
{
|
||||
DockHistoryHolder.Child = m_historyList;
|
||||
}
|
||||
}
|
||||
|
||||
private Memory GetMemory()
|
||||
{
|
||||
|
|
|
@ -1,189 +0,0 @@
|
|||
// Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
// Licensed under the MIT License.
|
||||
|
||||
using CalculatorApp.Common;
|
||||
using CalculatorApp.ViewModel.Common;
|
||||
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Data;
|
||||
using System.Diagnostics;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
using Windows.ApplicationModel.Core;
|
||||
using Windows.UI.Core;
|
||||
using Windows.UI.ViewManagement;
|
||||
using Windows.UI.Xaml;
|
||||
using Windows.UI.Xaml.Controls;
|
||||
|
||||
namespace CalculatorApp
|
||||
{
|
||||
public sealed class WindowFrameService
|
||||
{
|
||||
public Page GetCurrentPage()
|
||||
{
|
||||
return (m_frame.Content as Page);
|
||||
}
|
||||
|
||||
public void SetNewFrame(Windows.UI.Xaml.Controls.Frame frame)
|
||||
{
|
||||
Debug.Assert(frame.BackStackDepth == 0);
|
||||
m_frame = frame;
|
||||
}
|
||||
|
||||
// createdByUs means any window that we create.
|
||||
// !createdByUs means the main window
|
||||
internal static WindowFrameService CreateNewWindowFrameService(Frame viewFrame, bool createdByUs, WeakReference parent)
|
||||
{
|
||||
Debug.Assert(CoreWindow.GetForCurrentThread() != null);
|
||||
var frameService = new WindowFrameService(viewFrame, parent);
|
||||
frameService.InitializeFrameService(createdByUs);
|
||||
return frameService;
|
||||
}
|
||||
|
||||
public CoreDispatcher GetCoreDispatcher()
|
||||
{
|
||||
return m_coreDispatcher;
|
||||
}
|
||||
|
||||
public int GetViewId()
|
||||
{
|
||||
return m_viewId;
|
||||
}
|
||||
|
||||
public void RegisterOnWindowClosingHandler(Action onWindowClosingHandler)
|
||||
{
|
||||
m_onWindowClosingHandlers.Add(onWindowClosingHandler);
|
||||
}
|
||||
|
||||
public Task HandleViewRelease()
|
||||
{
|
||||
TaskCompletionSource<object> tsource = new TaskCompletionSource<object>();
|
||||
_ = m_coreDispatcher.RunAsync(CoreDispatcherPriority.Low, () =>
|
||||
{
|
||||
KeyboardShortcutManager.OnWindowClosed(this.m_viewId);
|
||||
Window.Current.Content = null;
|
||||
this.InvokeWindowClosingHandlers();
|
||||
// This is to ensure InvokeWindowClosingHandlers is be done before RemoveWindowFromMap
|
||||
// If InvokeWindowClosingHandlers throws any exception we want it to crash the application
|
||||
// so we are OK not setting closingHandlersCompletedEvent in that case
|
||||
tsource.SetResult(new object());
|
||||
this.m_coreDispatcher.StopProcessEvents();
|
||||
Window.Current.Close();
|
||||
});
|
||||
|
||||
return tsource.Task;
|
||||
}
|
||||
|
||||
// Throws InvalidArgumentException if a service is already registered with the specified id
|
||||
public void RegisterRuntimeWindowService(Type serviceId, object service)
|
||||
{
|
||||
if (TryResolveRuntimeWindowService(serviceId) != null)
|
||||
{
|
||||
throw new DuplicateNameException(serviceId + " already registered");
|
||||
}
|
||||
|
||||
m_runtimeServicesMap[serviceId.Name] = service;
|
||||
}
|
||||
|
||||
// Returns false if no service was registered with the specified id
|
||||
public bool RemoveRuntimeWindowService(Type serviceId)
|
||||
{
|
||||
return m_runtimeServicesMap.Remove(serviceId.Name);
|
||||
}
|
||||
|
||||
// Throws InvalidArgumentException if no service is registered with the specified id
|
||||
public object ResolveRuntimeWindowService(Type serviceId)
|
||||
{
|
||||
var service = TryResolveRuntimeWindowService(serviceId);
|
||||
|
||||
if (service != null)
|
||||
{
|
||||
return service;
|
||||
}
|
||||
else
|
||||
{
|
||||
throw new EntryPointNotFoundException(serviceId.Name + " not found");
|
||||
}
|
||||
}
|
||||
|
||||
public Frame GetFrame()
|
||||
{
|
||||
return m_frame;
|
||||
}
|
||||
|
||||
public void InvokeWindowClosingHandlers()
|
||||
{
|
||||
// Should be called only once just before we kill the window.
|
||||
foreach (var handler in m_onWindowClosingHandlers)
|
||||
{
|
||||
handler();
|
||||
}
|
||||
m_onWindowClosingHandlers.Clear();
|
||||
}
|
||||
|
||||
private WindowFrameService(Frame frame, WeakReference parent)
|
||||
{
|
||||
m_currentWindow = CoreWindow.GetForCurrentThread();
|
||||
m_coreDispatcher = m_currentWindow.Dispatcher;
|
||||
m_frame = frame;
|
||||
m_parent = parent;
|
||||
m_viewId = ApplicationView.GetApplicationViewIdForWindow(m_currentWindow);
|
||||
}
|
||||
|
||||
private void InitializeFrameService(bool createdByUs)
|
||||
{
|
||||
Debug.Assert(createdByUs == (!CoreApplication.GetCurrentView().IsHosted && !CoreApplication.GetCurrentView().IsMain));
|
||||
if (createdByUs)
|
||||
{
|
||||
ApplicationView.GetForCurrentView().Consolidated += OnConsolidated;
|
||||
}
|
||||
else
|
||||
{
|
||||
CoreWindow.GetForCurrentThread().Closed += OnClosed;
|
||||
}
|
||||
}
|
||||
|
||||
private void OnConsolidated(ApplicationView sender, ApplicationViewConsolidatedEventArgs e)
|
||||
{
|
||||
TraceLogger.GetInstance().DecreaseWindowCount();
|
||||
if (m_parent.IsAlive)
|
||||
{
|
||||
var parent = m_parent.Target as App;
|
||||
parent.RemoveWindow(this);
|
||||
}
|
||||
}
|
||||
|
||||
private void OnClosed(CoreWindow sender, CoreWindowEventArgs args)
|
||||
{
|
||||
if (m_parent.IsAlive)
|
||||
{
|
||||
var parent = m_parent.Target as App;
|
||||
parent.RemoveSecondaryWindow(this);
|
||||
}
|
||||
}
|
||||
|
||||
// Returns nullptr if no service is registered with the specified id
|
||||
private object TryResolveRuntimeWindowService(Type serviceId)
|
||||
{
|
||||
if (m_runtimeServicesMap.TryGetValue(serviceId.Name, out object retval))
|
||||
{
|
||||
return retval;
|
||||
}
|
||||
else
|
||||
{
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
private readonly Windows.UI.Core.CoreWindow m_currentWindow;
|
||||
private readonly Windows.UI.Core.CoreDispatcher m_coreDispatcher;
|
||||
private Windows.UI.Xaml.Controls.Frame m_frame;
|
||||
private readonly int m_viewId;
|
||||
private readonly WeakReference m_parent;
|
||||
|
||||
private readonly Dictionary<string, object> m_runtimeServicesMap = new Dictionary<string, object>();
|
||||
private readonly List<Action> m_onWindowClosingHandlers = new List<Action>();
|
||||
}
|
||||
}
|
||||
|
Loading…
Add table
Add a link
Reference in a new issue