跳到主要内容
版本:11.0.0

👉 如何使用资源

应用程序中的基础图形元素通常需要统一的呈现风格,例如画刷(Brush)、颜色等。您可以将这些定义为资源,放在 Avalonia UI 应用程序的不同层级中,也可以将它们放在需要的文件中引用。

资源(Resources)总是定义在资源字典内。这意味着每个资源都有一个Key属性。

定义资源字典的位置决定了资源的有效范围:资源在定义它们的文件及其子级中都可用。因此,您可以通过选择资源字典的位置来定制资源的范围。

声明资源

例如,您可能希望在整个应用程序中标准化笔刷颜色。在这种情况下,您可以在应用程序的 XAML 文件App.axaml中声明一个资源字典,如下所示:

App.axaml
<Application xmlns="https://github.com/avaloniaui"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
x:Class="MyApp.App">
<Application.Resources>
<SolidColorBrush x:Key="Warning">Yellow</SolidColorBrush>
</Application.Resources>
</Application>

或者,您可能希望一组资源仅适用于特定的窗口或用户控件。在这种情况下,您将在窗口或用户控件文件中定义一个资源字典。例如:

MyUserControl.axaml
<UserControl xmlns="https://github.com/avaloniaui"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
x:Class="MyApp.MyUserControl">
<UserControl.Resources>
<SolidColorBrush x:Key="Warning">LightYellow</SolidColorBrush>
</UserControl.Resources>
</UserControl>

实际上,您可以在控件级别定义资源,如果需要:

MainWindow.axaml
<Window xmlns="https://github.com/avaloniaui"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
x:Class="MyApp.MainWindow">
<StackPanel>
<StackPanel.Resources>
<SolidColorBrush x:Key="Warning">PaleGoldenRod</SolidColorBrush>
</StackPanel.Resources>
</StackPanel>
</Window>

您还可以声明特定样式使用的资源。

MyStyle.axaml
<Style Selector="TextBlock.warning">
<Style.Resources>
<SolidColorBrush x:Key="Warning">Yellow</SolidColorBrush>
</Style.Resources>
<Setter ... />
</Style>
备注

请注意,此资源在特定样式块之外是不可见的,这意味着它不会在样式块之外具有“warning”类的TextBlock中生效。

还可以为特定主题变体定义资源:深色(Dark)、浅色(Light) 或自定义主题。下面的示例中,BackgroundBrushForegroundBrush 将根据系统或应用程序设置的当前主题变体而具有不同的值。有关主题变体的更多信息,请阅读 主题变体 页面。

<ResourceDictionary>
<ResourceDictionary.ThemeDictionaries>
<ResourceDictionary x:Key='Light'>
<SolidColorBrush x:Key='BackgroundBrush'>White</SolidColorBrush>
<SolidColorBrush x:Key='ForegroundBrush'>Black</SolidColorBrush>
</ResourceDictionary>
<ResourceDictionary x:Key='Dark'>
<SolidColorBrush x:Key='BackgroundBrush'>Black</SolidColorBrush>
<SolidColorBrush x:Key='ForegroundBrush'>White</SolidColorBrush>
</ResourceDictionary>
</ResourceDictionary.ThemeDictionaries>
</ResourceDictionary>

资源字典文件

通过在单独的文件中定义资源字典,您可以提高 Avalonia UI 应用程序项目的组织性,使资源定义易于查找和维护。

位于资源字典文件中的资源可在整个应用程序中访问。

要添加资源字典文件,请按照以下步骤操作:

  • 在您要创建新文件的位置,右键单击项目。
  • 单击 添加,然后选择 新建项
  • 在左侧的列表中单击 Avalonia
  • 选择 Resource Dictionary (Avalonia)
  • 输入要使用的文件名。
  • 单击 添加

现在,您可以在指定的位置添加要定义的资源。如下所示:

<ResourceDictionary xmlns="https://github.com/avaloniaui"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml">
<!-- 在此添加资源 -->
</ResourceDictionary>

使用资源

您可以使用 DynamicResource 标记扩展使用在资源字典的有效范围内定义的资源。

例如,要直接在边框元素的背景属性上使用资源,可以使用以下 XAML:

<Border Background="{DynamicResource Warning}">
Look out!
</Border>

静态资源

或者,您可以选择使用 StaticResource 标记扩展。例如:

<Border Background="{StaticResource Warning}">
Look out!
</Border>

静态资源不同之处在于它不会响应代码中(运行时)对资源的更改。一旦加载,静态资源将不可更改。

使用静态资源的好处是它的计算量较小,因此加载速度更快,并且使用的内存更少。

资源优先级

Avalonia UI 通过从 DynamicResourceStaticResource 标记扩展所在的逻辑控件树级别向上搜索来解析要使用的资源,直到找到具有所请求的关键字的资源。

这意味着具有相同关键字的资源的优先级是基于它们与正在解析的资源标记扩展之间的距离来确定的。因此,逻辑控件树中较高级别的资源定义实际上会被处于较低级别的资源定义“覆盖”。例如,请考虑以下 XAML:

<UserControl ... >
<UserControl.Resources>
<SolidColorBrush x:Key="Warning">Yellow</SolidColorBrush>
</UserControl.Resources>

<StackPanel>
<StackPanel.Resources>
<SolidColorBrush x:Key="Warning">Orange</SolidColorBrush>
</StackPanel.Resources>

<Border Background="{DynamicResource Warning}">
Look out!
</Border>
</StackPanel>
</UserControl>

在这里,边框控件使用了关键字为Warning的资源。这个资源在父控件(StackPanel)级别上定义了两次,并且在UserControl级别上也定义了一次。Avalonia UI 最终确定的边框背景应该是橙色,因为沿着逻辑控件树向上搜索的第一个拥有此关键字的资源字典位于StackPanel中。

包含和合并资源

可以从资源字典文件中包含资源,并将其与另一个文件中定义的资源合并(即使没有任何资源)。

这意味着您可以在一个独立的文件中设计样式,并在另一个文件中引入其中的资源。这样更容易实现样式一致,也便于组织和维护应用程序解决方案。

要在样式文件中包含来自文件的资源字典,请添加以下 XAML:

<Styles.Resources>
<ResourceDictionary>
<ResourceDictionary.MergedDictionaries>
<ResourceInclude Source="/Assets/AppResources.axaml"/>
</ResourceDictionary.MergedDictionaries>
</ResourceDictionary>
</Styles.Resources>

在上面的示例中,资源文件 AppResources.axaml 位于 /Assets 项目文件夹中。然后,您可以使用这些资源定义样式,例如:

<Style Selector="Button.btn-info">
<Setter Property="Background" Value="{StaticResource InfoColor}"/>
</Style>

其中,资源 InfoColor 在导入的文件中被定义为 SolidColorBrush

信息

请注意,这里使用 StaticResource 引用,因为它不能更改——这里的需求是保持样式一致。

合并资源的优先级

如前所述,资源通过从标记扩展所在的点开始向上搜索逻辑控件树来解析。

然而,由于可以在应用程序的不同位置中定义样式和合并字典,优先级规则将更加复杂,具体规则如下所示:

  • Control resources -> Merged dictionaries
  • Style resources -> Merged dictionaries
  • App resources -> Merged dictionaries

例如,在下面的“应用程序”中,底部Border控件上使用的资源将遵循[]中指示的顺序进行搜索:

Application
|- Resources [11]
|- Merged dictionary [12]
|- Merged dictionary [13]
|- Styles
|- Resources [14]
|- Merged dictionary [15]
|- Merged dictionary [16]

Window
|- Resources [6]
|- Merged dictionary [7]
|- Styles
|- Resources [8]
|- Merged dictionary [9]
|- Merged dictionary [10]
|- StackPanel
|- Resources [1]
|- Merged dictionary [2]
|- Merged dictionary [3]
|- Styles
|- Resources [4]
|- Merged dictionary [5]
|- Border

从Border控件开始,首先搜索父级(StackPanel)控件中定义的资源。然后再考虑同一级别的合并字典 - 按照 XAML 中它们出现的顺序。

然后,搜索父级(StackPanel)控件中定义的样式,接着再搜索同一级别的合并字典。

在逻辑控件树中向上搜索,每个级别都以类似的方式进行。应用程序级别的资源和样式是搜索的最后一级。

从代码中使用资源

Avalonia 提供了不同的方法来实现代码中访问资源。

备注

在下面的示例中,ResourceNode 可以是支持 Resource 的任何节点,比如 Appliction.CurrentWindowUserControl 等等。

  • ResourceNode.Resources["TheKey"]:
    这将直接访问当前的 Dictionary。请注意:合并的字典和父级将不会被扫描。
  • ResourceNode.TryGetResource:
    此函数将尝试获取特定资源,并在成功时返回 true,否则返回 false。将扫描合并的字典,但不会遵循逻辑树。
  • ResourceNode.TryFindResource:
    此扩展方法将尝试获取特定资源,并在成功时返回 true,否则返回 false。将扫描合并字典和逻辑树。
  • ResourceNode.GetResourceObservable:
    这将返回一个可用于观察资源更改的 IObservable。例如,您可以绑定到它。
// 在此示例中,我们在 App.axaml 中定义了资源,并且希望在 MainWindow 构造函数中查找该值。
//
// </Application.Resources>
// <x:String x:Key="TheKey">HelloWorld</x:String>
// </Application.Resources>

public MainWindow()
{
InitializeComponent();

// found1 = false | result1 = null
var found1 = this.TryGetResource("TheKey", this.ActualThemeVariant, out var result1);

// found2 = true | result2 = "Hello World"
var found2 = this.TryFindResource("TheKey", this.ActualThemeVariant, out var result2);

// 从代码中找到资源并将其绑定到 TextBlock
myTextBlock.Bind(TextBlock.TextProperty, Resources.GetResourceObservable("TheKey"));

// 通过绑定的 observable 更新 myTextBlock.Text
this.Resources["TheKey"] = "Hello from code behind";
}