UWPLib: 将 XAML 控件包含在普通 Win32 中





5.00/5 (15投票s)
一种快速将 UWP 控件添加到 Windows 10 的普通 Win32 应用程序中的方法
引言
微软不断发明新的框架,但最终还是纯粹的 Win32 胜出。这里提供一种方法,基于新的 C++ 17/WinRT API,将任何 UWP 控件封装到纯 C++ Win32 应用程序中。
您需要 VS 2017+,Windows 10 build 1903 或更高版本,最新的 Windows SDK (17763+) 并链接到 "windowsapp.lib" (上述页面中讨论的预编译头文件无关)。
UWP
UWP 为我们提供了一些不错的控件,可以在 这里 找到。我们首先需要初始化 winrt
,然后 WindowsXamlManager。
#include <winrt/base.h>
#include <atlbase.h>
#include <winrt/Windows.system.h>
#include <winrt/Windows.UI.Core.h>
#include <winrt/Windows.UI.Input.Inking.h>
#include <winrt/Windows.UI.Text.h>
#include <winrt/Windows.Foundation.h>
#include <winrt/Windows.Foundation.Collections.h>
#include <winrt/Windows.UI.Xaml.Hosting.h>
#include <winrt/Windows.UI.Xaml.Controls.h>
#include <winrt/Windows.UI.Xaml.Controls.Primitives.h>
#include <winrt/Windows.UI.Xaml.Data.h>
#include <winrt/Windows.UI.Xaml.Media.h>
#include <windows.ui.xaml.hosting.desktopwindowxamlsource.h>
#include <winrt/Windows.UI.Xaml.Markup.h>
#pragma comment(lib, "windowsapp")
using namespace winrt;
using namespace Windows::Foundation;
using namespace Windows::Foundation::Collections;
using namespace Windows::Foundation::Numerics;
using namespace Windows::UI;
using namespace Windows::UI::Composition;
using namespace Windows::UI::Core;
using namespace Windows::UI::Text;
using namespace Windows::UI::Input::Inking;
using namespace Windows::UI::Xaml;
using namespace Windows::UI::Xaml::Data;
using namespace Windows::UI::Xaml::Hosting;
using namespace Windows::UI::Xaml::Controls;
using namespace Windows::Media::Core;
using namespace Windows::UI::Xaml::Markup;
winrt::init_apartment(apartment_type::single_threaded);
WindowsXamlManager windowsXamlManager = WindowsXamlManager::InitializeForCurrentThread();
这些操作由 DesktopWindowXamlSource
作为互操作接口提供。
DesktopWindowXamlSource xs;
auto interopDetail = xs.as<IDesktopWindowXamlSourceNative>();
interopDetail->AttachToWindow(hh);
interopDetail->get_WindowHandle(&s->hwndDetailXamlIsland);
winrt::param::hstring str(LR"(
<Grid Name="MainGrid" xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml">
<CalendarView />
</Grid>)");
winrt::Windows::Foundation::IInspectable ins = XamlReader::Load(str);
xs.Content(ins.as<UIElement>());
所有这些实际上都是纯粹的 COM。as()
类似于模板形式的旧 QueryInterface
。我们将控件附加到我们自己的 HWND
,并且我们还获得一个由框架创建的“内部”HWND
。要创建控件,我们使用 XAML、XML 或 HTML 类似的控件配置。在这种情况下,我们在网格中指定一个日历。我们需要一个 hstring
(一个 winrt string
)。我们可以使用 winrt::param::hstring
包装器 (类似于 _bstr_t
)。最后,我们将 string
加载到 IInspectable
(类似于 IUnknown
的基本接口) 使用 XamlReader,然后将其加载到 DesktopWindowXamlSource
。
要与控件交互,我们可以使用 as()
获取类型。例如,如果我加载一个按钮
<Button xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" Name="BX">BBBB</Button>
我们可以对其进行操作
Button butt = ins.as<Button>();
butt.Content(box_value(L"Hi"));
我在 MSDN 的 Button 中找到了该类,因此您可以使用任何“成员函数”。
要获取按钮的事件,我们可以使用 WinRT 事件处理 以及一些不错的 lambda 表达式。
butt.Click([](const IInspectable& sender, const RoutedEventArgs& rea)
{
sender.as<winrt::Windows::UI::Xaml::Controls::Button>().Content
(box_value(L"Clicked"));
});
每个“回调”都将 IInspectable
引用作为第一个参数。其余参数可以在文档中找到,例如,Click()
接受一个 RouterEventArgs
参数。
UWPLib
UWPLib
是一个项目,用于将 UWP 控件表示为带有消息和通知的 Win32 控件。
- 使用附带的清单创建应用程序
- 调用
winrt::init_apartment(apartment_type::single_threaded)
- 调用
WindowsXamlManager::InitializeForCurrentThread()
- 调用函数
Register()
之后,您可以使用 UWP_Custom
类创建 UWP 窗口,然后使用 WM_SETTEXT
加载标记。这被我的新 TurboTransfer 应用程序使用
auto pv = LR"(<Pivot xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" >
<PivotItem Header="Items">
<ListView Name="GridItems"/>
</PivotItem>
<PivotItem Header="Transfers">
<ListView Name="TransferItems"/>
</PivotItem>
<PivotItem Header="Add">
<StackPanel>
<Button x:Name="btn1" Content="Add Files" Margin="5" Width="150" />
<Button x:Name="btn2" Content="Add Directory" Margin="5" Width="150" />
</StackPanel>
</PivotItem>
<PivotItem Header="Upload">
<StackPanel>
</StackPanel>
</PivotItem>
<PivotItem Header="Configuration">
<StackPanel>
<TextBox Name="portnum" Margin="5" Header="Port Number" Text="7001"/>
<CheckBox Name="CB_RightClick" Content="Enable right click" />
<CheckBox Name="CB_ForceOctetStream"
Content="Force MIME application/octet-stream" />
<TextBox x:Name="ip" Margin="5" Header="IP or Hostname (Empty = default) " />
</StackPanel>
</PivotItem>
</Pivot>)";
SetWindowText(GetDlgItem(hh, 901), pv);
UWPLIB::UWPCONTROL* u = (UWPLIB::UWPCONTROL*)SendDlgItemMessage
(hh, 901, UWPM_GET_CONTROL, 0, 0);
pivot = u->ins.as<Pivot>();
项目
该项目使用了此处讨论的编程方面来创建一些 UWP 控件,如上所示。玩得开心。我将其用于我的大型 FluentTorrent。
此外,还有一个 DLL 和 Win32 项目,演示了在纯 Win32 应用程序中使用 UWP 菜单,并提供了一个可以将 HMENU 转换为 UWP 菜单的函数。
历史
- 2020 年 2 月 4 日:添加了更多屏幕截图和控件,添加了 DLL + Win32 应用程序
- 2019 年 3 月 20 日:转换为单个 lib 文件
- 2019 年 3 月 18 日:添加了 UWP 库
- 2019 年 3 月 14 日:首次发布