Avalonia资源拿捏方式探索笔记
002 Avalonia资源拿捏方式探索笔记
版本 Avalonia 11.0
0. 参考资料
1. 前言
在写 WPF 的时候,后期的资源管理必须要涉及资源管理的部分,这对于一个不小的项目来说是极为必要的。
但是我在使用 Avalonia 的时候,发现 Avalonia 对于资源和实例的创建和 WPF 很大,很多 WPF 中有点黑的操作在 Avalonia 中都不能使用,为此开一篇文章来将我收集的一些使用方法整理在这里。
2. 资源创建和使用的 WPF 回忆与初尝试
说实话,上来我就被 Avalonia 的资源创建整不会了。关于命名空间也相当的陌生。
我在学习 WPF 的时候,上来的教程似乎是关于颜色的资源创建。
<Window x:Class="Test01.Views.MainWindow"
xmlns="https://github.com/avaloniaui"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:views="clr-namespace:Test01.Views"
xmlns:vm="using:Test01.ViewModels"
Title="Test01"
Width="300"
Height="200"
Icon="/Assets/avalonia-logo.ico"
mc:Ignorable="d">
<Window.Resources>
<ResourceDictionary>
<SolidColorBrush x:Key="ThisSolidColorBrush" Color="Red" />
</ResourceDictionary>
</Window.Resources>
<Grid Background="{StaticResource ThisSolidColorBrush}">
<views:MainView />
</Grid>
</Window>

答案是效果不错。确实绑定的东西都在,只是 Avalonia 的智能编辑器不是很理想,很多时候是靠编写 WPF 的经验摁敲出来的。
3. 字符串如何创建
字符串在 WPF 的创建需要引入 System 的命名空间,而我对 Avalonia 的命名空间体系略有陌生,而且智能提示的缺失也让我一定程度上寸步难行。
而在 Avalonia 中,创建字符串资源我是在 https://docs.avaloniaui.net/zh-Hans/docs/guides/styles-and-resources/resources 中的最后才看到的,文中使用了 <x:String x:Key="TheKey">HelloWorld</x:String> 这个的写法。
注意看,x:String 和 x:Key,难道说 x 在 Avalonia 里面也纳入了 System 的命名空间吗,带着这种想法,我试了一下 String 资源的创建,确实没有什么问题。
<Window x:Class="Test01.Views.MainWindow"
xmlns="https://github.com/avaloniaui"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:views="clr-namespace:Test01.Views"
xmlns:vm="using:Test01.ViewModels"
Title="Test01"
Width="300"
Height="200"
Icon="/Assets/avalonia-logo.ico"
mc:Ignorable="d">
<Window.Resources>
<ResourceDictionary>
<x:String x:Key="ThisKey">hello</x:String>
</ResourceDictionary>
</Window.Resources>
<Grid>
<views:MainView />
<TextBlock Text="{StaticResource ThisKey}" />
</Grid>
</Window>

4. Double 等资源也可以吗
我在尝试定义 double 变量,它们可能也在 x 中被写出来。
<Window x:Class="Test01.Views.MainWindow"
xmlns="https://github.com/avaloniaui"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:views="clr-namespace:Test01.Views"
xmlns:vm="using:Test01.ViewModels"
Title="Test01"
Width="300"
Height="200"
Icon="/Assets/avalonia-logo.ico"
mc:Ignorable="d">
<Window.Resources>
<ResourceDictionary>
<x:Double x:Key="WidthKey">100</x:Double>
<x:Double x:Key="HeightKey">50</x:Double>
</ResourceDictionary>
</Window.Resources>
<Grid>
<views:MainView />
<Button Width="{StaticResource WidthKey}" Height="{StaticResource HeightKey}" />
</Grid>
</Window>

5. 数据模板 DataTemplate 的资源化
众所周知,在 WPF 里面有 DataTemplate 的概念,在早期我会用一种奇怪的方法去可视化我的 ViewModel。
总之这需要一个 ViewModel,让我们来创建一个 ViewModel。
///<summary>TestViewModel</summary>
public partial class TestViewModel : ViewModelBase
{
#region props
///<summary>标题。</summary>
public string Title { get => _title; set => SetProperty(ref _title, value); }
private string _title = "测试标题";
///<summary>正文文本。</summary>
public string Content { get => _content; set => SetProperty(ref _content, value); }
private string _content = "测试文本";
#endregion
}
然后我整个 DataTemplate 来做。
<Window x:Class="Test01.Views.MainWindow"
xmlns="https://github.com/avaloniaui"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:views="clr-namespace:Test01.Views"
xmlns:vm="using:Test01.ViewModels"
Title="Test01"
Width="300"
Height="200"
Icon="/Assets/avalonia-logo.ico"
mc:Ignorable="d">
<Window.Resources>
<ResourceDictionary>
<DataTemplate x:Key="TestViewModelDataTemplate" DataType="{x:Type vm:TestViewModel}">
<Grid Width="100"
Height="100"
RowDefinitions="1*,1*">
<Border Grid.RowSpan="2"
BorderBrush="Red"
BorderThickness="1" />
<TextBlock Grid.Row="0" Text="{Binding Title}" />
<TextBlock Grid.Row="1" Text="{Binding Content}" />
</Grid>
</DataTemplate>
</ResourceDictionary>
</Window.Resources>
<Grid>
<!--<views:MainView />-->
<ContentControl Content="{x:Static vm:TestViewModel.Instance}"
ContentTemplate="{StaticResource TestViewModelDataTemplate}" />
</Grid>
</Window>
结果是这样的

