Skip to content

WinUI 3 WinRT C++ 开发完整教程 - 第五部分:XAML框架与数据绑定深层机制

XAML 编译和解析机制

XAML 编译流程详解

XAML 不是在运行时解析的,而是在编译时被转换为 C++ 代码。让我们深入了解这个过程:

1. XAML 编译器的工作原理

xml
<!-- MainWindow.xaml 原始文件 -->
<Window x:Class="WinUI3App1C__.MainWindow"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml">
    <Grid>
        <Button x:Name="myButton" Content="点击我" Click="myButton_Click"/>
    </Grid>
</Window>

编译器会生成对应的 C++ 代码(MainWindow.g.h):

cpp
// MainWindow.g.h (简化版)
namespace winrt::WinUI3App1C__::implementation
{
    template<typename D>
    struct MainWindowT : DependencyObjectT<D>
    {
    private:
        bool _contentLoaded = false;
        
        // XAML 中定义的控件的成员变量
        winrt::Microsoft::UI::Xaml::Controls::Button _myButton{nullptr};
        
    public:
        void InitializeComponent()
        {
            if (_contentLoaded)
                return;
            
            _contentLoaded = true;
            
            // 创建根元素 (Grid)
            auto grid = winrt::Microsoft::UI::Xaml::Controls::Grid{};
            
            // 创建按钮
            _myButton = winrt::Microsoft::UI::Xaml::Controls::Button{};
            _myButton.Content(winrt::box_value(L"点击我"));
            
            // 绑定事件处理器
            _myButton.Click({this, &MainWindow::myButton_Click});
            
            // 添加到父容器
            grid.Children().Append(_myButton);
            
            // 设置为窗口内容
            Content(grid);
        }
        
        // 访问 XAML 控件的方法
        winrt::Microsoft::UI::Xaml::Controls::Button myButton()
        {
            return _myButton;
        }
    };
}

2. x:Name 的实现机制

当你在 XAML 中写 x:Name="myButton" 时,编译器会:

  1. 生成私有成员变量_myButton
  2. 生成公共访问方法myButton()
  3. 在 InitializeComponent 中初始化:创建对象并赋值
cpp
// 理解为什么可以在 C++ 代码中直接使用 myButton()
void MainWindow::SomeMethod()
{
    // 这个调用实际上是调用生成的访问方法
    myButton().Content(winrt::box_value(L"新内容"));
    
    // 等价于访问私有成员 _myButton
    // _myButton.Content(winrt::box_value(L"新内容")); // 直接访问会编译错误
}

3. 事件绑定的生成机制

XAML 中的 Click="myButton_Click" 会被编译器转换为:

cpp
// 在 InitializeComponent 中生成的代码
_myButton.Click({this, &implementation::MainWindow::myButton_Click});

// 这需要你在 .h 文件中声明对应的方法:
void myButton_Click(
    winrt::Windows::Foundation::IInspectable const& sender,
    winrt::Microsoft::UI::Xaml::RoutedEventArgs const& e);

XAML 命名空间映射

1. 默认命名空间

xml
<!-- 默认命名空间包含所有基础 UI 控件 -->
<Window xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation">
    <Button/>  <!-- Microsoft.UI.Xaml.Controls.Button -->
    <TextBox/> <!-- Microsoft.UI.Xaml.Controls.TextBox -->
</Window>

这个 URL 映射到:

  • winrt::Microsoft::UI::Xaml::Controls::
  • winrt::Microsoft::UI::Xaml::
  • 其他相关命名空间

2. XAML 命名空间 (x:)

xml
<Window xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml">
    <Button x:Name="myButton"/>     <!-- 指定名称 -->
    <Grid x:FieldModifier="public"/><!-- 修改字段访问性 -->
</Window>

3. 自定义命名空间映射

xml
<!-- 映射到项目中的自定义类型 -->
<Window xmlns:local="using:WinUI3App1C__">
    <local:CustomControl/>  <!-- 使用自定义控件 -->
</Window>

对应的 C++ 命名空间:

