用户界面迁移(包括 WinUI 3)

本主题介绍如何迁移用户界面 (UI) 代码,包括迁移到 WinUI 3

API 和/或功能差异摘要

Window.Current 属性会迁移到 App.Window。 并且 CoreDispatcher.RunAsync 方法也会迁移到 DispatcherQueue.TryEnqueue

需要在 MessageDialog 和选取器上设置窗口的句柄 (HWND)

要使用 DataTransferManager API,需要将它们与窗口进行关联

对于 ContentDialog弹出窗口,需要设置其 XamlRoot 属性。

可能需要重构视觉状态管理器和 Page.Resources XAML 标记

在 Windows App SDK 中,AcrylicBrush 始终从应用内容中采样。

更改 Windows.UI.Xaml.Window.Current to App.Window

如果 UWP 应用中使用的是 Windows.UI.Xaml.Window.Current 属性,则本部分适用。 Windows App SDK 中不支持此属性,因此本部分介绍如何移植使用 Window.Current 的 UWP 代码

// MainPage.xaml.cs in a UWP app
var width = Window.Current.Bounds.Width;
// MainPage.xaml.cpp in a UWP app
auto width{ Window::Current().Bounds().Width };

Windows App SDK 应用可以通过在 App 类上使用公共静态属性来添加自己的当前窗口的概念。

// App.xaml.cs in a Windows App SDK app
public partial class App : Application
{
    ...
    public static Window Window { get { return m_window; } }
    private static Window m_window;
}
// App.xaml.h in a Windows App SDK app
...
struct App : AppT<App>
{
    ...
    static winrt::Microsoft::UI::Xaml::Window Window(){ return window; };

private:
    static winrt::Microsoft::UI::Xaml::Window window;
};
...

// App.xaml.cpp
...
winrt::Microsoft::UI::Xaml::Window App::window{ nullptr };
...

然后,在 App 类本身中,只需将 Window.Current 更改为 window。 在 App 类之外,将 Window.Current 更改为 App.Window,如下所示:

// MainPage.xaml.cs in a UWP app
var width = App.Window.Bounds.Width;
// MainPage.xaml.cpp in a UWP app
#include <App.xaml.h>
auto width{ App::Window().Bounds().Width };

MessageDialog 和 Pickers

在 UWP 应用中,如果使用来自 Windows.UI.PopupsWindows.Storage.Pickers 命名空间的特定类型,则此部分包含有助于迁移该代码的信息。 下面的代码示例使用 MessageDialog,但可以应用完全相同的技术来显示选取器(例如 FileOpenPickerFileSavePickerFolderPicker)。

显示依赖于 CoreWindow 的 WinRT UI 对象中描述了在桌面应用程序中必须遵循的步骤。

注意

对于新应用,建议使用 ContentDialog 控件而不是 MessageDialog。 有关详细信息,请参阅下面的 ContentDialog 和 Popup 部分。

下面是显示 MessageDialog 的一些典型 UWP 代码

// In a UWP app
var showDialog = new Windows.UI.Popups.MessageDialog("Message here");
await showDialog.ShowAsync();
// In a UWP app
auto showDialog{ Windows::UI::Popups::MessageDialog(L"Message here") };
co_await showDialog.ShowAsync();

下面是 Windows App SDK 应用中的相应代码。

// MainWindow.xaml.cs in a WinUI 3 app
var showDialog = new Windows.UI.Popups.MessageDialog("Message here");
WinRT.Interop.InitializeWithWindow.Initialize(showDialog,
    WinRT.Interop.WindowNative.GetWindowHandle(this));
await showDialog.ShowAsync();
// pch.h in a WinUI 3 app
...
#include <Shobjidl.h>
#include <microsoft.ui.xaml.window.h>
#include <winrt/Windows.UI.Popups.h>
...

// MainWindow.xaml.cpp
...
auto showDialog{ Windows::UI::Popups::MessageDialog(L"Message here") };

auto windowNative{ this->m_inner.as<::IWindowNative>() };
HWND hWnd{ 0 };
windowNative->get_WindowHandle(&hWnd);
showDialog.as<::IInitializeWithWindow>()->Initialize(hWnd);

co_await showDialog.ShowAsync();

DataTransferManager

在 UWP 应用中,如果调用 DataTransferManager.ShowShareUI 方法,则本部分包含有助于迁移该代码的信息。

下面是一些调用 ShowShareUI 的典型 UWP 代码

// In a UWP app
var dataTransferManager = Windows.ApplicationModel.DataTransfer.DataTransferManager.GetForCurrentView();

dataTransferManager.DataRequested += (sender, args) =>
{
    args.Request.Data.Properties.Title = "In a UWP app...";
    args.Request.Data.SetText("...display the user interface for sharing content with another app.");
    args.Request.Data.RequestedOperation =
        Windows.ApplicationModel.DataTransfer.DataPackageOperation.Copy;
};

Windows.ApplicationModel.DataTransfer.DataTransferManager.ShowShareUI();
// In a UWP app
#include <winrt/Windows.ApplicationModel.DataTransfer.h>
...
auto dataTransferManager{ Windows::ApplicationModel::DataTransfer::DataTransferManager::GetForCurrentView() };

