UWP 支持一个 App 打开多个窗口,使得 App 更像传统的桌面应用,使用更加灵活,但是由于文档提及的内容实在太少,于是自己摸索了一阵,遇到了很多问题,也总结了一些经验。
UWP API 中,关于一个窗口的一共有四个类,分别是:
Window
class,表示一个 Windows 下可见的窗口,负责 host 窗口内容CoreWindow
class,负责接收和翻译 Windows 窗口消息并在CoreApplicationView.Dispatcher
上 dispatch(类似 WindowProc)ApplicationView
class,表示窗口的状态,比如大小和全屏状态CoreApplicationView
class,抽象的窗口,可以 host 在各种父窗口里,负责 dispatch 窗口消息和事件。如果是启动的第一个 View(MainView),App 的启动代码也运行在此。
然后是关闭窗口时的操作:
当用户对着你的 App 的一个窗口,也就是一个 Window
,按下右上角的 X 的时候,实际被关闭的是关联的 CoreApplicationView
,换句话说,窗口被“关闭”之后,它的 Content
还在继续运行。对于单窗口的 App,最后一个 View 被关闭的时候 App 就退出了;但对于多窗口的 App,必须自行维护窗口,一般用户不会注意到,但是会造成内存占用,正在播放的媒体也会继续播放。
解决方法:在 ApplicationView
被关闭时把关联的 Window.Content
设置成 null
。**不要使用 Window.Current.Close()
**,首先 App 的主要代码运行在启动的第一个 Window
里(其实是 CoreApplicationView
里),所以这个 Window
是无法 Close
的(会丢出异常);而对于其它 App 自己打开的 CoreApplicationView
,Close()
会导致 Dispatcher
立即被终结,任何排队的操作都会丢出异常,对于一个复杂的程序,特别是调用了第三方 UI 控件的 App 来说,很可能导致崩溃。
如何创建一个新窗口
关键 API:
CoreApplicationView CoreApplication.CreateNewView()
创建一个新的 ViewIAsyncAction CoreApplicationView.Dispatcher.RunAsync(CoreDispatcherPriority, DispatchedHandler)
在新的 View 的 Dispatcher 上执行代码,这里需要对新窗口设置内容(任何UIElement
都可以,比如Frame
和Page
,甚至是UserControl
),然后Window.Current.Activate()
(必须!否则无法显示内容),然后获得ApplicationView.Id
IAsyncOperation<bool> ApplicationViewSwitcher.TryShowAsStandaloneAsync(int)
把上面的ApplicationView.Id
对应的ApplicationView
作为独立窗口显示
组合起来:
1 | public static async void CreateNewViewAsync(Action initialize) |
如何实现像计算器一样每次启动都是新窗口
启动时显示新窗口并不像上面一样使用 ApplicationViewSwitcher.TryShowAsStandaloneAsync
,而是需要使用 OnLaunched()
里 LaunchActivatedEventArgs
的 ViewSwitcher
。
正常启动时这个属性总是为 null
,第一次必须以正常方式启动,并且调用 ApplicationViewSwitcher.DisableSystemViewActivationPolicy()
声明接下来的启动会由 App 自行维护窗口激活逻辑,之后的启动时就会提供一个 ActivationViewSwitcher
用于显示新的窗口。
1 | protected override void OnLaunched(LaunchActivatedEventArgs e) |
上面的代码还包括一个设置 StatusBar.BackgroundOpacity
的部分,是为了解决 Win10 Mobile 上 StatusBar 全黑或全白的 bug(需要给 project 添加 Mobile Extension 的 Reference)。原因是系统在启动 App 的时候将这个属性设成了 0,这样 SplashScreen 的时候 StatusBar 就是透明的,然而 App 完成启动后并没有自动改回来。至今这个 bug 也没有修好。