显示应用的多个视图
通过让用户在单独的窗口中查看应用的独立组成部分,来帮助他们提高工作效率。 为应用创建多个窗口时,任务栏会单独显示每个窗口。 用户可以独立地移动、调整大小、显示和隐藏应用窗口,并且可以在应用窗口间切换,就像它们是单独的应用一样。
重要的 API:Windows.UI.ViewManagement 命名空间、Windows.UI.WindowManagement 命名空间
应用何时应使用多个视图?
有多种情况可从多个视图中获益。 以下是几个示例:
- 电子邮件应用,可让用户在撰写新电子邮件的同时查看收到的邮件列表
- 地址簿应用,可让用户并排比较多个人员的联系信息
- 音乐播放器应用,可让用户在观看播放内容的同时浏览其他可收听的音乐列表
- 笔记应用,可让用户将信息从笔记的一页复制到另一页
- 阅读应用,可让用户在审阅所有标题概要后,打开多篇文章供稍后阅读
尽管每个应用布局都是独特的,但我们建议在可预测的位置(例如,可在新窗口中打开的内容的右上角)包含一个“新建窗口”按钮。 另外,请考虑在“在新窗口中打开”中包含一个上下文菜单选项。
若要创建应用的单独实例(而不是为同一实例创建不同的窗口),请参阅创建多实例 Windows 应用。
窗口宿主
可以通过不同的方式在应用中承载 Windows 内容。
-
应用视图是指将线程与应用用来显示内容的窗口进行 1:1 配对。 应用启动时创建的第一个视图称为 主视图。 每个 CoreWindow/ApplicationView 在其自身的线程中运行。 如果非要使用不同的 UI 线程,多窗口应用可能会变得复杂。
应用的主视图始终承载在 ApplicationView 中。 辅助窗口中的内容可以承载在 ApplicationView 或 AppWindow 中。
若要了解如何使用 ApplicationView 在应用中显示辅助窗口,请参阅使用 ApplicationView。
-
AppWindow 简化了多窗口 Windows 应用的创建,因为它在从中创建的同一 UI 线程上运行。
从 Windows 10 版本 1903 (SDK 18362) 开始提供 AppWindow 类,以及 WindowManagement 命名空间中的其他 API。 如果应用面向早期版本的 Windows 10,则必须使用 ApplicationView 创建辅助窗口。
若要了解如何使用 AppWindow 在应用中显示辅助窗口,请参阅使用 AppWindow。
注意
AppWindow 目前以预览版提供。 这意味着,可将使用 AppWindow 的应用提交到 Store,但某些平台和框架组件已知不能与 AppWindow 配合工作(请参阅限制)。
DesktopWindowXamlSource (XAML Islands)
使用 HWND 的 Win32 应用中的 UWP XAML 内容(也称为 XAML Islands)承载在 DesktopWindowXamlSource 中。
有关 XAML Islands 的详细信息,请参阅在桌面应用程序中使用 UWP XAML 宿主 API
使代码可跨窗口宿主移植
在 CoreWindow 中显示 XAML 内容时,始终有一个关联的 ApplicationView 和 XAML Window。 可以使用这些类中的 API 来获取窗口边界等信息。 若要检索这些类的实例,请使用静态 CoreWindow.GetForCurrentThread 方法、ApplicationView.GetForCurrentView 方法或 Window.Current 属性。 此外,有许多类使用 GetForCurrentView
模式来检索类的实例,例如 DisplayInformation.GetForCurrentView。
这些 API 的工作原理是,因为 CoreWindow/ApplicationView 只有一棵 XAML 内容树,因此 XAML 知道托管的上下文是 CoreWindow/ApplicationView。
当 XAML 内容在 AppWindow 或 DesktopWindowXamlSource 中运行时,你可以获得同时在同一线程中运行的多个 XAML 内容树。 在这种情况下,这些 API 不提供正确的信息,因为内容不再在当前 CoreWindow/ApplicationView(和 XAML 窗口)中运行。
若要确保代码可在所有窗口宿主中正常工作,应将依赖于 CoreWindow、ApplicationView和 Window 的 API 替换为从 XamlRoot 类获取其上下文的新 API。 XamlRoot 类表示 XAML 内容的树及其托管上下文的相关信息,无论是 CoreWindow、AppWindow 还是 DesktopWindowXamlSource。 无论 XAML 在哪个窗口宿主中运行,此抽象层都可让你编写相同的代码。
下表显示了无法在窗口宿主中正常工作的代码、可用于替换这些代码的新可移植代码,以及一些不需要更改的 API。
如果使用了... | 请替换为... |
---|---|
CoreWindow.GetForCurrentThread().Bounds | uiElement.XamlRoot.Size |
CoreWindow.GetForCurrentThread().SizeChanged | uiElement.XamlRoot.Changed |
CoreWindow.Visible | uiElement.XamlRoot.IsHostVisible |
CoreWindow.VisibilityChanged | uiElement.XamlRoot.Changed |
CoreWindow.GetForCurrentThread().GetKeyState | 未更改。 AppWindow 和 DesktopWindowXamlSource 支持此代码。 |
CoreWindow.GetForCurrentThread().GetAsyncKeyState | 未更改。 AppWindow 和 DesktopWindowXamlSource 支持此代码。 |
Window.Current | 返回与当前 CoreWindow 密切绑定的主 XAML Window 对象。 请参阅此表格后面的“说明”。 |
Window.Current.Bounds | uiElement.XamlRoot.Size |
Window.Current.Content | UIElement root = uiElement.XamlRoot.Content |
Window.Current.Compositor | 未更改。 AppWindow 和 DesktopWindowXamlSource 支持此代码。 |
VisualTreeHelper.FindElementsInHostCoordinates 虽然 UIElement 是可选参数,但如果 UIElement 在孤岛上托管时未提供 UIElement,该方法将引发异常。 |
指定 uiElement.XamlRoot 作为 UIElement,而不是将其留空。 |
VisualTreeHelper.GetOpenPopups 在 XAML Islands 应用中,此代码会引发错误。 在 AppWindow 应用中,此代码会返回主窗口中打开的弹出窗口。 |
VisualTreeHelper.GetOpenPopupsForXamlRoot(uiElement.XamlRoot) |
FocusManager.GetFocusedElement | FocusManager.GetFocusedElement(uiElement.XamlRoot) |
contentDialog.ShowAsync() | contentDialog.XamlRoot = uiElement.XamlRoot; contentDialog.ShowAsync(); |
menuFlyout.ShowAt(null, new Point(10, 10)); | menuFlyout.XamlRoot = uiElement.XamlRoot; menuFlyout.ShowAt(null, new Point(10, 10)); |
注意
对于 DesktopWindowXamlSource 中的 XAML 内容,线程上存在一个 CoreWindow/Window,但此对象始终不可见且大小为 1x1。 它仍可供应用访问,但不会返回有意义的边界或可见性。
对于 AppWindow 中的 XAML 内容,同一线程上始终只有一个 CoreWindow。 如果调用 GetForCurrentView
或 GetForCurrentThread
API,该 API 会返回一个对象,该对象反映线程上的 CoreWindow 状态,但不反映该线程上可能正在运行的任何 AppWindow 的状态。
应做事项和禁止事项
- 务必利用“打开新窗口”字形为辅助视图提供清晰的入口点。
- 务必向用户传达辅助视图的用途。
- 务必确保应用在单个视图中完全正常运行,用户打开辅助视图只是出于方便。
- 不依赖辅助视图提供通知或其他暂时性的视觉内容。