cpp
namespace winrt::WinUI3App1C__::implementation
{
    struct CustomControl : CustomControlT<CustomControl>
    {
        // 自定义控件实现
    };
}

依赖属性系统深度解析

依赖属性的底层实现

依赖属性是 WinUI 3 的核心特性,支持数据绑定、动画、样式等功能:

1. 依赖属性的内部结构

cpp
// 简化的依赖属性系统实现
class DependencyPropertySystem
{
private:
    // 全局属性注册表
    static std::unordered_map<winrt::guid, std::unique_ptr<PropertyMetadata>> s_properties;
    
    // 每个对象的属性值存储
    std::unordered_map<winrt::Microsoft::UI::Xaml::DependencyProperty, winrt::Windows::Foundation::IInspectable> m_localValues;
    
public:
    // 注册依赖属性
    static winrt::Microsoft::UI::Xaml::DependencyProperty Register(
        hstring const& name,
        winrt::Windows::UI::Xaml::Interop::TypeName const& propertyType,
        winrt::Windows::UI::Xaml::Interop::TypeName const& ownerType,
        winrt::Microsoft::UI::Xaml::PropertyMetadata const& metadata);
    
    // 获取属性值
    winrt::Windows::Foundation::IInspectable GetValue(
        winrt::Microsoft::UI::Xaml::DependencyProperty const& property);
    
    // 设置属性值
    void SetValue(
        winrt::Microsoft::UI::Xaml::DependencyProperty const& property,
        winrt::Windows::Foundation::IInspectable const& value);
};

2. 创建自定义依赖属性

让我们创建一个完整的依赖属性示例:

cpp
// CustomControl.idl
namespace WinUI3App1C__
{
    [default_interface]
    runtimeclass CustomControl : Microsoft.UI.Xaml.Controls.UserControl
    {
        CustomControl();
        
        // 声明依赖属性
        static Microsoft.UI.Xaml.DependencyProperty TitleProperty{ get; };
        String Title;
        
        static Microsoft.UI.Xaml.DependencyProperty ValueProperty{ get; };
        Double Value;
    }
}
cpp
// CustomControl.h
namespace winrt::WinUI3App1C__::implementation
{
    struct CustomControl : CustomControlT<CustomControl>
    {
    private:
        // 静态依赖属性字段
        static winrt::Microsoft::UI::Xaml::DependencyProperty s_titleProperty;
        static winrt::Microsoft::UI::Xaml::DependencyProperty s_valueProperty;
        
    public:
        CustomControl();
        
        // 依赖属性访问器
        static winrt::Microsoft::UI::Xaml::DependencyProperty TitleProperty() { return s_titleProperty; }
        hstring Title() const;
        void Title(hstring const& value);
        
        static winrt::Microsoft::UI::Xaml::DependencyProperty ValueProperty() { return s_valueProperty; }
        double Value() const;
        void Value(double value);
        
    private:
        // 属性变更回调
        static void OnTitlePropertyChanged(
            winrt::Microsoft::UI::Xaml::DependencyObject const& d,
            winrt::Microsoft::UI::Xaml::DependencyPropertyChangedEventArgs const& e);
        
        static void OnValuePropertyChanged(
            winrt::Microsoft::UI::Xaml::DependencyObject const& d,
            winrt::Microsoft::UI::Xaml::DependencyPropertyChangedEventArgs const& e);
    };
}
cpp
// CustomControl.cpp
namespace winrt::WinUI3App1C__::implementation
{
    // 注册依赖属性
    winrt::Microsoft::UI::Xaml::DependencyProperty CustomControl::s_titleProperty =
        winrt::Microsoft::UI::Xaml::DependencyProperty::Register(
            L"Title",                                        // 属性名称
            winrt::xaml_typename<hstring>(),                 // 属性类型
            winrt::xaml_typename<winrt::WinUI3App1C__::CustomControl>(), // 拥有者类型
            winrt::Microsoft::UI::Xaml::PropertyMetadata{
                winrt::box_value(L""),                       // 默认值
                &OnTitlePropertyChanged                      // 变更回调
            });
    