6. 特定样式的可使用资源(似乎不太重要,有一些意外的地方)
在 Avalonia 中因为引入了自己的样式概念,所以在 Avalonia 中,资源有了一些归属的区分。
似乎在 Avalonia 的资源字典体系中,所有都是需要一个 x:Key,所以一个 Style 的定义可以放在 Avalonia 定义的 Styles 里面。
<Window x:Class="Test01.Views.MainWindow"
xmlns="https://github.com/avaloniaui"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:views="clr-namespace:Test01.Views"
xmlns:vm="using:Test01.ViewModels"
Title="Test01"
Width="300"
Height="200"
Icon="/Assets/avalonia-logo.ico"
mc:Ignorable="d">
<Window.Styles>
<Style Selector="TextBlock.test">
<Style.Resources>
<SolidColorBrush x:Key="ThisTextBlockSolidColorBrush" Color="Red" />
</Style.Resources>
</Style>
</Window.Styles>
<Grid>
<!--<views:MainView />-->
<TextBlock Background="{StaticResource ThisTextBlockSolidColorBrush}"
Classes="test"
Text="111" />
</Grid>
</Window>

虽然上面有指定 classes,但是我发现,即时把 Classes 给去掉,似乎也是可行。

而且居然不是 TextBlock ,而是 Button 居然也可以?!
7. 如何在同项目中的资源字典中获取资源?
为此我新建了一个资源字典名叫 ResourceDictionaries/TestResourceDictionary.axaml。
<ResourceDictionary xmlns="https://github.com/avaloniaui" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml">
<!-- Add Resources Here -->
<SolidColorBrush x:Key="TestSolidColorBrush" Color="Red" />
</ResourceDictionary>
我尝试在我的 Window.axaml 中引用,但是结果出错了。

提示是这样的。
Avalonia.Markup.Xaml.XamlLoadException:“No precompiled XAML found for ResourceDictionaries/TestResourceDictionary.axaml (baseUri: avares://Test01/Views/MainWindow.axaml), make sure to specify x:Class and include your XAML file as AvaloniaResource”

可是明明我的属性和其他 axaml 一样,都是 Avalonia XAML 呀。我在尝试寻找原因。
为什么我的 <ResourceInclude Source="ResourceDictionaries/TestResourceDictionary.axaml" /> 引用不对呢,我在寻找原因,后来我仔细看到详细的错误描述:
Unable to resolve XAML resource "avares://Test01/Designer/ResourceDictionaries/TestResourceDictionary.axaml" in the "Test01" assembly. Make sure this file exists and is public. Line 17, position 34.
为什么我的资源被认为是来自 Test01/Designer 呢,难道是我写的还不够完整吗?
于是我看了一下官方文档的示例,好家伙,还得在前面加 / 啊!
正确的写法是这样的: <ResourceInclude Source="/ResourceDictionaries/TestResourceDictionary.axaml" />,你发现改了哪里了吗?答案就是在前面加了一个斜杠 /。

8. 如何在自定义类库中获取资源字典和资源?
新建一个类库,我的 Avalonia 是 .NET 7,为此对应我建一个 .NET 7 的普通类库,注意,普通类库就可以了,我的类库叫做 TestLib01。
随后我建立了一个资源字典 ResourceDictionaries/LibTestResourceDictionary.axaml 资源字典。
<ResourceDictionary xmlns="https://github.com/avaloniaui" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml">
<!-- Add Resources Here -->
<SolidColorBrush x:Key="LibTestSolidColorBrush" Color="Blue" />
</ResourceDictionary>
然后我在 MainWindow.xaml 里面这样调用。
<Window x:Class="Test01.Views.MainWindow"
xmlns="https://github.com/avaloniaui"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:views="clr-namespace:Test01.Views"
xmlns:vm="using:Test01.ViewModels"
Title="Test01"
Width="300"
Height="200"
Icon="/Assets/avalonia-logo.ico"
mc:Ignorable="d">
<Window.Resources>
<ResourceDictionary>
<ResourceDictionary.MergedDictionaries>
<ResourceInclude Source="avares://TestLib01/ResourceDictionaries/LibTestResourceDictionary.axaml" />
</ResourceDictionary.MergedDictionaries>
</ResourceDictionary>
</Window.Resources>
<Grid>
<TextBlock Background="{StaticResource LibTestSolidColorBrush}" />
</Grid>
</Window>


浙公网安备 33010602011771号