Bewerken

Delen via


Display WinRT UI objects that depend on CoreWindow

Certain pickers, popups, dialogs, and other Windows Runtime (WinRT) objects depend on a CoreWindow; typically to display a user-interface (UI). Even though CoreWindow isn't supported in desktop apps (see Core unsupported classes), you can still use many of those WinRT classes in your desktop app by adding a little bit of interoperation code.

Your desktop app can be WinUI 3, Windows Presentation Foundation (WPF), or Windows Forms (WinForms) apps. Code examples are presented in C# and C++/WinRT.

Set the owner window handle (HWND) for a WinRT UI object

For classes that implement the IInitializeWithWindow interface (or the equivalent IDataTransferManagerInterop interface), you can use that interface to set an owner window on the object before you display it. It's a two-step process.

  1. Decide which window will be the owner of the UI object that you want to display, and retrieve that window's HWND. For more details and code examples for this step, see Retrieve a window handle (HWND).
  2. Then call the appropriate interoperatability API (for C# or C++/WinRT) to set an owner window handle (HWND) for the WinRT UI object.

For classes that implement IInitializeWithWindow

These classes implement IInitializeWithWindow:

Note

The list above is necessarily incomplete—refer to a type's documentation to see whether it implements IInitializeWithWindow (or an equivalent interop interface).

The next sections contain code examples to display a FolderPicker. But it's the same technique to display any of the APIs listed above.

WinUI 3 with C# (also WPF/WinForms with .NET 6 or later)

Note

The code examples in this section use the WinRT.Interop.WindowNative C# interop class. If you target .NET 6 or later, then you can use that class in a WPF or WinForms project. For info about setting up your project to do that, see Call interop APIs from a .NET app.

The C# code below expects that you've already used the pattern documented in Retrieve a window handle (HWND). Then, to set the owner window for the UI object that you want to display, the code calls the Initialize method on the WinRT.Interop.InitializeWithWindow C# interop class. For more info about the C# interop classes, see Call interop APIs from a .NET app.

// MainWindow.xaml.cs
private async void ShowFolderPickerAsync(IntPtr hWnd)
{
    // Create a folder picker.
    var folderPicker = new Windows.Storage.Pickers.FolderPicker();

    // Initialize the folder picker with the window handle (HWND).
    WinRT.Interop.InitializeWithWindow.Initialize(folderPicker, hWnd);

    // Use the folder picker as usual.
    folderPicker.FileTypeFilter.Add("*");
    var folder = await folderPicker.PickSingleFolderAsync();
}

WinUI 3 with C++

The C++/WinRT code below expects that you've already used the pattern documented in Retrieve a window handle (HWND). Then, to set the owner window for the UI object that you want to display, the code calls the interoperatability method IInitializeWithWindow::Initialize.

// pch.h
...
#include <microsoft.ui.xaml.window.h>
#include <Shobjidl.h>
#include <winrt/Windows.Storage.Pickers.h>

// MainWindow.xaml.cpp
winrt::fire_and_forget ShowFolderPickerAsync(HWND hWnd)
{
    // Create a folder picker.
    Windows::Storage::Pickers::FolderPicker folderPicker;

    // Initialize the folder picker with the window handle (HWND).
    auto initializeWithWindow{ folderPicker.as<::IInitializeWithWindow>() };
    initializeWithWindow->Initialize(hWnd);

    // Use the folder picker as usual.
    folderPicker.FileTypeFilter().Append(L"*");
    auto folder{ co_await folderPicker.PickSingleFolderAsync() };
}

For classes that implement IDataTransferManagerInterop

The Windows.ApplicationModel.DataTransfer.DataTransferManager class implements the IDataTransferManagerInterop interface (which, like IInitializeWithWindow, lets you set an owner window).

In a desktop app, instead of calling the DataTransferManager.ShowShareUI method, you call IDataTransferManagerInterop::ShowShareUIForWindow, as shown in the code examples below.

WinUI 3 with C# (also WPF/WinForms with .NET 6 or later)

// MainWindow.xaml.cs
...
public sealed partial class MainWindow : Window
{
    ...

    [System.Runtime.InteropServices.ComImport]
    [System.Runtime.InteropServices.Guid("3A3DCD6C-3EAB-43DC-BCDE-45671CE800C8")]
    [System.Runtime.InteropServices.InterfaceType(
        System.Runtime.InteropServices.ComInterfaceType.InterfaceIsIUnknown)]
    interface IDataTransferManagerInterop
    {
        IntPtr GetForWindow([System.Runtime.InteropServices.In] IntPtr appWindow,
            [System.Runtime.InteropServices.In] ref Guid riid);
        void ShowShareUIForWindow(IntPtr appWindow);
    }

    static readonly Guid _dtm_iid = 
        new Guid(0xa5caee9b, 0x8708, 0x49d1, 0x8d, 0x36, 0x67, 0xd2, 0x5a, 0x8d, 0xa0, 0x0c);

    private void myButton_Click(object sender, RoutedEventArgs e)
    {
        // Retrieve the window handle (HWND) of the current WinUI 3 window.
        var hWnd = WinRT.Interop.WindowNative.GetWindowHandle(this);

        IDataTransferManagerInterop interop =
        Windows.ApplicationModel.DataTransfer.DataTransferManager.As
            <IDataTransferManagerInterop>();

        IntPtr result = interop.GetForWindow(hWnd, _dtm_iid);
        var dataTransferManager = WinRT.MarshalInterface
            <Windows.ApplicationModel.DataTransfer.DataTransferManager>.FromAbi(result);

        dataTransferManager.DataRequested += (sender, args) =>
        {
            args.Request.Data.Properties.Title = "In a desktop app...";
            args.Request.Data.SetText("...display WinRT UI objects that depend on CoreWindow.");
            args.Request.Data.RequestedOperation = 
                Windows.ApplicationModel.DataTransfer.DataPackageOperation.Copy;
        };

        // Show the Share UI
        interop.ShowShareUIForWindow(hWnd);
    }
}
...

WinUI 3 with C++

// pch.h in a Windows App SDK app
...
#include <shobjidl_core.h>
#include <microsoft.ui.xaml.window.h>
#include <winrt/Windows.ApplicationModel.DataTransfer.h>
...

// MainWindow.xaml.cpp
...
void MainWindow::myButton_Click(IInspectable const&, RoutedEventArgs const&)
{
    // Retrieve the window handle (HWND) of the current WinUI 3 window.
    auto windowNative{ this->m_inner.as<::IWindowNative>() };
    HWND hWnd{ 0 };
    windowNative->get_WindowHandle(&hWnd);

    winrt::com_ptr<IDataTransferManagerInterop> interop = 
        winrt::get_activation_factory<Windows::ApplicationModel::DataTransfer::DataTransferManager,
        IDataTransferManagerInterop>();

    winrt::guid _dtm_iid{ 0xa5caee9b, 0x8708, 0x49d1, { 0x8d, 0x36, 0x67, 0xd2, 0x5a, 0x8d, 0xa0, 0x0c } };
    Windows::ApplicationModel::DataTransfer::DataTransferManager dataTransferManager{ nullptr };
    interop->GetForWindow(hWnd, _dtm_iid, winrt::put_abi(dataTransferManager));

    dataTransferManager.DataRequested([](Windows::ApplicationModel::DataTransfer::DataTransferManager const& /* sender */,
        Windows::ApplicationModel::DataTransfer::DataRequestedEventArgs const& args)
    {
        args.Request().Data().Properties().Title(L"In a desktop app...");
        args.Request().Data().SetText(L"...display WinRT UI objects that depend on CoreWindow.");
        args.Request().Data().RequestedOperation(Windows::ApplicationModel::DataTransfer::DataPackageOperation::Copy);
    });

    interop->ShowShareUIForWindow(hWnd);
}
...

For classes that implement IUserConsentVerifierInterop

The Windows.Security.Credentials.UI.UserConsentVerifier class implements the IUserConsentVerifierInterop interface (which, like IInitializeWithWindow, lets you set an owner window).

In a desktop app, instead of calling the UserConsentVerifier.RequestVerificationAsync method:

For more info, and code examples, see UserConsentVerifier.

For classes that implement other interop interfaces

These interfaces have XxxForWindow methods, which let you set an owner window handle (HWND). You can use these interfaces directly from C++/WinRT. Versions of the interfaces also exist in the form of C# classes—for more details, see Call interop APIs from a .NET app.