    winrt::Microsoft::UI::Xaml::DependencyProperty CustomControl::s_valueProperty =
        winrt::Microsoft::UI::Xaml::DependencyProperty::Register(
            L"Value",
            winrt::xaml_typename<double>(),
            winrt::xaml_typename<winrt::WinUI3App1C__::CustomControl>(),
            winrt::Microsoft::UI::Xaml::PropertyMetadata{
                winrt::box_value(0.0),
                &OnValuePropertyChanged
            });
    
    CustomControl::CustomControl()
    {
        InitializeComponent();
    }
    
    // 属性访问器实现
    hstring CustomControl::Title() const
    {
        return winrt::unbox_value<hstring>(GetValue(s_titleProperty));
    }
    
    void CustomControl::Title(hstring const& value)
    {
        SetValue(s_titleProperty, winrt::box_value(value));
    }
    
    double CustomControl::Value() const
    {
        return winrt::unbox_value<double>(GetValue(s_valueProperty));
    }
    
    void CustomControl::Value(double value)
    {
        SetValue(s_valueProperty, winrt::box_value(value));
    }
    
    // 属性变更回调
    void CustomControl::OnTitlePropertyChanged(
        winrt::Microsoft::UI::Xaml::DependencyObject const& d,
        winrt::Microsoft::UI::Xaml::DependencyPropertyChangedEventArgs const& e)
    {
        if (auto control = d.try_as<winrt::WinUI3App1C__::implementation::CustomControl>())
        {
            // 处理 Title 属性变更
            hstring oldValue = winrt::unbox_value<hstring>(e.OldValue());
            hstring newValue = winrt::unbox_value<hstring>(e.NewValue());
            
            OutputDebugStringW((L"Title 从 '" + oldValue + L"' 变更为 '" + newValue + L"'\n").c_str());
        }
    }
    
    void CustomControl::OnValuePropertyChanged(
        winrt::Microsoft::UI::Xaml::DependencyObject const& d,
        winrt::Microsoft::UI::Xaml::DependencyPropertyChangedEventArgs const& e)
    {
        if (auto control = d.try_as<winrt::WinUI3App1C__::implementation::CustomControl>())
        {
            // 处理 Value 属性变更
            double oldValue = winrt::unbox_value<double>(e.OldValue());
            double newValue = winrt::unbox_value<double>(e.NewValue());
            
            OutputDebugStringW((L"Value 从 " + winrt::to_hstring(oldValue) + 
                               L" 变更为 " + winrt::to_hstring(newValue) + L"\n").c_str());
        }
    }
}

3. 依赖属性的值解析优先级

依赖属性系统按以下优先级解析属性值:

  1. 动画值(最高优先级)
  2. 本地值(通过 SetValue 设置)
  3. 模板绑定
  4. 样式 Setter
  5. 继承值
  6. 默认值(最低优先级)
cpp
// 演示属性值优先级
void DemonstrateDependencyPropertyPriority()
{
    auto button = winrt::Microsoft::UI::Xaml::Controls::Button{};
    
    // 1. 默认值(在注册时指定)
    // button.FontSize() 返回默认值
    
    // 2. 通过样式设置(优先级高于默认值)
    auto style = winrt::Microsoft::UI::Xaml::Style{winrt::xaml_typename<winrt::Microsoft::UI::Xaml::Controls::Button>()};
    auto setter = winrt::Microsoft::UI::Xaml::Setter{};
    setter.Property(winrt::Microsoft::UI::Xaml::Controls::Control::FontSizeProperty());
    setter.Value(winrt::box_value(16.0));
    style.Setters().Append(setter);
    button.Style(style);
    
    // 3. 本地值(优先级最高,会覆盖样式值)
    button.FontSize(20.0);
    
    // 4. 清除本地值,恢复到样式值
    button.ClearValue(winrt::Microsoft::UI::Xaml::Controls::Control::FontSizeProperty());
}

数据绑定底层原理