dataTransferManager.DataRequested([](Windows::ApplicationModel::DataTransfer::DataTransferManager const& /* sender */,
    Windows::ApplicationModel::DataTransfer::DataRequestedEventArgs const& args)
    {
        args.Request().Data().Properties().Title(L"In a UWP app...");
        args.Request().Data().SetText(L"...display the user interface for sharing content with another app.");
        args.Request().Data().RequestedOperation(Windows::ApplicationModel::DataTransfer::DataPackageOperation::Copy);
    });

Windows::ApplicationModel::DataTransfer::DataTransferManager::ShowShareUI();

要在 Windows App SDK 应用中使用 DataTransferManager.ShowShareUI,需要将共享 UI 与窗口关联。 这需要手动完成。 有关详细信息和代码示例,请参阅显示依赖于 CoreWindow 的 WINRT UI 对象

ContentDialog 和 Popup

如果在 UWP 应用中使用 Windows.UI.Xaml.Controls.ContentDialogWindows.UI.Xaml.Controls.Primitives.Popup 类,本部分包含有助于迁移该代码的信息。 下面的代码示例使用 ContentDialog,但可以应用完全相同的技术来显示 Popup 对象

下面是显示 ContentDialog 的一些典型 UWP 代码

// MainPage.xaml.cs in a UWP app
var unsupportedFilesDialog = new ContentDialog();
// Set Title, Content, etc.
await unsupportedFilesDialog.ShowAsync();
// MainPage.xaml.cpp in a UWP app
ContentDialog unsupportedFilesDialog{};
// Set Title, Content, etc.
co_await unsupportedFilesDialog.ShowAsync();

在 Windows App SDK 应用中,也只需设置对话框的 XamlRoot 属性。 操作方法如下。

// MainPage.xaml.cs in a Windows App SDK app
var unsupportedFilesDialog = new ContentDialog();
// Set Title, Content, etc.
unsupportedFilesDialog.XamlRoot = this.Content.XamlRoot;
await unsupportedFilesDialog.ShowAsync();
// MainPage.xaml.cpp in a Windows App SDK app
ContentDialog unsupportedFilesDialog{};
// Set Title, Content, etc.
unsupportedFilesDialog.XamlRoot(this->Content().XamlRoot());
co_await unsupportedFilesDialog.ShowAsync();

是否需要实现页面导航?

在 UWP 项目中,默认情况下,App 类的方法中将包含导航代码,即使应用简单到只有一个页面

在 Visual Studio 中创建新 Windows App SDK 项目时,项目模板会提供 MainWindow 类(属于 Microsoft.UI.Xaml.Window),但是未提供 Page。 并且项目模板未提供任何导航代码。

对于足够简单的 Windows App SDK 应用(单页应用),也许可以对它进行简化。 可能不需要在 Windows App SDK 项目中创建页面或用户控件,而是将该单页的 XAML 标记和后置代码复制到 MainWindow 中。 但是,MainWindow 不支持某些内容。 窗口不是 DependencyObject,因此其上不存在 Resources 和 DataContext 等功能。 加载和卸载等事件也不具有这些功能。 有关详细信息和规避方法,请参阅视觉状态管理器和 Page.Resources

另一方面,如果希望或需要在 Windows App SDK 应用中进行页面间导航,则可以通过迁移 UWP 应用中的 App.OnLaunchedApp::OnNavigationFailed 方法来实现这一目的。 在 App.OnLaunched 中,找到导航代码(创建 rootFrame 的代码,并导航到应用的第一页)并直接将其合并到两个现有代码行(创建窗口的行,然后激活它)之间。 还需要迁移复制粘贴的代码。 有关简单的代码示例,请参阅 Page 类

视觉状态管理器和 Page.Resources

另请参阅是否需要实现页面导航?。 如果 UWP 应用简单到可以在其中将 XAML 标记和后置代码复制到 MainWindow 中,请注意这些异常

MainWindow 类(类型为 Microsoft.UI.Xaml.Window)不是控件,因此它不支持视觉状态管理器 XAML 标记和后置代码(请参阅教程:创建自适应布局)。 不过,有以下两个选项可供选择:

  • 将 UserControl 项添加到项目,并迁移标记和后置代码。 然后将该用户控件的实例放在 MainWindow 中
  • 将 Page 项添加到项目,并迁移标记和后置代码。 然后,将代码添加到 App 类以导航到启动时的该,如是否需要实现页导航?中所述。

此外,不能将 <Page.Resources> 元素复制到 MainWindow 并简单地将其重命名为 <Window.Resources>。 相反,请将 MainWindow 的 XAML 标记中的根布局容器(例如网格)下的“资源”元素作为父级。 效果如下所示:

<Window ...>
    <Grid>
        <Grid.Resources>...</Grid.Resources>
        ...
    </Grid>
</Window>

AcrylicBrush.BackgroundSource 属性

AcrylicBrush.BackgroundSource 属性存在于 UWP 中,但不存在于 Windows App SDK 中。 在 Windows App SDK 中,AcrylicBrush 始终从应用内容中采样。

因此,如果要访问 UWP 应用源代码中的 AcrylicBrush.BackgroundSource 属性(无论是在 XAML 标记中还是命令性代码中),在将应用迁移到 Windows App SDK 时,请删除该代码。 请改为使用 DesktopAcrylicController 类。