在C++/WinRT的XAML Islands环境中如何获取HWND初始化FolderPicker?
解决XAML Islands中UWP UserControl获取Win32宿主HWND的问题
我刚好处理过类似的场景,给你两个可行的方案,都能解决在XAML Islands托管的UWP UserControl里获取Win32宿主HWND来初始化FolderPicker的问题:
方案一:从Win32宿主主动传递HWND给UserControl
这种方式简单直接,因为UserControl是由Win32宿主创建并托管的,宿主可以在初始化阶段把自己的HWND传递给UserControl保存起来。
步骤1:在UserControl中添加接收HWND的方法
在你的C++/WinRT UserControl类中,添加一个公共方法来接收并存储宿主的HWND(注意用intptr_t来安全传递HWND类型):
// MyUserControl.h #pragma once #include <winrt/Windows.UI.Xaml.Controls.h> namespace winrt::MyApp::implementation { struct MyUserControl : MyUserControlT<MyUserControl> { MyUserControl() = default; // 新增方法:接收宿主HWND void SetHostHwnd(intptr_t hwnd); private: HWND m_hostHwnd = nullptr; // 存储宿主HWND }; } // MyUserControl.cpp #include "pch.h" #include "MyUserControl.h" namespace winrt::MyApp::implementation { void MyUserControl::SetHostHwnd(intptr_t hwnd) { m_hostHwnd = reinterpret_cast<HWND>(hwnd); } }
步骤2:在Win32宿主中传递HWND
当宿主创建并初始化UserControl时,调用上面的方法把自己的主窗口HWND传进去:
// Win32宿主窗口代码示例 void MainWindow::InitializeXamlIslands() { // 创建DesktopWindowXamlSource并关联到宿主窗口 m_xamlSource = winrt::Windows::UI::Xaml::Hosting::DesktopWindowXamlSource(); auto interop = m_xamlSource.as<IDesktopWindowXamlSourceNative>(); interop->AttachToWindow(m_mainWindowHwnd); // 创建你的UserControl实例 auto myControl = winrt::MyApp::MyUserControl(); // 传递宿主HWND myControl.SetHostHwnd(reinterpret_cast<intptr_t>(m_mainWindowHwnd)); // 将UserControl设置为XamlSource的内容 m_xamlSource.Content(myControl); }
步骤3:在UserControl中使用HWND初始化FolderPicker
当你需要调用FolderPicker时,直接用存储的HWND初始化即可:
winrt::Windows::Storage::StorageFolder MyUserControl::PickFolder() { if (!m_hostHwnd) { // 处理HWND未初始化的情况 return nullptr; } auto picker = winrt::Windows::Storage::Pickers::FolderPicker(); picker.SuggestedStartLocation(winrt::Windows::Storage::Pickers::PickerLocationId::Desktop); picker.FileTypeFilter().Append(L"*"); // 添加需要的文件类型筛选器 // 获取IInitializeWithWindow接口并初始化 auto interop = picker.as<IInitializeWithWindow>(); HRESULT hr = interop->Initialize(m_hostHwnd); if (SUCCEEDED(hr)) { // 异步调用选择器(注意要在UI线程执行) auto folder = co_await picker.PickSingleFolderAsync(); return folder; } return nullptr; }
方案二:在UserControl内部直接获取关联的HWND
如果你不想让宿主额外传递HWND,可以通过XAML Islands的内置API,从UserControl的XamlRoot关联到对应的DesktopWindowXamlSource,进而获取宿主的HWND。这种方式更解耦,不需要宿主做额外工作。
直接在UserControl的方法中添加以下代码:
#include <winrt/Windows.UI.Xaml.Hosting.h> winrt::Windows::Storage::StorageFolder MyUserControl::PickFolder() { auto xamlRoot = this->XamlRoot(); if (!xamlRoot) { // UserControl还未加载到XAML树中,处理异常 return nullptr; } // 从XamlRoot获取DesktopWindowXamlSource auto contentIslandEnv = xamlRoot.ContentIslandEnvironment(); auto desktopSource = contentIslandEnv.as<winrt::Windows::UI::Xaml::Hosting::DesktopWindowXamlSource>(); if (!desktopSource) { return nullptr; } // 获取宿主HWND HWND hostHwnd = reinterpret_cast<HWND>(desktopSource.WindowHandle().value); // 初始化并调用FolderPicker auto picker = winrt::Windows::Storage::Pickers::FolderPicker(); picker.SuggestedStartLocation(winrt::Windows::Storage::Pickers::PickerLocationId::DocumentsLibrary); picker.FileTypeFilter().Append(L"*"); auto interop = picker.as<IInitializeWithWindow>(); if (SUCCEEDED(interop->Initialize(hostHwnd))) { auto folder = co_await picker.PickSingleFolderAsync(); return folder; } return nullptr; }
注意事项
- 无论哪种方案,调用FolderPicker都必须在UI线程执行,如果是异步操作,确保上下文正确(可以用
co_await winrt::resume_foreground(this->Dispatcher())切换到UI线程)。 - 方案二需要确保你的应用运行在Windows 10 1903(18362)及以上版本,因为
ContentIslandEnvironment和DesktopWindowXamlSource.WindowHandle是这个版本引入的API。
内容的提问来源于stack exchange,提问作者Stefan V
相关产品推荐
相关产品推荐

