C++/WinUI 3 技術筆記:(一)創建第一個 WinUI 3 項目


微軟在 Windows 10 Version 1809 上正式發布了新的 UI 框架,命名為 WinUI 3。

這已經是微軟發布的第不知道多少個 UI 框架了,但是微軟宣稱它將支持原生 C++ 和 Win32 應用。這引起了我的注意,因為微軟已經很久沒有為 Win32 提供新的技術了。

WinUI 3 與 Win32、UWP

按微軟的說法,WinUI 3 是同時為 Win32 和 UWP 程序提供支持的,也就是說它應該允許獨立運行在 Win32 框架上,不受 UWP 的權限管理限制。

對於 C++ 開發者,WinUI 3 借助 C++/WinRT 有完全的原生 C++ 支持,而不需要 C++/CX 或 C++/CLI 這樣劍走偏鋒的設計。這無疑對 GCC 或 Clang 上編譯 WinUI 3 留下了可能。作為開發者,着實不希望微軟帶領技術走向分裂。

對於 UI 設計,WinUI 3 繼承了 UWP 程序的 XAML 技術,為用戶提供了 Fluent 風格的控件和交互體驗。也就是說在核心的 UI 開發方式上,還是和 UWP 保持一致的,只是控件風格有所改變。但是 WinUI 3 不受 UWP 復雜的權限約束限制,可以說對 Win32 開發者十分友好了。

創建一個新 WinUI 3 項目

開發環境

開發前按照微軟文檔配置安裝環境。

Install tools for developing apps for Windows 10 and Windows 11

基本上要在最新版本的 Windows 系統和 Visual Studio 上才支持。

創建工程

接下來按照微軟文檔創建第一個 WinUI 3 項目。

Create your first WinUI 3 project

先安裝 WinUI 3 工程模板,使用模板創建工程。

WinUI 3 project templates in Visual Studio

微軟文檔主要支持 C# 和 C++ 兩種開發語言,這里以 C++/WinUI 3 為例。

微軟提供了兩種空應用程序模板,一種使用 Windows Application Packaging(WAP)打包,另一種使用 MSIX 打包。與常見的 msi、exe 文件的安裝包不同,這兩種打包方式大多只會在 Windows 應用商店中使用。

其實這里微軟也允許創建不被打包的傳統程序,這需要用戶自行部署依賴環境,這里暫不討論。

這里先使用微軟推薦的新版 MSIX 打包。

核心代碼和界面

工程創建后,資源管理器中有不少文件,這里先關注兩個重要的 xaml 項。

App.xaml 描述了 Windows 應用的基本屬性。

<!-- on App.xaml -->
<Application
    x:Class="WinUIDemo.App"
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
    xmlns:local="using:WinUIDemo">
    <Application.Resources>
        <ResourceDictionary>
            <ResourceDictionary.MergedDictionaries>
                <XamlControlsResources xmlns="using:Microsoft.UI.Xaml.Controls" />
                <!-- Other merged dictionaries here -->
            </ResourceDictionary.MergedDictionaries>
            <!-- Other app resources here -->
        </ResourceDictionary>
    </Application.Resources>
</Application>

與其關聯的 App.xaml.hApp.xaml.cpp 實現了 WinRT 主應用類,這里封裝了程序入口函數。

// on App.xaml.h
struct App : AppT<App>
{
    App();

    void OnLaunched(Microsoft::UI::Xaml::LaunchActivatedEventArgs const&);

private:
    winrt::Microsoft::UI::Xaml::Window window{ nullptr };
};
// on App.xaml.cpp
void App::OnLaunched(LaunchActivatedEventArgs const&)
{
    window = make<MainWindow>();
    window.Activate();
}

入口很簡單,創建主窗口並激活。

MainWindow.xaml 描述了應用的主窗口界面,MainWindow.xaml.hMainWindow.xaml.cpp 實現了主窗口類,這里封裝了窗口事件回調。

<!--on MainWindow.xaml-->
<Window
    x:Class="WinUIDemo.MainWindow"
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
    xmlns:local="using:WinUIDemo"
    xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
    xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
    mc:Ignorable="d">

    <StackPanel Orientation="Horizontal" HorizontalAlignment="Center" VerticalAlignment="Center">
        <Button x:Name="myButton" Click="myButton_Click">Click Me</Button>
    </StackPanel>