x:Bind 与 Binding 的区别

1. x:Bind(编译时绑定)

xml
<!-- x:Bind 在编译时生成代码 -->
<TextBlock Text="{x:Bind UserName, Mode=OneWay}"/>
<TextBlock Text="{x:Bind FormatAge(User.Age)}"/>

编译器生成的代码:

cpp
// MainWindow.g.h 中生成的绑定代码
class MainWindow_obj2_Bindings
{
private:
    winrt::weak_ref<winrt::WinUI3App1C__::MainWindow> weakRefToRootObject;
    
public:
    void Initialize(winrt::WinUI3App1C__::MainWindow const& rootObject)
    {
        weakRefToRootObject = winrt::make_weak(rootObject);
    }
    
    void Update()
    {
        if (auto rootObject = weakRefToRootObject.get())
        {
            // 获取绑定的值
            hstring userName = rootObject.UserName();
            hstring formattedAge = rootObject.FormatAge(rootObject.User().Age());
            
            // 更新 UI 元素
            rootObject.userNameTextBlock().Text(userName);
            rootObject.ageTextBlock().Text(formattedAge);
        }
    }
};

x:Bind 的优势

  • 编译时检查:类型安全,编译时发现错误
  • 高性能:直接调用,无反射开销
  • 智能提示:IDE 支持自动完成

2. Binding(运行时绑定)

xml
<!-- Binding 使用反射,在运行时解析 -->
<TextBlock Text="{Binding UserName, Mode=OneWay}"/>
<TextBlock Text="{Binding User.Age, Converter={StaticResource AgeConverter}}"/>
cpp
// Binding 的内部实现原理(简化版)
class RuntimeBindingEngine
{
public:
    static void EstablishBinding(
        winrt::Microsoft::UI::Xaml::DependencyObject const& target,
        winrt::Microsoft::UI::Xaml::DependencyProperty const& targetProperty,
        winrt::Windows::Foundation::IInspectable const& source,
        hstring const& path)
    {
        // 1. 解析属性路径
        auto pathSegments = ParsePropertyPath(path);
        
        // 2. 使用反射获取属性值
        auto value = GetValueThroughReflection(source, pathSegments);
        
        // 3. 应用转换器(如果有)
        if (auto converter = GetConverter())
        {
            value = converter.Convert(value, /* ... */);
        }
        
        // 4. 设置目标属性
        target.SetValue(targetProperty, value);
        
        // 5. 建立变更通知
        RegisterForPropertyChanges(source, path, [=](auto newValue)
        {
            // 属性变更时更新目标
            target.SetValue(targetProperty, newValue);
        });
    }
    
private:
    static std::vector<hstring> ParsePropertyPath(hstring const& path)
    {
        // 解析 "User.Age" 为 ["User", "Age"]
        std::vector<hstring> segments;
        // 实现路径解析逻辑...
        return segments;
    }
    
    static winrt::Windows::Foundation::IInspectable GetValueThroughReflection(
        winrt::Windows::Foundation::IInspectable const& source,
        std::vector<hstring> const& pathSegments)
    {
        // 使用 WinRT 反射 API 获取属性值
        auto currentObject = source;
        for (auto&& segment : pathSegments)
        {
            // 获取属性值...
        }
        return currentObject;
    }
};

双向绑定的实现

cpp
// 实现双向绑定的接口
namespace winrt::WinUI3App1C__::implementation
{
    struct UserViewModel : UserViewModelT<UserViewModel>
    {
    private:
        hstring m_userName;
        int32_t m_age;
        
        // 属性变更事件
        winrt::event<winrt::Microsoft::UI::Xaml::Data::PropertyChangedEventHandler> m_propertyChanged;
        
    public:
        // 实现 INotifyPropertyChanged
        winrt::event_token PropertyChanged(winrt::Microsoft::UI::Xaml::Data::PropertyChangedEventHandler const& handler)
        {
            return m_propertyChanged.add(handler);
        }
        
        void PropertyChanged(winrt::event_token const& token)
        {
            m_propertyChanged.remove(token);
        }
        
