Avalonia 在 C# 中获得 ResourceDictionary 资源字典和 DataTemplate 数据模板对象的通用方法

Avalonia 在 C# 中获得 ResourceDictionary 资源字典和 DataTemplate 数据模板对象的通用方法

一、前言

我们在进行一些动态构造的时候,其中一些函数参数要求传递 IDataTemplate 的对象,而 DataTemplate 作为一个以 xaml 定义的对象,我们一般是无法在 C# 中访问到的。
和 WPF 不一样,Avalonia 的 ResourceDictionary 的实例化也并不容易,特别是如果我们的处理是在 ViewModel 的地方时,你知道的,ViewModel 和 View 的分层和独立让 ViewModel 在框架设计上完全无法触摸 View 的分毫。

本文要介绍的是在 Avalonia 的 C# 编码部分如何获得 DataTemplate 对象的解决方法。

二、从创建资源字典开始

1. 资源字典的创建

我们使用的是 Visual Studio 2022,我尚不清楚如果你使用的是 Visual Studio Code,你该如何创建一个资源字典。关于资源的创建,其实我在 https://www.cnblogs.com/fanbal/articles/18186938 中有提到,但是为了你阅读方便,我们还是在这里也提供一个示例。

我们的教程项目是一个干净的 Avalonia 空白项目,项目的名字叫做 HowGetResDict。

它的结构是这样的:

一个空白模板应该有的样子,具有Assets、Views、ViewModels这三个文件夹

请在项目中创建一个名为 ResourceDictionaries 的文件夹,然后在文件夹中新建资源字典项:

一个具有自定义模板的新文件夹

如果你不知道怎么新建资源字典项,你可以参考这个界面:

选择 Avalonia 目录的第一项

2. 资源字典的内容

资源字典中的内容非常简单,实际上就只有一个表达 TextBlock 的 DataTemplate。

<ResourceDictionary xmlns="https://github.com/avaloniaui" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml">
    <DataTemplate x:Key="TestDataTemplate">
        <TextBlock Text="1111" />
    </DataTemplate>
</ResourceDictionary>

我当然欢迎和推荐你在这里填入更多东西用来测试,如果你对 WPF 很熟悉的话,你会知晓,其实 DataTemplate 和 double、FontFamaily 等是没有太大差异的,我在之前写 WPF 的时候,喜欢将 Style 样式的 ControlTemplate 拆出来,Style 样式的部分以 StaticResource 绑定的形式给 Setter ,用以减少资源字典的缩进。

但是代价就是你必须为拆除来的 ControlTemplate 进行独立的命名,命名是一件很繁琐的事情,所以在后来我和缩进妥协了,选择最开始在 Style 中 Setter.Value 内部嵌入 ControlTemplate 的风格,毕竟编辑器能够折叠,如果将它们拆开,虽然会减少缩进但是会让折叠量翻倍,于是作罢。

三、在 App 中定义

在 App 中定义有两种风格:Avalonia XAML 和 AvaloniaResource

1. Avalonia XAML

默认的情况是使用 Avalonia XAML 进行资源的加载的。

在这种情况下你在 App.axaml 中的写法可以参考这个样子:

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

    <Application.Styles>
        <FluentTheme />
    </Application.Styles>

    <Application.Resources>
        <ResourceDictionary>
            <ResourceDictionary.MergedDictionaries>
                <ResourceInclude Source="/ResourceDictionaries/MyResourceDictionary.axaml" />
            </ResourceDictionary.MergedDictionaries>
        </ResourceDictionary>
    </Application.Resources>

</Application>

具体请看<ResourceInclude Source="/ResourceDictionaries/MyResourceDictionary.axaml" /> 这一块内容。

Source URL 书写的时候有斜杠////////////不要看漏了!!!

2. AvaloniaResource

我其实会推荐你使用 AvaloniaResource 来进行填写,因为这种做法不会漏斜杠。

我们来看一下写法

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

    <Application.Styles>
        <FluentTheme />
    </Application.Styles>

    <Application.Resources>
        <ResourceDictionary>
            <ResourceDictionary.MergedDictionaries>
                <ResourceInclude Source="avares://HowGetResDict/ResourceDictionaries/MyResourceDictionary.axaml" />
            </ResourceDictionary.MergedDictionaries>
        </ResourceDictionary>
    </Application.Resources>

</Application>

和上面的代码片段的差异同样在 ResourceInclude 的部分,这块内容变为了
<ResourceInclude Source="avares://HowGetResDict/ResourceDictionaries/MyResourceDictionary.axaml" />

在这种情况下你需要将资源字典的属性进行一些修改,修改其中的生成操作:

简体中文中叫做生成操作

四、在 C# 中获得 ResourceDictionary 资源字典 和定义了的 DataTemplate 数据模板的方法

思路上是通过获取全局的 App 单例对象来实现 xaml 和 C# 的连接,如何获得 App 你当然可以有很多中方法,是目前最简单的通过 App 静态对象也好,还是使用你注册得到的容器来获得相应的单例也好,这边代码的意思是希望你能通过 App 来解决问题,至于怎么拿到 App,各位可以各显神通。

1. 错误的写法

请不要使用直接访问索引器的方式、不要使用类似 resDict["XXX"] 的方式来访问。
大概率,这是访问不到的,此乃我的经验和观察。


private void Test()
{
    var topResources = App.Current.Resources;

    // 别用这种方式,索引器在这里是无效的。
    var testDataTemplate = topResources["TestDataTemplate"] as IDataTemplate;
}

你看,确实是空的, null,找不着吧。

通过调试可以看到其中返回的确实是 null

2. 正确的做法

请使用 TryGetResource 来进行访问:

private void Test()
{
    var topResources = App.Current.Resources;

    if (topResources.TryGetResource("TestDataTemplate", theme: null, out var outDataTemplate))
    {
        var dataTemplate = outDataTemplate as IDataTemplate;
    }
}

像是这样子就可以拿到我们想要的 DataTemplate 对象了。

通过调试可以看到确实有拿到对象

五、总结

如果你想要拿到写在 ResourceDictionary 资源字典的 DataTemplate 资源字典,你需要经过以下的一些步骤:

  1. 确保你的 ResourceDictionary 资源字典里面确实写了一个合法的 DataTemplate 资源字典;
  2. 在 App.axaml 中引入你的 ResourceDictionary 资源字典,以正确的方式提供 Source URL;
  3. C# 代码的部分获得最顶层的 App 对象,然后以正确的方式调用。
posted @ 2025-03-04 11:42  fanbal  阅读(315)  评论(0)    收藏  举报