说一下IHandle<T>实现多语言功能
因为Caliburn.Micro是基于MvvM的UI与codebehind分离,
binding可以是双向的所以我们想动态的实现多语言切换很是方便今天我做一个小demo给大家提供一个思路
先看一下效果
点击英文 变成英文状态点chinese就会变成中文
源码的下载地址在文章的最下边
多语言用的是资源文件建一个MyLanguage的资源文件再添加一个MyLanguage.en-US的资源文件如果你还想要
其它的语言可自己添加。两个资源文件里写上你要的文本如下图这样,它们的名称是一样的只是值一个是中文一个是英文
下面我们就要开始用Caliburn.Micro的IHandle<T>去实现多语言了
先写一个资源的接口
public interface IResource { string GetString(string name); CultureInfo CurrentCulture { set; } } public interface IResourceTask { void ChangeLanguage(string language); string GetString(string name); event EventHandler LanguageChanged; }
IResource接口是资源要实现的,GetString(stirng name)方法是得到根据名字得到资源里的值
CurrentCulture是中英文语言转换的
ResourceTask接口是一个管理接口它管理资源的我们通过它去实现
语言转换时把发送广播把页面上的所有文字转换成想要的语言。
再写一个简单的信息接口,也就是我们发送广播时的数据格式
public interface IMessage { } public class LanguageChangedMessage : IMessage { }
LanguageChangedMessage就是我们要发送广播的数据格式
下面就来实现一下IResourceTask接口
public class ResourceTask : IResourceTask { public ResourceTask() { System.Data.DataTable _dt = new System.Data.DataTable(); } [ImportMany] public IResource[] Resources { get; set; } public void ChangeLanguage(string language) { CultureInfo culture = new CultureInfo(language); Thread.CurrentThread.CurrentCulture = culture; Thread.CurrentThread.CurrentUICulture = culture; Resources.Apply(item => item.CurrentCulture = culture); IEventAggregator eventAggregator = IoC.Get<IEventAggregator>(); eventAggregator.Publish(new LanguageChangedMessage()); if (LanguageChanged != null) { LanguageChanged(this, null); } } public event EventHandler LanguageChanged; public string GetString(string name) { string str = null; foreach (var resource in Resources) { str = resource.GetString(name); if (str != null) { break; } } return str; } }
通过Resources得到所有export IResource的类
ChangeLanguage(string language)方法里的
Resources.Apply(item => item.CurrentCulture = culture);是把所有实现IResult类的CurrentCulture修改成我们要换成的语言格式
eventAggregator.Publish(new LanguageChangedMessage()); 就是去发送广播,把页面上所有的的文字切换
EventHandler LanguageChanged;事件是如果我们还想切换完语言后做一些事件就可以写在这个事件里
再写一个实现 IResult的类
[Export(typeof(IResource))] [PartCreationPolicy(CreationPolicy.NonShared)] public class MyResource : IResource { private ResourceManager stringResource; private CultureInfo culture = new CultureInfo("zh-cn"); public CultureInfo CurrentCulture { get { return culture; } set { culture = value; } } public MyResource() { stringResource = new ResourceManager("WPFMultLanguage.Resource.MyLanguage", typeof(MyResource).Assembly); } public string GetString(string name) { return stringResource.GetString(name, culture); } }
ResourceManager 可以对我们前边写的两种语言的资源文件的读写
在类初始化的时候我们给出资源文件的路径
在GetString(string name)里我们就可以通过ResourceManager根据当前的culture去读取资源文件里的字符了
接下来的问题就是我们怎么去通过接收广播把页面上把文字切换
我们写一个 XAML 标记扩展类
public class MyResourceExtension : MarkupExtension, INotifyPropertyChanged, IHandle<LanguageChangedMessage> { public string Key { get; set; } public string Value { get { if (Key == null) { return null; } IResourceTask result = IoC.Get<IResourceTask>(); string s = result.GetString(Key); return s; } } public MyResourceExtension() { if (!Execute.InDesignMode) { IoC.Get<IEventAggregator>().Subscribe(this); } } public event PropertyChangedEventHandler PropertyChanged; public void PropertyChanted() { if (PropertyChanged != null) { PropertyChanged(this, new PropertyChangedEventArgs("Value")); } } public override object ProvideValue(IServiceProvider serviceProvider) { IProvideValueTarget target = serviceProvider.GetService(typeof(IProvideValueTarget)) as IProvideValueTarget; Binding binding = new Binding("Value") { Source = this, Mode = BindingMode.OneWay }; return binding.ProvideValue(serviceProvider) as BindingExpression; } public void Handle(LanguageChangedMessage message) { PropertyChanted(); } }
这个类我们要实现MarkupExtension基类这样我们才能把我们的类可以在xmal里标识出来
我们要重写一下ProvideValue(IServiceProvider serviceProvider)方法这里我们是要把Value双向绑定到页面上
这个类实现了还INotifyPropertyChanged和IHandle<LanguageChangedMessage>接口
这两个类能干什么我想你们应该都知道吧一个是用来binging的一个是用来接收消息的
Key就是我资源文件里的名称项
value是资源文件里的值项看一下它的get也可以看来出是通过IResourceTask的getstring把值取出来
接口信息的方法Handle(LanguageChangedMessage message)
只要有消息过来我们就PropertyChanged Value值这样就可以 把字符切换了,
我们再看一下前台页面是怎么处理的
<Window x:Class="WPFMultLanguage.Views.MyView" xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" xmlns:local="clr-namespace:WPFMultLanguage.Command" xmlns:cal="http://www.caliburnproject.org" cal:Message.Attach="[Event Loaded]=[Action LoadEvent($source)]" Title="MyView" Height="300" Width="300"> <StackPanel> <Menu> <MenuItem Header="{local:MyResource Key=英文}" cal:Message.Attach="[Event Click]=[Action ChanguageLanguage('en')]"></MenuItem> <MenuItem Header="{local:MyResource Key=中文}" cal:Message.Attach="[Event Click]=[Action ChanguageLanguage('zh')]"></MenuItem> </Menu> <TextBlock Text="{local:MyResource Key=语言}"/> <Button Content="{local:MyResource Key=你好}"/> <TextBox x:Name="tb_Show" Text="{local:MyResource Key=文本}"></TextBox> <TextBox x:Name="tb_Load" Text="{local:MyResource Key=文本2}"></TextBox> </StackPanel> </Window>
xmlns:local="clr-namespace:WPFMultLanguage.Command"
这样是把我们上边写的的xaml扩展类放到页面上 Header="{local:MyResource Key=英文}"
这样每一个控件都会初始化一个MyResourceExtension类都会去订阅IHandle<LanguageChangedMessage>广播
再看一下viewModel
namespace WPFMultLanguage.ViewModels { [Export(typeof(IShell))] public class MyViewModel :Screen { public MyViewModel() { } public void LoadEvent(object obj) { //var res = IoC.Get<IResourceTask>(); //((MyView)GetView()).tb_Load.Text = res.GetString("文本2"); } public void ChanguageLanguage(string lan) { var res = IoC.Get<IResourceTask>(); switch (lan) { case "zh": res.ChangeLanguage("zh-CN"); break; case "en": res.ChangeLanguage("en-US"); break; } } } }