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

本主题说明如何迁移用户界面 (UI) 代码,包括迁移到 Windows UI 库 (WinUI) 3

API 和/或功能差异摘要

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

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

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

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

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

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

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

如果 UWP 应用中使用的是 Windows.UI.Xaml.Window.Current 属性,则本部分适用。 Windows 应用 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 应用 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)。

桌面应用中必须遵循的步骤在 显示 WinRT UI 对象中介绍,这些对象依赖于 CoreWindow

注意

对于新应用,建议使用 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 应用 SDK应用中的等效代码。

// In App.xaml.cs in a Windows App SDK app
...
using System.Runtime.InteropServices;
using WinRT;
...
public partial class App : Application
{
    ...
    protected override void OnLaunched(Microsoft.UI.Xaml.LaunchActivatedEventArgs args)
    {
        m_window = new MainWindow();
        m_window.Activate();
        WindowHandle = WinRT.Interop.WindowNative.GetWindowHandle(m_window);
    }

    public static IntPtr WindowHandle { get; private set; }
    private Window m_window;
}

// MainWindow.xaml.cs
...
using System.Runtime.InteropServices;
using WinRT;
...
public sealed partial class MainWindow : Window
{
    ...
    private static void SetOwnerWindow(object dialog)
    {
        WinRT.Interop.InitializeWithWindow.Initialize(dialog, App.WindowHandle);
    }

    ...
    var showDialog = new Windows.UI.Popups.MessageDialog("Message here");
    SetOwnerWindow(showDialog);
    await showDialog.ShowAsync();
}
// pch.h in a Windows App SDK app
...
#include <Shobjidl.h>
#include <microsoft.ui.xaml.window.h>
#include <winrt/Windows.UI.Popups.h>
...

// App.xaml.h
...
struct App : AppT<App>
{
    ...
    static HWND WindowHandle() { return m_hWnd; }

private:
    static HWND m_hWnd;
};
...

// App.xaml.cpp
...
HWND App::m_hWnd{ 0 };
...
void App::OnLaunched(LaunchActivatedEventArgs const&)
{
    window = make<MainWindow>();
    window.Activate();

    auto windowNative{ window.as<::IWindowNative>() };
    HWND hWnd{ 0 };
    windowNative->get_WindowHandle(&hWnd);
    App::m_hWnd = hWnd;
}
...

// MainWindow.xaml.h
...
#include <App.xaml.h>
...
struct MainWindow : MainWindowT<MainWindow>
{
  ...
  private:
    static void SetOwnerWindow(Windows::Foundation::IInspectable const&);
};
...

// MainWindow.xaml.cpp
...
void MainWindow::SetOwnerWindow(IInspectable const& dialog)
{
    auto initializeWithWindow{ dialog.as<::IInitializeWithWindow>() };
    initializeWithWindow->Initialize(App::WindowHandle());
}

...
auto showDialog{ Windows::UI::Popups::MessageDialog(L"Message here") };
SetOwnerWindow(showDialog);
co_await showDialog.ShowAsync();

DataTransferManager

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

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

// In a UWP app
Windows.ApplicationModel.DataTransfer.DataTransferManager.ShowShareUI();
// In a UWP app
Windows::ApplicationModel::DataTransfer::DataTransferManager::ShowShareUI();

若要在 Windows 应用 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 应用 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 应用 SDK 项目时,项目模板会提供 MainWindow 类(类型为 Microsoft.UI.Xaml.Window),但是未提供 Page。 并且项目模板未提供任何导航代码。

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

如果另一方面需要或需要在Windows 应用 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 应用 SDK 中。 在 Windows 应用 SDK 中,AcrylicBrush 始终从应用内容中采样。

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