从 WPF 到 Avalonia 的迁移系列实战篇3:ResourceDictionary资源与样式的差异与迁移技巧

发布于:2025-08-29 ⋅ 阅读:(13) ⋅ 点赞:(0)

从 WPF 到 Avalonia 的迁移系列实战篇3:ResourceDictionary资源与样式的差异与迁移技巧

我的GitHub仓库Avalonia学习项目包含完整的Avalonia实践案例与代码对比。
我的gitcode仓库是Avalonia学习项目
文中主要示例代码均可在仓库中查看,涵盖核心功能实现与优化方案。
点击链接即可直接访问,建议结合代码注释逐步调试。

在 WPF 开发中,我们习惯了用 ResourceDictionary 作为统一的资源容器,里面既可以放普通资源(画刷、字符串、模板),也可以放样式(Style)。
但是当你迁移到 Avalonia 时,会发现它把 资源样式 分成了两块:ResourcesStyles。这一点如果不注意,很容易踩坑。本文将结合learningAvalonia仓库的示例,详细对比一下两者的差异,并给出迁移技巧。


一、WPF 中的 ResourceDictionary

在 WPF 中,ResourceDictionary 是一个“万能容器”,里面可以存放一切资源,包括:

  • 普通资源(颜色、画刷、模板等)
  • 控件样式(Style

1. WPF资源字典的典型结构

以仓库中WPF项目的资源文件为例:

  • 颜色与字体资源(ColorAndFomts.xaml):定义了颜色、画笔、字体样式等基础资源
<ResourceDictionary
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
    xmlns:system="clr-namespace:System;assembly=System.Runtime"
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml">
    <!--  定义颜色  -->
    <Color x:Key="PrimaryColor">#FF4CAF50</Color>
    <SolidColorBrush Color="{StaticResource PrimaryColor}" x:Key="PrimaryColorBrush" />

    <Color x:Key="SecondaryColor">#FFC107</Color>
    <SolidColorBrush Color="{StaticResource SecondaryColor}" x:Key="SecondaryColorBrush" />

    <Color x:Key="AccentColor">#FFEB3B</Color>
    <SolidColorBrush Color="{StaticResource AccentColor}" x:Key="AccentColorBrush" />
    <!--  定义字体  -->
    <FontWeight x:Key="BoldFontWeight">Bold</FontWeight>
    <system:Double x:Key="DefaultFontSize">30</system:Double>

</ResourceDictionary>

  • 样式资源(blinkingButtonStyle.xaml):定义了控件样式,与其他资源存储在同类型的ResourceDictionary
<ResourceDictionary
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
    xmlns:control="clr-namespace:WpfDemo.control"
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml">

    <Style TargetType="{x:Type control:BlinkingButton}">
        <Setter Property="Background">
            <Setter.Value>
                <LinearGradientBrush EndPoint="1,1" StartPoint="0,0">
                    <GradientStop Color="#00C3FF" Offset="0" />
                    <GradientStop Color="#FF61A6" Offset="1" />
                </LinearGradientBrush>
            </Setter.Value>
        </Setter>
        <Setter Property="Foreground" Value="White" />
        <Setter Property="FontWeight" Value="Bold" />
        <Setter Property="BorderThickness" Value="0" />
        <Setter Property="Padding" Value="12,6" />
        <Setter Property="Template">
            <Setter.Value>
                <ControlTemplate TargetType="{x:Type control:BlinkingButton}">
                    <Border Background="{TemplateBinding Background}" CornerRadius="20">
                        <ContentPresenter HorizontalAlignment="Center" VerticalAlignment="Center" />
                    </Border>
                </ControlTemplate>
            </Setter.Value>
        </Setter>
    </Style>
    <!--  新增的 Button 样式  -->
    <Style TargetType="{x:Type Button}" x:Key="RoundedButtonStyle">
        <Setter Property="Background" Value="LightBlue" />
        <Setter Property="Foreground" Value="White" />
        <Setter Property="FontWeight" Value="Bold" />
        <Setter Property="BorderThickness" Value="0" />
        <Setter Property="Padding" Value="12,6" />
        <Setter Property="Template">
            <Setter.Value>
                <ControlTemplate TargetType="{x:Type Button}">
                    <Border Background="{TemplateBinding Background}" CornerRadius="20">
                        <ContentPresenter HorizontalAlignment="Center" VerticalAlignment="Center" />
                    </Border>
                </ControlTemplate>
            </Setter.Value>
        </Setter>
    </Style>

</ResourceDictionary>

2. WPF中资源的合并与引用

<Application
    StartupUri="MainWindow.xaml"
    x:Class="WpfDemo.App"
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml">
    <Application.Resources>
        <ResourceDictionary>
            <ResourceDictionary.MergedDictionaries>
                <ResourceDictionary Source="Themes/blinkingButtonStyle.xaml" />
                <ResourceDictionary Source="Themes/ColorAndFomts.xaml" />
            </ResourceDictionary.MergedDictionaries>
        </ResourceDictionary>
    </Application.Resources>
</Application>

在界面中引用时,无论是基础资源还是样式,都通过{StaticResource}

<Button
            Background="{StaticResource SecondaryColorBrush}"
            Content="我是一个圆角按钮"
            FontSize="{StaticResource DefaultFontSize}"
            FontWeight="{StaticResource BoldFontWeight}"
            Grid.Row="2"
            Height="80"
            HorizontalAlignment="Center"
            Style="{StaticResource RoundedButtonStyle}"
            VerticalAlignment="Center"
            Width="300" />

3. WPF资源管理的特点

  • 资源与样式混合存储:颜色、画笔、样式等所有资源都在ResourceDictionary根节点下,仅通过x:Key区分。
  • 样式即资源Style是一种特殊的资源,必须通过x:Key(或TargetType隐式Key)标识,引用方式与其他资源一致。
  • 缺乏结构区分:大型项目中资源字典可能包含成百上千个对象,混合存储会导致维护困难。

在这种模式下,WPF 查找资源的规则是:
控件本地 → 上级容器 → 窗口 → 应用程序 → 系统/主题。

所以,WPF 开发者习惯了把所有资源都丢到一个字典里。


二、Avalonia中的ResourceDictionary:资源与样式的分离设计

Avalonia作为WPF的跨平台继承者,在资源管理上进行了针对性优化。其核心变化是:ResourceDictionary明确划分为Resources(基础资源)和Styles(样式集合)两个独立区域,使资源结构更清晰。

1. Avalonia资源字典的典型结构

Avalonia的ResourceDictionary通过两个子节点分离资源类型:

  • <Resources>:存储非样式资源(颜色、画笔、字体、数据等),与WPF中的基础资源对应。
  • <Styles>:专门存储Style对象,支持通过Selector(选择器)更灵活地定位目标控件。

以仓库中Avalonia项目的资源文件为例:

  • 颜色与字体资源(ColorAndFomts.xaml):定义了颜色、画笔、字体样式等基础资源,依然使用ResourceDictionary
<ResourceDictionary
    xmlns="https://github.com/avaloniaui"
    xmlns:system="clr-namespace:System;assembly=System.Runtime"
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml">
    <!--  Add Resources Here  -->
    <!--  定义颜色  -->
    <Color x:Key="PrimaryColor">#FF4CAF50</Color>
    <SolidColorBrush Color="{StaticResource PrimaryColor}" x:Key="PrimaryColorBrush" />

    <Color x:Key="SecondaryColor">#FFC107</Color>
    <SolidColorBrush Color="{StaticResource SecondaryColor}" x:Key="SecondaryColorBrush" />

    <Color x:Key="AccentColor">#FFEB3B</Color>
    <SolidColorBrush Color="{StaticResource AccentColor}" x:Key="AccentColorBrush" />
    <!--  定义字体  -->
    <FontWeight x:Key="BoldFontWeight">Bold</FontWeight>
    <system:Double x:Key="DefaultFontSize">30</system:Double>
</ResourceDictionary>

样式资源(blinkingButtonStyles.xaml):定义了控件样式,必须放在 Styles 节点下,不能混在 Resources 中。

<Styles
    xmlns="https://github.com/avaloniaui"
    xmlns:local="clr-namespace:AvaloniaDemo.Controls"
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml">
    <Style Selector="local|BlinkingButton">
        <Setter Property="Background">
            <Setter.Value>
                <LinearGradientBrush EndPoint="100%,100%" StartPoint="0%,0%">
                    <GradientStop Color="#00C3FF" Offset="0" />
                    <GradientStop Color="#FF61A6" Offset="1" />
                </LinearGradientBrush>
            </Setter.Value>
        </Setter>
        <Setter Property="Foreground" Value="White" />
        <Setter Property="FontWeight" Value="Bold" />
        <Setter Property="BorderThickness" Value="0" />
        <Setter Property="Padding" Value="12,6" />
        <Setter Property="Template">
            <ControlTemplate>
                <Border Background="{TemplateBinding Background}" CornerRadius="20">
                    <ContentPresenter
                        Content="{TemplateBinding Content}"
                        ContentTemplate="{TemplateBinding ContentTemplate}"
                        Foreground="{TemplateBinding Foreground}"
                        HorizontalAlignment="Center"
                        VerticalAlignment="Center" />
                </Border>
            </ControlTemplate>
        </Setter>
    </Style>
    <!--  新增的 Button 样式  -->
    <Style Selector="Button.rounded">
        <Setter Property="Background" Value="LightBlue" />
        <Setter Property="Foreground" Value="White" />
        <Setter Property="FontWeight" Value="Bold" />
        <Setter Property="BorderThickness" Value="0" />
        <Setter Property="Padding" Value="12,6" />
        <Setter Property="Template">
            <Setter.Value>
                <ControlTemplate TargetType="{x:Type Button}">
                    <Border Background="{TemplateBinding Background}" CornerRadius="20">
                        <ContentPresenter
                            Content="{TemplateBinding Content}"
                            HorizontalAlignment="Center"
                            VerticalAlignment="Center" />
                    </Border>
                </ControlTemplate>
            </Setter.Value>
        </Setter>
    </Style>

</Styles>

2. Avalonia中资源的合并与引用

Avalonia合并资源字典的方式与WPF类似,但内部会区分ResourcesStyles的合并:

<!-- AvaloniaDemo/App.axaml -->
<Application
    RequestedThemeVariant="Default"
    x:Class="AvaloniaDemo.App"
    xmlns="https://github.com/avaloniaui"
    xmlns:local="using:AvaloniaDemo"
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml">
    <!--  "Default" ThemeVariant follows system theme variant. "Dark" or "Light" are other available options.  -->

    <Application.DataTemplates>
        <local:ViewLocator />
    </Application.DataTemplates>
    <!--  Styles:只能放 控件样式(Setter、ControlTemplate 等)  -->
    <Application.Styles>
        <FluentTheme />
        <StyleInclude Source="avares://AvaloniaDemo/Themes/BlinkingButtonStyles.axaml" />
    </Application.Styles>
    <!--  Resources:只能放 非样式资源(Brush、字符串、数值等)。  -->
    <Application.Resources>
        <ResourceDictionary>
            <ResourceDictionary.MergedDictionaries>
                <ResourceInclude Source="avares://AvaloniaDemo/Themes/ColorAndFonts.axaml" />
            </ResourceDictionary.MergedDictionaries>
        </ResourceDictionary>
    </Application.Resources>
</Application>

引用时,基础资源仍通过{StaticResource},而样式可*使用 CSS-like 选择器,例如:

样式的特点

  • 必须放在 Styles 节点下,不能混在 Resources 中。

  • 使用 CSS-like 选择器,例如:

     <Button
              Background="{StaticResource SecondaryColorBrush}"
              Classes="rounded"
              Content="我是一个圆角按钮"
              FontFamily="{StaticResource DefaultFontFamily}"
              FontSize="{StaticResource DefaultFontSize}"
              FontWeight="{StaticResource BoldFontWeight}"
              Grid.Row="2"
              Height="80"
              HorizontalAlignment="Center"
              VerticalAlignment="Center"
              Width="300" />
    

三、迁移时的对比总结

功能 WPF (ResourceDictionary) Avalonia (Resources & Styles)
普通资源存放 ResourceDictionary Resources
样式存放 ResourceDictionary Styles
样式语法 基于 TargetType / BasedOn 基于 CSS-like Selector
动态资源引用 {DynamicResource} {DynamicResource}(不常用)
合并资源字典 <ResourceDictionary.MergedDictionaries> <StyleInclude> / <ResourceInclude>

四、迁移技巧

  1. 把样式移到 Styles

    • WPF 中写在 ResourceDictionary 里的 Style,要迁移到 Avalonia 的 <Styles> 中。
    <!-- WPF -->
    <ResourceDictionary>
        <Style TargetType="Button">
            <Setter Property="Background" Value="Red"/>
        </Style>
    </ResourceDictionary>
    
    <!-- Avalonia -->
    <Styles>
        <Style Selector="Button">
            <Setter Property="Background" Value="Red"/>
        </Style>
    </Styles>
    
  2. 普通资源依旧放在 Resources
    画刷、模板、字符串等写在 <Resources> 中,然后用 {StaticResource}{DynamicResource} 引用。

  3. 合并资源字典

    • WPF:

      <Application.Resources>
          <ResourceDictionary>
              <ResourceDictionary.MergedDictionaries>
                  <ResourceDictionary Source="Dictionary1.xaml"/>
              </ResourceDictionary.MergedDictionaries>
          </ResourceDictionary>
      </Application.Resources>
      
    • Avalonia:

      <Application.Styles>
          <FluentTheme Mode="Light"/>
          <StyleInclude Source="avares://MyApp/Styles/Button.axaml"/>
      </Application.Styles>
      
  4. 利用 CSS-like Selector 提升可维护性
    Avalonia 的 :hover:checked 等伪类可以取代 WPF 的触发器,更直观。


五、结语

WPF 中的 ResourceDictionary 是一个“万能大容器”,而 Avalonia 则通过把 ResourcesStyles 分离,让资源管理更清晰,结构更接近 Web 开发思路。

如果你正在从 WPF 迁移到 Avalonia,建议你先把样式统一放到 <Styles>,资源放到 <Resources>,这样能避免很多“为什么样式不起作用”的坑。

我的GitHub仓库Avalonia学习项目包含完整的Avalonia实践案例与代码对比。
我的gitcode仓库是Avalonia学习项目
文中主要示例代码均可在仓库中查看,涵盖核心功能实现与优化方案。
点击链接即可直接访问,建议结合代码注释逐步调试。


网站公告

今日签到

点亮在社区的每一天
去签到