wpf chartcontrol 绑定数据_DotNetCore 3.0 助力 WPF本地化

概览

随着我们的应用程序越来越受欢迎,我们的下一步将要开发多语言功能。方便越来越多的国家使用我们中国的应用程序,基于 WPF 本地化,我们很多时候使用的是系统资源文件,可是动态切换本地化,就比较麻烦了。
有没有一种方法既可以适用系统的资源文件,又能方便快捷的切换本地化呢?

实现思路

现在我们将要实现的是基于 DotNetCore 3.0 以上版本 and WPF 桌面应用程序模块化的多语言功能。动态切换多语言思路:

  • 把所有模块的资源文件添加到字典集合。

  • 将资源文件里的key,绑定到前台。

  • 通过通知更改 CurrentCulture 多语言来使用改变的语言文件里的key。

  • 通过绑定 Binding 拼接Path 在输出。

动态切换

我们先来看实现结果aa9dc58fa4c98e959fa9298650224ca5.gif

  • 第一行是我们的主程序的数据展示,用于业务中的本地化

  • 第二行是我们业务模块A的数据展示

  • 第三行是我们业务模块B的数据展示

来看一下xaml展示7eff85c846af6f208aaa697dffd1ea32.png

通过ComboBox选择来切换语言d496e09e1d370a1828ccdf8cb9042f2d.png

搭建模拟业务项目

创建一个WPF App(.NET Core)应用程序e8273a53b6f90e7d770e0780666fdd77.png

创建完成后,我们需要引入业务A模块及业务B模块和业务帮助模块861a49dde6cef67303361bd74e4f772a.png
PS:根据自己的业务需要来完成项目的搭建。本教程完全适配多语言功能。

使用ResX资源文件

在各个模块里添加Strings 文件夹用来包含 各个国家和地区的语言文件。

e49421e1f3cc8751ffc0eb36cfc2d0a2.png

多语言可以参考:https://github.com/UnRunDeaD/WPF---Localization/blob/master/ComboListLanguages.txt

资源文件可以放在任意模块内,比如业务模块A ,主程序,底层业务,控件工具集等

创建各个业务模块资源文件

Strings文件夹可以任意命名
SR资源文件可以任意命名b036fd9f431f3f8ab731023cf424af18.png

帮助类

封装到底层供各个模块调用

    public class TranslationSource : INotifyPropertyChanged
{
public static TranslationSource Instance { get; } = new TranslationSource();

private readonly Dictionary<string, ResourceManager> resourceManagerDictionary = new Dictionary<string, ResourceManager>();

public string this[string key]
{
get
{
Tuple<string, string> tuple = SplitName(key);
string translation = null;
if (resourceManagerDictionary.ContainsKey(tuple.Item1))
translation = resourceManagerDictionary[tuple.Item1].GetString(tuple.Item2, currentCulture);
return translation ?? key;
}
}

private CultureInfo currentCulture = CultureInfo.InstalledUICulture;
public CultureInfo CurrentCulture
{
get { return currentCulture; }
set
{
if (currentCulture != value)
{
currentCulture = value;

PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(string.Empty));
}
}
}


public event PropertyChangedEventHandler PropertyChanged;

public void AddResourceManager(ResourceManager resourceManager){
if (!resourceManagerDictionary.ContainsKey(resourceManager.BaseName))
{
resourceManagerDictionary.Add(resourceManager.BaseName, resourceManager);
}
}

public static Tuple<string, string> SplitName(string local){
int idx = local.ToString().LastIndexOf(".");
var tuple = new Tuple<string, string>(local.Substring(0, idx), local.Substring(idx + 1));
return tuple;
}
}

public class Translation : DependencyObject
{
public static readonly DependencyProperty ResourceManagerProperty =
DependencyProperty.RegisterAttached("ResourceManager", typeof(ResourceManager), typeof(Translation));

public static ResourceManager GetResourceManager(DependencyObject dependencyObject){
return (ResourceManager)dependencyObject.GetValue(ResourceManagerProperty);
}

public static void SetResourceManager(DependencyObject dependencyObject, ResourceManager value){
dependencyObject.SetValue(ResourceManagerProperty, value);
}
}

public class LocExtension : MarkupExtension
{
public string StringName { get; }

public LocExtension(string stringName){
StringName = stringName;
}

private ResourceManager GetResourceManager(object control){
if (control is DependencyObject dependencyObject)
{
object localValue = dependencyObject.ReadLocalValue(Translation.ResourceManagerProperty);


if (localValue != DependencyProperty.UnsetValue)
{
if (localValue is ResourceManager resourceManager)
{
TranslationSource.Instance.AddResourceManager(resourceManager);

return resourceManager;
}
}
}

return null;
}

public override object ProvideValue(IServiceProvider serviceProvider){

object targetObject = (serviceProvider as IProvideValueTarget)?.TargetObject;

if (targetObject?.GetType().Name == "SharedDp")
return targetObject;

string baseName = GetResourceManager(targetObject)?.BaseName ?? string.Empty;

if (string.IsNullOrEmpty(baseName))
{

object rootObject = (serviceProvider as IRootObjectProvider)?.RootObject;
baseName = GetResourceManager(rootObject)?.BaseName ?? string.Empty;
}

if (string.IsNullOrEmpty(baseName))
{
if (targetObject is FrameworkElement frameworkElement)
{
baseName = GetResourceManager(frameworkElement.TemplatedParent)?.BaseName ?? string.Empty;
}
}

Binding binding = new Binding
{
Mode = BindingMode.OneWay,
Path = new PropertyPath($"[{baseName}.{StringName}]"),
Source = TranslationSource.Instance,
FallbackValue = StringName
};

return binding.ProvideValue(serviceProvider);
}
}
前台绑定

05a63c659146bce7d284d694ac95d776.png


xmlns:ext="clr-namespace:WpfUtil.Extension;assembly=WpfUtil"

xmlns:resx="clr-namespace:ModuleA.Strings"



ext:Translation.ResourceManager="{x:Static resx:SR.ResourceManager}"

显示文字

//读取资源文件里的键值

<Label Content="{ext:Loc Test}" FontSize="21" />
后台实现

根据业务的需要,我们在界面上无法适用静态文字显示的,一般通过后台代码来完成,对于 code-behind 的变量使用,同样可以应用于资源字典。
比如在业余模块代码段里的模拟实现

Copy



Message = string.Format(SR.ResourceManager.GetString("Message",Thread.CurrentThread.CurrentUICulture),System.DateTime.Now);

PS: 欢迎各位大佬慷慨指点,有不足之处,请指出!有疑问,请指出,喜欢它,请支持!

下载地址

https://github.com/androllen/WpfNetCoreLocalization

相关链接

https://github.com/Jinjinov/wpf-localization-multiple-resource-resx-one-language/blob/master/README.md
https://codinginfinity.me/post/2015-05-10/localization_of_a_wpf_app_the_simple_approach

原文链接:https://www.cnblogs.com/luquanmingren/p/11384104.html

.NET社区新闻,深度好文,欢迎访问公众号文章汇总 http://www.csharpkit.com 

c7ba802087e8c11a4ed338ada117b8da.png

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值