User interface migration (including WinUI 3)

This topic shows how to migrate your user interface (UI) code, including migrating to the Windows UI Library (WinUI) 3.

Summary of API and/or feature differences

The Window.Current property migrates to App.Window. And the CoreDispatcher.RunAsync method migrates to DispatcherQueue.TryEnqueue.

You need to set your window's handle (HWND) on a MessageDialog, and on Pickers.

To use DataTransferManager APIs, you need to associate them with your window.

For ContentDialog and Popup, you need to set their XamlRoot property.

You may need to refactor your Visual State Manager and Page.Resources XAML markup.

In the Windows App SDK, the AcrylicBrush always samples from the app content.

Change Windows.UI.Xaml.Window.Current to App.Window

This section applies if you're using the Windows.UI.Xaml.Window.Current property in your UWP app. That property isn't supported in the Windows App SDK, so this section describes how to port UWP code that uses Window.Current.

// 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 };

Your Windows App SDK app can add its own notion of a current, or main window by using a public static property on your App class.

// 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 };
...

Then, within the App class itself, you can change Window.Current to simply window. Outside of the App class, change Window.Current to App.Window, like this:

// 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, and Pickers

In your UWP app, if you use certain types from the Windows.UI.Popups or Windows.Storage.Pickers namespaces, then this section contains info to help you migrate that code. The code examples below use MessageDialog, but you can apply exactly the same techniques to displaying a picker (for example, a FileOpenPicker, a FileSavePicker, or a FolderPicker).

The steps that you have to follow in a desktop app are described in Display WinRT UI objects that depend on CoreWindow.

Note

For new apps, we recommend using the ContentDialog control instead of MessageDialog. For more info, see the ContentDialog, and Popup section below.

Here's some typical UWP code to display a MessageDialog.

// 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();

And here's the equivalent code in a Windows App SDK app.

// 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

In your UWP app, if you call the DataTransferManager.ShowShareUI method, then this section contains info to help you migrate that code.

Here's some typical UWP code that calls ShowShareUI.

// 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();

To use DataTransferManager.ShowShareUI in your Windows App SDK app, you need to associate the Share UI with your window. And that needs to be done manually. For more info, and code examples, see Display WinRT UI objects that depend on CoreWindow.

ContentDialog, and Popup

If in your UWP app you're using the Windows.UI.Xaml.Controls.ContentDialog or Windows.UI.Xaml.Controls.Primitives.Popup classes, then this section contains info to help you migrate that code. The code examples below use ContentDialog, but you can apply exactly the same techniques to displaying a Popup object.

Here's some typical UWP code to display a ContentDialog.

// 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();

In your Windows App SDK app, you just need to also set the dialog's XamlRoot property. Here's how.

// 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();

Do I need to implement page navigation?

In a UWP project, by default there will be navigation code in the methods of the App class, even if your app is simple enough that it has only one Page.

When you create a new Windows App SDK project in Visual Studio, the project template provides you with a MainWindow class (of type Microsoft.UI.Xaml.Window), but no Page. And the project template doesn't provide any navigation code.

For a Windows App SDK app that's simple enough (a single-page app), you might be able to simplify it. It might be that you needn't create pages or user controls in your Windows App SDK project—but instead copy the XAML markup and code-behind of that single page into MainWindow. However, there are some things that MainWindow doesn't support. Window isn't a DependencyObject, so capabilities such as Resources and DataContext don't exist on it. Neither do events such as Load and Unload. For more info, and workarounds, see Visual State Manager, and Page.Resources.

If on the other hand you want or need navigation between pages in your Windows App SDK app, then you can do so by migrating the App.OnLaunched and App::OnNavigationFailed methods from your UWP app. In App.OnLaunched, locate the navigation code (the code that creates rootFrame, and navigates to the first page of your app) and merge it right in between the two existing lines of code (the lines that create a window and then activate it). You'll also need to migrate the code you've copy-pasted. For a simple code example, see Page class.

Visual State Manager, and Page.Resources

Also see Do I need to implement page navigation?. If you do have a UWP app that's simple enough where you can copy your XAML markup and code-behind into MainWindow, then bear in mind these exceptions.

Your MainWindow class (of type Microsoft.UI.Xaml.Window) isn't a control, so it doesn't support Visual State Manager XAML markup and code-behind (see Tutorial: Create adaptive layouts). You have these two options, though:

  • Add a UserControl item to the project, and migrate your markup and code-behind to that. Then place an instance of that user control in MainWindow.
  • Add a Page item to the project, and migrate your markup and code-behind to that. Then add code to your App class to navigate to that Page on startup, as described in Do I need to implement page navigation?.

In addition, you won't be able to copy a <Page.Resources> element over to MainWindow and just rename it to <Window.Resources>. Instead, parent the Resources element under the root layout container (for example, a Grid) in the XAML markup for MainWindow. That will look like this:

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

AcrylicBrush.BackgroundSource property

The AcrylicBrush.BackgroundSource property exists in UWP, but not in the Windows App SDK. In the Windows App SDK, the AcrylicBrush always samples from the app content.

So if you're accessing the AcrylicBrush.BackgroundSource property in the source code of your UWP app (whether that's in XAML markup or in imperative code), then remove that code when migrating your app to the Windows App SDK. Instead, use the DesktopAcrylicController class.