        // 带变更通知的属性
        hstring UserName() const { return m_userName; }
        void UserName(hstring const& value)
        {
            if (m_userName != value)
            {
                m_userName = value;
                RaisePropertyChanged(L"UserName");
            }
        }
        
        int32_t Age() const { return m_age; }
        void Age(int32_t value)
        {
            if (m_age != value)
            {
                m_age = value;
                RaisePropertyChanged(L"Age");
            }
        }
        
    private:
        void RaisePropertyChanged(hstring const& propertyName)
        {
            m_propertyChanged(*this, winrt::Microsoft::UI::Xaml::Data::PropertyChangedEventArgs{propertyName});
        }
    };
}

在 XAML 中使用双向绑定:

xml
<!-- TwoWay 绑定会在控件值变化时更新源属性 -->
<TextBox Text="{x:Bind ViewModel.UserName, Mode=TwoWay}"/>
<Slider Value="{x:Bind ViewModel.Age, Mode=TwoWay}"/>

XAML 资源系统详解

资源查找机制

XAML 资源系统使用层次化查找:

xml
<!-- App.xaml - 应用程序级资源 -->
<Application.Resources>
    <SolidColorBrush x:Key="AppPrimaryBrush" Color="Blue"/>
    <Style x:Key="AppButtonStyle" TargetType="Button">
        <Setter Property="Background" Value="{StaticResource AppPrimaryBrush}"/>
    </Style>
</Application.Resources>
xml
<!-- MainWindow.xaml - 窗口级资源 -->
<Window.Resources>
    <SolidColorBrush x:Key="WindowBackgroundBrush" Color="White"/>
    <local:MyConverter x:Key="BoolToVisibilityConverter"/>
</Window.Resources>

<Grid>
    <Grid.Resources>
        <!-- 页面级资源 -->
        <SolidColorBrush x:Key="GridBackgroundBrush" Color="LightGray"/>
    </Grid.Resources>
    
    <Button Background="{StaticResource AppPrimaryBrush}"
            Style="{StaticResource AppButtonStyle}"/>
</Grid>

资源查找的 C++ 实现

cpp
// 资源查找的内部实现
class ResourceLookup
{
public:
    static winrt::Windows::Foundation::IInspectable FindResource(
        winrt::Microsoft::UI::Xaml::FrameworkElement const& element,
        winrt::Windows::Foundation::IInspectable const& key)
    {
        // 1. 从当前元素开始查找
        auto current = element;
        while (current != nullptr)
        {
            if (current.Resources().HasKey(key))
            {
                return current.Resources().Lookup(key);
            }
            
            // 2. 向上查找父元素
            current = current.Parent().try_as<winrt::Microsoft::UI::Xaml::FrameworkElement>();
        }
        
        // 3. 查找应用程序资源
        auto app = winrt::Microsoft::UI::Xaml::Application::Current();
        if (app.Resources().HasKey(key))
        {
            return app.Resources().Lookup(key);
        }
        
        // 4. 查找系统资源
        return FindSystemResource(key);
    }
    
private:
    static winrt::Windows::Foundation::IInspectable FindSystemResource(
        winrt::Windows::Foundation::IInspectable const& key)
    {
        // 查找系统定义的资源(如系统颜色、字体等)
        return nullptr;
    }
};

动态资源和主题切换

cpp
// 实现主题切换
class ThemeManager
{
private:
    winrt::Microsoft::UI::Xaml::ResourceDictionary m_lightTheme;
    winrt::Microsoft::UI::Xaml::ResourceDictionary m_darkTheme;
    
public:
    void InitializeThemes()
    {
        // 创建浅色主题
        m_lightTheme = winrt::Microsoft::UI::Xaml::ResourceDictionary{};
        m_lightTheme.Insert(L"BackgroundBrush", 
            winrt::Microsoft::UI::Xaml::Media::SolidColorBrush{winrt::Windows::UI::Colors::White()});
        m_lightTheme.Insert(L"ForegroundBrush", 
            winrt::Microsoft::UI::Xaml::Media::SolidColorBrush{winrt::Windows::UI::Colors::Black()});
        
        // 创建深色主题
        m_darkTheme = winrt::Microsoft::UI::Xaml::ResourceDictionary{};
        m_darkTheme.Insert(L"BackgroundBrush", 
            winrt::Microsoft::UI::Xaml::Media::SolidColorBrush{winrt::Windows::UI::Colors::Black()});
        m_darkTheme.Insert(L"ForegroundBrush", 
            winrt::Microsoft::UI::Xaml::Media::SolidColorBrush{winrt::Windows::UI::Colors::White()});
    }
    