</Window>

主窗口描述了一個居中的堆疊布局,里面有一個按鈕 myButton

// on MainWindow.xaml.h
struct MainWindow : MainWindowT<MainWindow>
{
    MainWindow();

    int32_t MyProperty();
    void MyProperty(int32_t value);

    void myButton_Click(
        Windows::Foundation::IInspectable const& sender, 
        Microsoft::UI::Xaml::RoutedEventArgs const& args);
};
// on MainWindow.xaml.cpp
void MainWindow::myButton_Click(IInspectable const&, RoutedEventArgs const&)
{
    myButton().Content(box_value(L"Clicked"));
}

主窗口類也很簡單,當按鈕 myButton 點擊時,替換其內容文本。

事件綁定

和傳統的 Win32 UI 技術不同,MainWindow 類並沒有消息處理函數,也無需處理消息循環,但是這個 myButton_Click 函數是如何和 MainWindow.xaml 中描述的 myButton 按鈕的點擊事件綁定在一起呢?

追蹤 myButton_Click 函數的調用,可以發現調用者在 MainWindow.xaml.g.hpp 文件中。

// on MainWindow.xaml.g.hpp
template <typename D, typename ... I>
void MainWindowT<D, I...>::Connect(int32_t connectionId, IInspectable const& target)
{
    switch (connectionId)
    {
    case 2:
        {
            auto targetElement = target.as<::winrt::Microsoft::UI::Xaml::Controls::Button>();
            this->myButton(targetElement);
            auto weakThis = ::winrt::make_weak<class_type>(*this);
            targetElement.Click([weakThis](
                ::winrt::Windows::Foundation::IInspectable const& p0, 
                ::winrt::Microsoft::UI::Xaml::RoutedEventArgs const& p1){
                    if (auto t = weakThis.get())
                    {
                        ::winrt::get_self<D>(t)->myButton_Click(p0, p1);
                    }
                });
        }
        break;
    }
    _contentLoaded = true;
}

這個文件正是 C++/WinRT 對 MainWindow.xaml 描述的對象生成的 C++ 代碼。微軟在符合 C++ 17 標准的前提下,通過使用大量現代 C++ 的語言特性,將 WinRT API 封裝到頭文件模板庫中,供開發者使用。

運行

編譯運行模板工程。

測試了下這個窗口,無論是執行速度、字體渲染還是高 DPI 縮放都處理的很不錯。不得不說,微軟在技術實力和設計大框架上一直都讓人很佩服(笑)。

添加自定義頁面

下面我們添加一個自己頁面,在工程中插入一個新的 WinUI 3 空白頁 RootPage.xaml

界面和代碼參考微軟提供的 XAML 控件演示工程。

Xaml-Controls-Gallery

<!-- on RootPage.xaml -->
<Page
    x:Class="WinUIDemo.RootPage"
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
    xmlns:local="using:WinUIDemo"
    xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
    xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
    mc:Ignorable="d">

    <NavigationView x:Name="RootNavigation">
        <NavigationView.MenuItems>
            <NavigationViewItem Icon="Play" Content="Menu Item1" Tag="SamplePage1" />
            <NavigationViewItem Icon="Save" Content="Menu Item2" Tag="SamplePage2" />
            <NavigationViewItem Icon="Refresh" Content="Menu Item3" Tag="SamplePage3" />
            <NavigationViewItem Icon="Download" Content="Menu Item4" Tag="SamplePage4" />
        </NavigationView.MenuItems>
        <Frame x:Name="RootFrame"/>
    </NavigationView>
</Page>

在 Page 中描述一個側邊導航欄,和一個主頁面框架,作為應用的基礎結構。

// on App.xaml.cpp
void App::OnLaunched(LaunchActivatedEventArgs const&)
{
    window = make<MainWindow>();
    auto rootPage = make<RootPage>();
    window.Content(rootPage);
    window.Activate();
}

在入口函數中,把創建的頁面對象塞進窗口中,一個簡單的應用就搭好了。

布局、控件、頁面路由和事件交互等話題在下一篇中討論。

參考

作者:lyincc
版權:本文采用「CC BY 4.0」知識共享許可協議進行許可。
地址:https://www.cnblogs.com/lyincc/p/cpp-winui-3-note-01.html


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM