目录
介绍
本地化、国际化和主题化
多年前,我遇到了一个用于本地化/国际化WPF应用程序的出色软件包。我成功地使用它来启用我构建的WPF应用程序,以便在英语和德语版本之间切换。
这个WPF包是由Tomer Shamam开发的,从那时起就从他以前的博客中删除了。他将代码发送给我,并在他的许可下,我将其发布在Github WPFLocalizationPackage存储库中。
最近,我再次需要国际化一个应用程序。最初,我想将该WPF包移植到Avalonia(WPF的一个出色的多平台开源后代)。最终,我决定从Tomer的WPF包中借鉴一些想法,从头开始构建一个新功能。
这个决定的主要原因是Avalonia的DynamicResource标记扩展和绑定工作得更好,并且没有困扰WPF同行的怪癖。因此,我没有创建自定义标记扩展,而是简单地利用了 Avalonia的DynamicResource。
我仍然使用Tomer的想法,即C#对象包含不同的本地化词典,可以在不同的语言环境之间轻松切换。同样重要的是,我创建了一个与他的演示非常相似的演示应用程序,以确保我的实现涵盖了他的所有功能等等。
这两个包——Tomer的原始包和我的Avalonia包——可用于更改任何控件上的任何Dependency(或Avalonia)属性,而不仅仅是与本地化相关的属性。因此,相同的功能可用于主题化或蒙皮——完全改变应用程序的外观。
我的本地化/国际化包的优点是:
- 它的使用简单——正如样本所证明的那样。
- 它的动态特性——包括大公司的解决方案在内的一些其他解决方案需要为每个语言环境进行不同的编译,而我的包(以及Tomer的WPF包)允许在应用程序运行时切换语言环境。
- 允许创建多组主题/本地化字典,每组只控制所需定制的一个“坐标”。几个示例将展示如何在几种语言和几种颜色主题之间动态切换——以便语言和颜色主题的任意组合成为可能。
运行高级演示
为了展示新主题和L10N(本地化)包的强大功能,我将从一个高级示例开始。此时不要看代码(后面会解释)。从NP.Demos.ThemingAndLocalizationDemo下载并运行演示。
这是您将看到的内容:
右上角的两个组合框允许选择语言(“英语”、“希伯来语”或“俄语”)和颜色主题(“深色”或“浅色”)。上图显示了英语和深色主题下的应用程序。
这是希伯来语和浅色主题下的应用程序视图:
这是俄罗斯/深色组合:
什么是Avalonia
Avalonia是一个出色的多平台开源UI框架,用于开发跨Windows、Mac和Linux运行的桌面解决方案。
Avalonia的功能类似于WPF,但除了多平台之外,Avalonia已经比WPF更强大且错误更少。
浏览器中的Avalonia也将于今年年底推出(请参阅Avalonia in Browser Demo中的一个小但非常令人印象深刻的演示)。
移动版Avalonia也即将推出。
一旦浏览器中的Avalonia和移动版Avalonia发布,它将比用于构建任何类型的多平台应用程序的任何竞争框架好几英里:桌面、浏览器和移动。
Avalonia教程和有关这个精彩软件包的更多信息可以在我的文章中找到:
- 在Easy Samples中使用AvaloniaUI进行多平台UI编码——第1部分——AvaloniaUI构建块
- 多平台Avalonia .NET Framework简单示例中的XAML基础知识
- 简单示例中的多平台Avalonia .NET Framework编程基本概念
- 简单示例中的多平台Avalonia .NET Framework编程高级概念
在多个平台上运行示例
本文中的所有示例均已在Windows 10、Mac Catalina和Ubuntu 20.04上进行了测试
主题/本地化代码位置
新的主题/L10N功能是NP.Avalonia.Visuals开源包的一部分。这个包也可以有其他用途,我计划写另一篇文章来解释它最重要的功能。
还有另一个项目NP.ViewModelInterfaces包含由一些Visual对象实现的非可视界面。此代码的目的是在非可视项目中使用,例如,在不引用Avalonia代码的View Model项目中控制和查询某些可视对象。到目前为止,NP.ViewModelInterfaces仅包含与主题和本地化功能相关的接口:IThemeLoader.cs和ThemeInfo.cs
Nuget包位置
Nuget包可从nuget.org的NP.Avalonia.Visuals获得。它取决于将自动安装的其他几个软件包。
您不需要单独引用Avalonia软件包,因为它们将通过安装NP.Avalonia.Visuals来安装。
如果您想从非可视(视图模型)项目中对主题/l10n功能进行一些控制,您可以只安装NP.ViewModelInterfaces也可从nuget.org获得。
主题/本地化代码示例
示例代码位置
所有Theming/L10N演示代码都可以在NP.Demos.ThemingAndL10N获得。
简单的主题示例
此示例位于NP.Demos.SimpleThemingSample下
下载它,编译并运行。这是您将看到的内容:
按顶部的“深色主题”按钮。您会看到背景变为黑色,而文本颜色(前景)变为白色:
只有顶部的两个按钮不会改变。
查看主项目中的文件:
ColorThemes文件夹下有两个XAML文件,名为DarkResources.axaml和LightResources.axaml。让我们来看看它们。这是DarkResources.axaml的内容:
<ResourceDictionary xmlns="https://github.com/avaloniaui"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml">
<!-- Defing Background and Foreground brushes for dark theme -->
<SolidColorBrush x:Key="BackgroundBrush"
Color="Black"/>
<SolidColorBrush x:Key="ForegroundBrush"
Color="White"/>
</ResourceDictionary>
这是LightResources.axaml:
<ResourceDictionary xmlns="https://github.com/avaloniaui"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml">
<!-- Defing Background and Foreground brushes for light theme -->
<SolidColorBrush x:Key="BackgroundBrush"
Color="White"/>
<SolidColorBrush x:Key="ForegroundBrush"
Color="Black"/>
</ResourceDictionary>
它们包含具有相同键和相反颜色的Avalonia资源——在DarkResources.axaml中 BackgroundBrush是'Black'而ForegroundBrush是白色——而在LightResources.axaml中则相反。
在通常的主要解决方案文件中,只有App.axaml、MainWindow.axaml和MainWindow.axaml.cs有一些不平凡的变化。
查看App.axaml的内容:
<Application xmlns="https://github.com/avaloniaui"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:np="https://np.com/visuals"
x:Class="NP.Demos.SimpleThemingSample.App">
<Application.Resources>
<ResourceDictionary>
<ResourceDictionary.MergedDictionaries>
<!-- define the Theme loader with two themes - Dark and Light -->
<np:ThemeLoader Name="ColorThemeLoader"
SelectedThemeId="Light"> <!-- Set original theme to Light -->
<np:ThemeInfo Id="Dark"
ResourceUrl="/ColorThemes/DarkResources.axaml"/>
<np:ThemeInfo Id="Light"
ResourceUrl="/ColorThemes/LightResources.axaml"/>
</np:ThemeLoader>
</ResourceDictionary.MergedDictionaries>
</ResourceDictionary>
</Application.Resources>
<!-- Default Avalonia Styles -->
<Application.Styles>
<StyleInclude Source="avares://Avalonia.Themes.Default/Accents/BaseDark.xaml"/>
<StyleInclude Source="avares://Avalonia.Themes.Default/DefaultTheme.xaml"/>
</Application.Styles>
</Application>
完成定义ThemeLoader的技巧的重要代码包含在ResourceDictionary.MergeDictionaries标记中:
<!-- define the Theme loader with two themes - Dark and Light -->
<np:ThemeLoader Name="ColorThemeLoader"
SelectedThemeId="Light"> <!-- Set original theme to Light -->
<np:ThemeInfo Id="Dark"
ResourceUrl="/ColorThemes/DarkResources.axaml"/>
<np:ThemeInfo Id="Light"
ResourceUrl="/ColorThemes/LightResources.axaml"/>
</np:ThemeLoader>
ThemeLoader本质上是一个可以交换其内容的智能ResourceDictionary。
我们使用ThemeInfo对象定义了两个ThemeLoader主题。第一个ThemeInfo对象指定了深色主题——它的Id是"Dark"并且它的ResourceUrl="/ColorThemes/DarkResources.axaml"被设置为指向上述DarkResources.axaml文件。第二个ThemeInfo对象通过ResourceUrl指向LightResource.asaml文件来指定浅色主题。它的Id是"Light"。
主题加载器可以通过更改其SelectedThemeId来交换其资源内容。最初,它被设置为"Light"第二个ThemeInfo对象的Id,所以它在应用程序启动时加载,我们让应用程序在浅色主题下运行。
我们将我们的ThemeLoader属性Name设置为“ColorThemeLoader”。通过这个名称,我们将能够在后面的MainWindow.axaml.cs代码中找到这个加载器。
按下按钮“Dark Theme”将导致后面的代码(在MainWindow.axaml.cs文件中)将我们ThemeLoader的“SelectedThemeId”更改为“ Dark”,以便应用程序更改其颜色。
MainWindow.axaml文件非常简单:
<Window ...
Background="{DynamicResource BackgroundBrush}"
Width="300"
Height="200">
<Grid RowDefinitions="Auto, *"
Margin="10">
<StackPanel Orientation="Horizontal">
<!-- this button switches to light theme -->
<Button x:Name="LightButton"
Content="Light Theme"
Margin="0,0,10,0"/>
<!-- this button switches to dark theme -->
<Button x:Name="DarkButton"
Content="Dark Theme"
Margin="0,0,0,0"/>
</StackPanel>
<TextBlock Text="Hello World from Avalonia !!!"
Grid.Row="1"
HorizontalAlignment="Center"
VerticalAlignment="Center"
FontSize="20"
Foreground="{DynamicResource ForegroundBrush}"/>
</Grid>
</Window>
MainWindow's Background属性使用DynamicResource标记扩展连接到BackgroundBrush资源(在深色主题下指向黑色,在浅色主题下指向白色):Background="{DynamicResource BackgroundBrush}"
TextBlock's Foreground使用相同的方法来获取ForegroundBrush资源的值:Foreground="{DynamicResource ForegroundBrush}".
以下是MainWindow.axaml.cs文件内容的高亮代码:
public partial class MainWindow : Window
{
// reference to ThemeLoader object defined
// in XAML
private ThemeLoader _themeLoader;
public MainWindow()
{
InitializeComponent();
...
// find the theme loader by its name
_themeLoader =
Application.Current.Resources.GetThemeLoader("ColorThemeLoader")!;
// set the handler for lightButton's click event
Button lightButton = this.FindControl<Button>("LightButton");
lightButton.Click += LightButton_Click;
// set the handler for darkButton's click event
Button darkButton = this.FindControl<Button>("DarkButton");
darkButton.Click += DarkButton_Click;
}
private void LightButton_Click(object? sender,
global::Avalonia.Interactivity.RoutedEventArgs e)
{
// set the theme to Light
_themeLoader.SelectedThemeId = "Light";
}
private void DarkButton_Click(object? sender,
global::Avalonia.Interactivity.RoutedEventArgs e)
{
// set the theme to Dark
_themeLoader.SelectedThemeId = "Dark";
}
...
}
首先,我们通过将其名称传递给GetThemeLoader扩展方法来获取在XAML中定义的ThemeLoader对象:
// find the theme loader by its name
_themeLoader =
Application.Current.Resources.GetThemeLoader("ColorThemeLoader")!;
接下来,我们得到XAML中定义的LightButton和DarkButton的引用,并为它们Click事件设置处理程序:
// set the handler for lightButton's click event
Button lightButton = this.FindControl<Button>("LightButton");
lightButton.Click += LightButton_Click;
// set the handler for darkButton's click event
Button darkButton = this.FindControl<Button>("DarkButton");
darkButton.Click += DarkButton_Click;
在每个处理程序中,我们相应地为亮和暗按钮设置SelectedThemeId为string "Light"或"Dark":
private void LightButton_Click(object? sender, RoutedEventArgs e)
{
// set the theme to Light
_themeLoader.SelectedThemeId = "Light";
}
private void DarkButton_Click(object? sender, RoutedEventArgs e)
{
// set the theme to Dark
_themeLoader.SelectedThemeId = "Dark";
}
带有样式更改的简单主题
前面的示例展示了如何更改背景和文本颜色。然而,顶部的“Light Theme”和“Dark Theme”按钮并没有改变——它们仍然是深色的,因为在我们定义<StyleInclude Source="avares://Avalonia.Themes.Default/Accents/BaseDark.xaml">的App.axaml文件的<Styles>...</Styles>部分中,当主题改变时,对BaseDark.xaml样式的引用没有改变。
此示例的目的是展示当我们更改主题时如何更改对BaseLight.axaml文件的引用,该文件也可从Avalonia获得。
此演示的代码可在NP.Demos.SimpleThemingSampleWithStyleChange获得。
编译并运行演示——这是您将看到的:
请注意,按钮是轻的。然后按“深色主题”按钮——主题改变,按钮也改变:
与上一个示例的唯一代码差异位于App.asaml文件中:
<Application xmlns="https://github.com/avaloniaui"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:np="https://np.com/visuals"
x:Class="NP.Demos.SimpleThemingSample.App">
<Application.Resources>
<ResourceDictionary>
<ResourceDictionary.MergedDictionaries>
<!-- define the Theme loader with two themes - Dark and Light -->
<np:ThemeLoader Name="ColorThemeLoader"
SelectedThemeId="Light"
StyleResourceName="ColorLoaderStyles">
<!-- to refer to the style by StyleReference-->
<np:ThemeInfo Id="Dark"
ResourceUrl="/ColorThemes/DarkResources.axaml"
StyleUrl="avares://Avalonia.Themes.Default/Accents/BaseDark.xaml"/>
<!-- refers to dark styles -->
<np:ThemeInfo Id="Light"
ResourceUrl="/ColorThemes/LightResources.axaml"
StyleUrl="avares://Avalonia.Themes.Default/Accents/BaseLight.xaml"/>
<!-- refers to light styles -->
</np:ThemeLoader>
</ResourceDictionary.MergedDictionaries>
</ResourceDictionary>
</Application.Resources>
<Application.Styles>
<!-- reference to the styles defined within the ThemeLoader-->
<np:StyleReference TheStyle="{StaticResource ColorLoaderStyles}"/>
<!-- Default Avalonia Style -->
<StyleInclude Source="avares://Avalonia.Themes.Default/DefaultTheme.xaml"/>
</Application.Styles>
</Application>
请注意,每个ThemeInfo对象都在前面的示例中解释了顶部Id和ResourceUrl属性上定义了StyleUrl:
<np:ThemeInfo Id="Dark"
ResourceUrl="/ColorThemes/DarkResources.axaml"
StyleUrl="avares://Avalonia.Themes.Default/Accents/BaseDark.xaml"/>
<!-- refers to dark styles -->
<np:ThemeInfo Id="Light"
ResourceUrl="/ColorThemes/LightResources.axaml"
StyleUrl="avares://Avalonia.Themes.Default/Accents/BaseLight.xaml"/>
<!-- refers to light styles -->
“Dark”主题对应的ThemeInfo对象指向“BaseDark.xaml”文件,而定义“Light”主题的另一个ThemeInfo对象指向BaseLight.xaml。
主题加载器现在定义了一个属性StyleResourceName:StyleResourceName="ColorLoaderStyles">。选择此属性值时应避免与ThemeLoader中包含的资源的任何资源键发生冲突。StyleReference对象使用此值来引用我们ThemeLoader选择的依赖于主题的Style:
<Application.Styles>
<!-- reference to the styles defined within the ThemeLoader-->
<np:StyleReference TheStyle="{StaticResource ColorLoaderStyles}"/>
...
</Application.Styles>
更改主题和语言示例
此示例的目的是演示一个支持独立更改其颜色主题和语言的应用程序,以便支持颜色主题和语言的每种组合。
此演示的代码可在以下URL中找到:NP.Demos.SimpleThemingAndL10NSample。
以下是下载、编译和运行示例后您将看到的内容:
按下“深色主题”按钮会将颜色主题更改为“深色”:
按“希伯来语”按钮会将文本更改为希伯来语:
按下“Light Theme”按钮将再次将颜色主题更改为“Light ”:
查看演示应用程序中的项目文件:
此示例有两组字典:
- ColorThemes文件夹下的DarkResources.axaml和LightResources.axaml
- LanguageDictionaries文件夹下的EnglishDictionary.axaml和HebrewDictionary.axaml
颜色主题文件——DarkResources.axaml和LightResources.axaml与前面的示例完全相同。
看看EnglishDictionary.axaml:
<ResourceDictionary xmlns="https://github.com/avaloniaui"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml">
<x:String x:Key="WindowTitle">Theming Demo</x:String>
<x:String x:Key="WelcomeText">Hello World from Avalonia !!!</x:String>
<x:String x:Key="WindowTitleText">Window Title is '{0}'</x:String>
</ResourceDictionary>
它是一个非常简单的字典文件,它定义了三种 string资源——“WindowTitle”、“WelcomeText”和“WindowTitleText”。
HebrewDictionary.axaml定义了相同的资源,但它们的值被翻译成希伯来语。
WindowTitle控制Window的标题,WelcomeText是窗口内显示的文本,WindowTitleText也作为第二行显示在窗口内。我添加它以显示使用Avalonia绑定的文本的动态替换。请注意,WindowTitleText的文本“Window Title is '{0}'”具有“{0}”部分,该部分将替换为窗口的标题。下面将解释它是如何实现的。
打开App.axaml文件。它定义了两个ThemeLoaders——一个用于颜色主题,另一个用于语言:
<Application.Resources>
<ResourceDictionary>
<ResourceDictionary.MergedDictionaries>
<!-- define the Theme loader with two themes - Dark and Light -->
<np:ThemeLoader Name="ColorThemeLoader"
SelectedThemeId="Light"
StyleResourceName="ColorLoaderStyles">
<!-- to refer to the style by StyleReference-->
<np:ThemeInfo Id="Dark"
ResourceUrl="/ColorThemes/DarkResources.axaml"
StyleUrl="avares://Avalonia.Themes.Default/Accents/BaseDark.xaml"/>
<!-- refers to dark styles -->
<np:ThemeInfo Id="Light"
ResourceUrl="/ColorThemes/LightResources.axaml"
StyleUrl="avares://Avalonia.Themes.Default/Accents/BaseLight.xaml"/>
<!-- refers to light styles -->
</np:ThemeLoader>
<np:ThemeLoader Name="LanguageLoader"
SelectedThemeId="English">
<!-- to refer to the style by StyleReference-->
<np:ThemeInfo Id="English"
ResourceUrl="/LanguageDictionaries/EnglishDictionary.axaml"/>
<!-- refers to dark styles -->
<np:ThemeInfo Id="Hebrew"
ResourceUrl="/LanguageDictionaries/HebrewDictionary.axaml"/>
<!-- refers to light styles -->
</np:ThemeLoader>
</ResourceDictionary.MergedDictionaries>
</ResourceDictionary>
</Application.Resources>
ColorThemeLoader默认选择"Light"和LanguageLoader "English"(SelectedThemeId="English")。
现在打开MainWindow.asaml文件:
<Window ...
Title="{DynamicResource WindowTitle}"
Background="{DynamicResource BackgroundBrush}"
...
>
<Grid RowDefinitions="Auto, *"
Margin="10">
<StackPanel Orientation="Horizontal">
<!-- this button switches to light theme -->
<Button x:Name="LightButton"
Content="Light Theme"
Margin="0,0,10,0"/>
<!-- this button switches to dark theme -->
<Button x:Name="DarkButton"
Content="Dark Theme"
Margin="0,0,10,0"/>
<!-- this button switches to English language-->
<Button x:Name="EnglishButton"
Content="English"
Margin="0,0,10,0"/>
<!-- this button switches to Hebrew language-->
<Button x:Name="HebrewButton"
Content="Hebrew"
Margin="0,0,10,0"/>
</StackPanel>
<StackPanel HorizontalAlignment="Center"
VerticalAlignment="Center"
Grid.Row="1">
<TextBlock Text="{DynamicResource WelcomeText}"
...
Foreground="{DynamicResource ForegroundBrush}"
Margin="0,0,0,10"/>
<TextBlock HorizontalAlignment="Center"
...
Foreground="{DynamicResource ForegroundBrush}"
Margin="0,0,0,10">
<TextBlock.Text> <!-- Use multibinding to format the string -->
<MultiBinding Converter="{x:Static np:StringFormatConverter.Instance}">
<!-- pass the main string from a language dictionary -->
<DynamicResourceExtension ResourceKey="WindowTitleText"/>
<!-- pass window title as a string parameter -->
<Binding Path="Title"
RelativeSource="{RelativeSource AncestorType=Window}"/>
</MultiBinding>
</TextBlock.Text>
</TextBlock>
</StackPanel>
</Grid>
</Window>
Window标签将其Title设置为WindowTitle, Background 设置为来自主题/本地化字典的BackgroundBrush:
Title="{DynamicResource WindowTitle}"
Background="{DynamicResource BackgroundBrush}"
顶部定义了四个按钮:两个按钮用于深色和浅色主题,两个按钮用于英语和希伯来语。
TextBlocks将它们Text和Foreground属性设置为动态设置为语言和颜色字典中定义的资源,例如,
Text="{DynamicResource WelcomeText}"
Foreground="{DynamicResource ForegroundBrush}"
第二个TextBlock有一种巧妙的方式使用MultiBinding来绑定其文本——将单个目标绑定到多个源。这样做是为了扩展string自动插入的Window's Title属性值:
<TextBlock.Text> <!-- Use multibinding to format the string -->
<MultiBinding Converter="{x:Static np:StringFormatConverter.Instance}">
<!-- pass the main string from a language dictionary -->
<DynamicResourceExtension ResourceKey="WindowTitleText"/>
<!-- pass window title as a string parameter -->
<Binding Path="Title"
RelativeSource="{RelativeSource AncestorType=Window}"/>
</MultiBinding>
</TextBlock.Text>
我们使用NP.Avalonia.Visuals包中StringFormatConverter定义的。它将第一个string作为格式,其余作为参数并调用它们的string.Format(string format, params object[] args)方法。多重绑定中的第一个绑定由DynamicResourceExtension提供(是的,在Avalonia中——DynamicResource只是一个绑定,因此它可以插入到MultiBinding中作为它的Binding子元素之一)。此绑定返回string格式(对于英语,应该是"Window Title is '{0}'")。
第二个绑定返回要插入第一个string的窗口的标题(代替“{0}”)。
现在看一下MainWindow.axaml.cs文件,它与上一个示例的同名文件非常相似,只是在这里,我们定义了两个ThemeLoader对象(_colorThemeLoader和_languageThemeLoader)并将处理程序分配给4个而不是2个按钮的Click事件:
public partial class MainWindow : Window
{
private ThemeLoader _colorThemeLoader;
private ThemeLoader _languageThemeLoader;
public MainWindow()
{
InitializeComponent();
...
// find the color theme loader by name
_colorThemeLoader =
Application.Current.Resources.GetThemeLoader("ColorThemeLoader")!;
// find the language theme loader by name
_languageThemeLoader =
Application.Current.Resources.GetThemeLoader("LanguageLoader")!;
Button lightButton = this.FindControl<Button>("LightButton");
lightButton.Click += LightButton_Click;
Button darkButton = this.FindControl<Button>("DarkButton");
darkButton.Click += DarkButton_Click;
Button englishButton = this.FindControl<Button>("EnglishButton");
englishButton.Click += EnglishButton_Click;
Button hebrewButton = this.FindControl<Button>("HebrewButton");
hebrewButton.Click += HebrewButton_Click;
}
private void LightButton_Click(object? sender, RoutedEventArgs e)
{
// set the theme to Light
_colorThemeLoader.SelectedThemeId = "Light";
}
private void DarkButton_Click(object? sender, RoutedEventArgs e)
{
// set the theme to Dark
_colorThemeLoader.SelectedThemeId = "Dark";
}
private void EnglishButton_Click(object? sender, RoutedEventArgs e)
{
// set language to English
_languageThemeLoader.SelectedThemeId = "English";
}
private void HebrewButton_Click(object? sender, RoutedEventArgs e)
{
// set language to Hebrew
_languageThemeLoader.SelectedThemeId = "Hebrew";
}
...
}
高级演示代码
我们已经在本文的介绍部分展示了高级演示,所以这里我们只讨论代码(位于NP.Demos.ThemingAndLocalizationDemo)。
从概念上讲,高级演示的代码除了上面简单示例中讨论的内容之外并没有包含太多新内容。更多属性被本地化,包括窗口和控件大小、应用程序流程(希伯来语是从右到左书写和查看的)、水平对齐等。此外,RussianResources.asaml文件已添加到LanguageDictionaries中。
此演示中使用的一个功能值得特别讨论。该演示使用DynamicResourceBinding对象,例如,
<TextBlock.Text>
<MultiBinding Converter="{x:Static np:StringFormatConverter.Instance}">
<np:DynamicResourceBinding Path="Uid"/>
<Binding Path="ID"/>
</MultiBinding>
</TextBlock.Text>
DynamicResourceBinding结合了Binding和DynamicResource的功能。它绑定到一个string或一个对象,然后将其用作DynamicResource的资源键。当提供资源键的属性或资源键指向的动态资源改变它们的值时,DynamicResourceBinding返回值将会改变。
我创建DynamicResourceBinding是为了匹配Tomer的自定义标记扩展提供的一些功能,但它对于自定义应用程序也非常有用。
我将在以后专门讨论NP.Avalonia.Visuals包功能的文章中详细介绍DynamicResourceBinding。
结论
本文解释并提供了我在多平台桌面应用程序中构建和使用的基于Avalonia的主题化/本地化功能的详细示例。
此功能的灵感来自于Tomer Shamam编写的旧WPF包。
此功能是作为NP.Avalonia.Visuals nuget包和Github存储库的一部分在最简单和最宽松的MIT许可证下发布的——这本质上意味着您可以在任何应用程序中使用它,无论是否商业,只要您不责怪作者(我)可能的错误并提供简短的归因。
此处描述的所有示例均在Windows 10、Mac Catalina和Ubuntu 20.04机器上进行了测试。
https://www.codeproject.com/Articles/5317972/Theming-and-Localization-Functionality-for-Multipl