    void ApplyTheme(bool isDarkTheme)
    {
        auto app = winrt::Microsoft::UI::Xaml::Application::Current();
        auto mergedDictionaries = app.Resources().MergedDictionaries();
        
        // 移除当前主题
        mergedDictionaries.Clear();
        
        // 应用新主题
        if (isDarkTheme)
        {
            mergedDictionaries.Append(m_darkTheme);
        }
        else
        {
            mergedDictionaries.Append(m_lightTheme);
        }
        
        // 通知所有控件更新
        InvalidateVisualTree();
    }
    
private:
    void InvalidateVisualTree()
    {
        // 遍历所有窗口和控件,触发重新绘制
        // 这会导致所有 DynamicResource 引用重新解析
    }
};

模板和样式机制

控件模板的内部工作原理

cpp
// 控件模板的应用过程
class TemplatedControl : public winrt::Microsoft::UI::Xaml::Controls::Control
{
protected:
    virtual void OnApplyTemplate() override
    {
        // 1. 基类处理
        __super::OnApplyTemplate();
        
        // 2. 查找模板中的命名元素
        auto contentPresenter = GetTemplateChild(L"PART_ContentPresenter")
            .try_as<winrt::Microsoft::UI::Xaml::Controls::ContentPresenter>();
        
        auto border = GetTemplateChild(L"PART_Border")
            .try_as<winrt::Microsoft::UI::Xaml::Controls::Border>();
        
        // 3. 建立模板绑定
        if (contentPresenter)
        {
            // 绑定内容属性
            auto binding = winrt::Microsoft::UI::Xaml::Data::Binding{};
            binding.Source(*this);
            binding.Path(winrt::Microsoft::UI::Xaml::PropertyPath{L"Content"});
            winrt::Microsoft::UI::Xaml::Data::BindingOperations::SetBinding(
                contentPresenter, 
                winrt::Microsoft::UI::Xaml::Controls::ContentPresenter::ContentProperty(), 
                binding);
        }
        
        // 4. 注册模板元素的事件
        if (border)
        {
            border.PointerEntered([this](auto&&, auto&&) { OnPointerEntered(); });
            border.PointerExited([this](auto&&, auto&&) { OnPointerExited(); });
        }
    }
    
private:
    void OnPointerEntered()
    {
        // 处理鼠标进入
        winrt::Microsoft::UI::Xaml::VisualStateManager::GoToState(*this, L"PointerOver", true);
    }
    
    void OnPointerExited()
    {
        // 处理鼠标离开
        winrt::Microsoft::UI::Xaml::VisualStateManager::GoToState(*this, L"Normal", true);
    }
};

自定义控件模板

xml
<!-- 定义自定义控件模板 -->
<Style x:Key="CustomButtonStyle" TargetType="Button">
    <Setter Property="Template">
        <Setter.Value>
            <ControlTemplate TargetType="Button">
                <Border x:Name="PART_Border"
                        Background="{TemplateBinding Background}"
                        BorderBrush="{TemplateBinding BorderBrush}"
                        BorderThickness="{TemplateBinding BorderThickness}"
                        CornerRadius="5">
                    <VisualStateManager.VisualStateGroups>
                        <VisualStateGroup x:Name="CommonStates">
                            <VisualState x:Name="Normal"/>
                            <VisualState x:Name="PointerOver">
                                <VisualState.Setters>
                                    <Setter Target="PART_Border.Background" Value="LightBlue"/>
                                </VisualState.Setters>
                            </VisualState>
                            <VisualState x:Name="Pressed">
                                <VisualState.Setters>
                                    <Setter Target="PART_Border.Background" Value="DarkBlue"/>
                                </VisualState.Setters>
                            </VisualState>
                        </VisualStateGroup>
                    </VisualStateManager.VisualStateGroups>
                    
                    <ContentPresenter x:Name="PART_ContentPresenter"
                                      Content="{TemplateBinding Content}"
                                      ContentTemplate="{TemplateBinding ContentTemplate}"
                                      HorizontalAlignment="{TemplateBinding HorizontalContentAlignment}"
                                      VerticalAlignment="{TemplateBinding VerticalContentAlignment}"/>
                </Border>
            </ControlTemplate>
        </Setter.Value>
    </Setter>
</Style>

自定义标记扩展

创建自定义标记扩展

cpp
// CustomExtension.idl
namespace WinUI3App1C__
{
    [default_interface]
    runtimeclass LocalizationExtension : Microsoft.UI.Xaml.Markup.MarkupExtension
    {
        LocalizationExtension();
        LocalizationExtension(String key);
        
        String Key;
        String DefaultValue;
    }
}
cpp
// LocalizationExtension.h
namespace winrt::WinUI3App1C__::implementation
{
    struct LocalizationExtension : LocalizationExtensionT<LocalizationExtension>
    {
    private:
        hstring m_key;
        hstring m_defaultValue;
        
    public:
        LocalizationExtension() = default;
        LocalizationExtension(hstring const& key) : m_key(key) {}
        
        hstring Key() const { return m_key; }
        void Key(hstring const& value) { m_key = value; }
        
        hstring DefaultValue() const { return m_defaultValue; }
        void DefaultValue(hstring const& value) { m_defaultValue = value; }
        
        // 实现 MarkupExtension 的核心方法
        winrt::Windows::Foundation::IInspectable ProvideValue(
            winrt::Microsoft::UI::Xaml::Markup::IXamlServiceProvider const& serviceProvider) override;
    };
}
cpp
// LocalizationExtension.cpp
winrt::Windows::Foundation::IInspectable LocalizationExtension::ProvideValue(
    winrt::Microsoft::UI::Xaml::Markup::IXamlServiceProvider const& serviceProvider)
{
    // 1. 获取资源加载器
    auto resourceLoader = winrt::Windows::ApplicationModel::Resources::ResourceLoader::GetForCurrentView();
    
    // 2. 尝试加载本地化字符串
    try
    {
        hstring localizedText = resourceLoader.GetString(m_key);
        if (!localizedText.empty())
        {
            return winrt::box_value(localizedText);
        }
    }
    catch (...)
    {
        // 资源不存在,使用默认值
    }
    
    // 3. 返回默认值或键名
    hstring fallback = !m_defaultValue.empty() ? m_defaultValue : m_key;
    return winrt::box_value(fallback);
}

在 XAML 中使用自定义标记扩展:

xml
<Window xmlns:local="using:WinUI3App1C__">
    <TextBlock Text="{local:LocalizationExtension Key=WelcomeMessage, DefaultValue='Welcome!'}"/>
    <Button Content="{local:LocalizationExtension ClickMe}"/>
</Window>

总结

本部分深入讲解了 XAML 框架的核心机制:

  1. XAML 编译:理解 XAML 如何转换为 C++ 代码
  2. 依赖属性:掌握 WinUI 3 属性系统的底层实现
  3. 数据绑定:了解编译时和运行时绑定的区别和原理
  4. 资源系统:掌握资源查找和主题切换机制
  5. 模板系统:理解控件模板的应用和自定义
  6. 标记扩展:学会创建自定义的 XAML 扩展

这些深层知识帮助我们更好地理解和使用 WinUI 3 框架。


这是 WinUI 3 WinRT C++ 完整教程的第五部分。下一部分我们将学习高级控件开发和性能优化技巧。