记得前一次,老周给大伙,不,小伙伴们介绍了如何填写 .resw 文件,并且在 XAML 中使用 x:Uid 标记来加载。也顺便给大伙儿分析了运行时是如何解析 .resw 文件的。

本来说好了,后续老周会写一篇关于如何在代码里面手动加载文本资源的博文,但一直拖到今天,因为老周前阵子在忙着开发自己的 UWP 应用,已经向应用商店提交了一个版本,昨天刚提交完一次更新。

好,今天咱们就聊聊代码加载文本资源的事情。

在 XAML 中使用 uid 加载资源虽然方便,但是它有个缺点——不同控件有不同的属性,有时候不太方便匹配,当然了,如果你的资源所针对的控件类型不多,那是无所谓的。

为了弥补 uid 加载的不足,我们完全可以自己来编写资源加载代码。这种做法向来是老周惯用的。大家应该还记得当年的 WinForm 应用吧,它也是可以在设计器中直接翻译和编辑资源的(.resx),然后 VS 会帮我们生成一个管理资源的类。

在实际开发中,老周一向不用这一招的,一般是自己写资源类的,这样很灵活,可以自由控制,再让资源类公开一些属性,然后与 UI 进行绑定即可。在WPF中老周并没有开发过多语言的应用,所以没怎么去弄。

同样的道理,在UWP应用中我们也照样可以自己去封装,然后与 UI 绑定即可,这样自己管理起来也方便,而且可以同时用于 XAML 与非 XAML 代码上。故,还是很有意义的。有时候,多折腾一下也是好的,爱折腾的人生更精彩。所以,去年春天老周学 PR,夏天学AE,秋天学CAD,冬天学葫芦丝。今年过年时学单反相机,清明节后学巴乌,劳动节后学电器维修,请在煤气公司工作的同学教我安装燃气灶,七月份学陶笛,九月份去看老师傅做丝绸卷画,十月份临摹柳公权的《玄秘塔碑》。

人一旦有事情可做,就不会胡思乱想了。

下面老周演示一个例子。

项目中放两个资源文件,一个是英文资源,一个是中文资源。

在中文资源中,输入以下三项内容。

在英文资源中,输入以下三项。

资源准备完成后,咱们开始封装。其实,在UWP中,加载资源有个很NND简单的方法,就是用 Windows.ApplicationModel.Resources 命名空间下的

ResourceLoader 类。这个类很好用,调用静态的 GetForCurrentView 方法就能得到一个实例,然后用 GetString 方法就可以加载到字符串资源了。

注意,GetForCurrentView 方法有两个重载,一个是带参数的,参数指定你的.resw 文件名,不带路径和扩展名,这个在前一篇鸟文中老周介绍过的,比如,本例中,资源文件为 Goods.<language tag>.resw,所以传递的参数就是 Goods。另一个是不带参数的 GetForCurrentView方法,如果调用这个版本的重载,那么,你的 .resw 文件必须命名为 Resources.resw,如 Resources.lang-zh-cn.resw,Resources.lang-zh-tw.resw 等。

先看看封装好的类。

    public class ResourcesItems : INotifyPropertyChanged
{
static readonly ResourcesItems mInstance = new ResourcesItems();
public static ResourcesItems Current { get { return mInstance; } } private ResourcesItems()
{
// 构造时加载一下,填充默认值
Load();
} public event PropertyChangedEventHandler PropertyChanged; private void OnPropertyChanged(string propname)
{
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propname));
} public void Load()
{
var loader = ResourceLoader.GetForCurrentView("Goods");
Item1 = loader.GetString("t1");
Item2 = loader.GetString("t2");
Item3 = loader.GetString("t3");
} #region 属性
string _it1, _it2, _it3;
public string Item1
{
get { return _it1; }
set
{
if (value != _it1)
{
_it1 = value;
OnPropertyChanged(nameof(Item1));
}
}
}
public string Item2
{
get { return _it2; }
set
{
if (_it2 != value)
{
_it2 = value;
OnPropertyChanged(nameof(Item2));
}
}
}
public string Item3
{
get { return _it3; }
set
{
if (_it3 != value)
{
_it3 = value;
OnPropertyChanged(nameof(Item3));
}
}
}
#endregion
}

这里我用 Item1、Item2和 Item3 三个属性分别对应资源文件中的三个项。实现 INotifyPropertyChanged 接口是很有划时代战略意义的,这样当语言改变时,从资源文件中重新加载文本时,UI 上的绑定可以实时获得最新的值。

在 99.99986% 的应用场景中,我们只需要实例化一次资源类就行了,所以,保持它在应用生命周期中只有一个实例即可,没必要创建那么实例,浪费食物链资源。故而可以把构造函数私有化,然后用一个静态的、只读的属性来公开当前类的实例。即

        static readonly ResourcesItems mInstance = new ResourcesItems();
public static ResourcesItems Current { get { return mInstance; } }

这个类可以公开一个方法,让外部调用,来加载资源。

        public void Load()
{
var loader = ResourceLoader.GetForCurrentView("Goods");
Item1 = loader.GetString("t1");
Item2 = loader.GetString("t2");
Item3 = loader.GetString("t3");
}

回到应用程序页面类,比如项目模板生成的 MainPage 类,把这个资源管理类作为一个属性公开一下。

        ResourcesItems TheRes
{
get { return ResourcesItems.Current; }
}

有人会说,老周,你干吗这么多此一举?因为待会儿我要用 x:Bind 来绑定,但是,XAML 编译有个“八阿哥”,当你用x:Bind间接绑到实例上时,会发生编译错误。据说在今年 7 月份时,SDK开发团队已收到这个问题报告,将来会修复的。反正秋季更新1709,16299 中还没修复。

就是,如果你这样绑定会出错。

<Pig Head = "{x:Bind ResourceItems.Current.Item1}" ... />

所以,为了避开这个缺陷,可以在页面类中封一下,然后我们可以把 x bind 的源指向页面类的这个属性,这样就不会报错了。

   <TextBlock>
<Run Text="{x:Bind TheRes.Item1,Mode=OneWay}" FontSize="16" Foreground="Blue"/>
<Run Text="{x:Bind TheRes.Item2,Mode=OneWay}" FontSize="16" Foreground="Red"/>
<Run Text="{x:Bind TheRes.Item3,Mode=OneWay}" FontSize="16" Foreground="DarkBlue"/>
</TextBlock>

这样绑定之后,我们就可以在代码中实时修改应用程序的语言,然后再让刚刚封装的资源类重新加载文本就行了。

这里,刻意封装了一个方法,用改应用程序的当前语言。

        private void SetLang(string lang)
{
var context = Windows.ApplicationModel.Resources.Core.ResourceContext.GetForCurrentView();
var qs = context.QualifierValues;
// 这样也可以修改当前应用的语言
qs["Language"] = lang;
}

ResourceContext 负责应用上下文的资源设定,其中,我们有两个办法来改语言,一个是改  Languages 属性,注意它是个列表,也就是说你可以同时设置一串语言组合,默认应用的应该是列表中的第一个(提前是它与当前 Win 10 系统的语言相同)。

不过,我选择用第二种方法,就是从 QualifierValues 属性中获得一个字典,这个字典里面的条目是用于描述当前应用程序资源的限定符,Key 是限定符名称,Value 当然是对应的值。啥是限定符呢。比如当前应用的语言,屏幕缩放比例,是否高对比度,是否旋转屏幕等。

你如果好奇里面有些啥玩意儿,可以用以下代码来输出一下。

#if DEBUG
var strbd = new System.Text.StringBuilder();
foreach (var item in qs)
{
string ts = $"{item.Key} = {item.Value}";
strbd.AppendLine(ts);
}
System.Diagnostics.Debug.WriteLine($"\n\n{strbd}\n\n");
#endif

然后你会看到类似以下这样的输出。

Language = zh-cn
Contrast = standard
Scale = 100
HomeRegion = CN
TargetSize =
LayoutDirection = LTR
Theme = dark
AlternateForm =
DXFeatureLevel =
Configuration =
DeviceFamily = Desktop
Custom =

看到以上输出,你懂了吧。如果还不懂,那撞墙吧。

我们这里只需要改 Language 的值就行了。所以

            var qs = context.QualifierValues;
// 这样也可以修改当前应用的语言
qs["Language"] = lang;

于是,有人肯定又问老周了,老周你是不是失忆了?上一篇中你不是介绍了一个 Windows.Globalization.ApplicationLanguages 类吗,里面只要改一下 PrimaryLanguageOverride 属性就可以了。对的,那个属性确实爽用,一行代码完成。但是,那个属性只适合于非实时更改,就是,可能只是应用运行时,或者用户在设置时改一下。因为那个属性修改了以后,不会马上生效的,可能要等 3 秒钟后才会刷新,除非你故意让程序卡 3 秒钟。不然的话,你要是想让切换语言马上生效,就要改资源限定符了。改限定符 Language 是很管用的,一改就灵,实时刷新。

好了,大功告成,看看效果如何。

示例源代码下载地址

OK,今天的文章就写到这里了,改天有新的发现,老周会及时分享。我现在是越来越喜欢 UWP 应用了,性能高,能够很好适应高分屏,以及各操作方式,最重要的是它安全。

【Win 10 应用开发】在代码中加载文本资源的更多相关文章

  1. 【Win 10 应用开发】启动远程设备上的应用

    这个功能必须在“红石-1”(build 14393)以上的系统版中才能使用,运行在一台设备上的应用,可以通过URI来启动另一台设备上的应用.激活远程应用需要以下前提: 系统必须是build 14393 ...

  2. 【Win 10 应用开发】导入.pfx证书

    这个功能其实并不常用,一般开发较少涉及到证书,不过,简单了解一下还是有必要的. 先来说说制作测试证书的方法,这里老周讲两种方法,可以生成用于测试的.pfx文件. 产生证书,大家都知道有个makecer ...

  3. 【Win 10应用开发】Adaptive磁贴模板的XML文档结构

    在若干天之前,老周给大家讲了Adaptive Toast通知的XML模板,所以相应地,今天老周给大家介绍一下Adaptive磁贴的新XML模板. 同样道理,你依旧可以使用8.1时候的磁贴模板,在win ...

  4. 【Win 10 应用开发】RTM版的UAP项目解剖

    Windows 10 发布后,其实SDK也偷偷地在VS的自定义安装列表中出现了,今天开发人员中心也更新了下载.正式版的SDK在API结构上和以前预览的时候是一样的,只是版本变成10240罢了,所以大家 ...

  5. 【Win 10 应用开发】Toast通知激活应用——前台&后台

    老周最近热衷于讲故事,接下来还是讲故事时间. 有人问我:你上大学的时候,有加入过学生会吗?读大学有没有必要加入学生会? 哎哟,这怎么回答呢,从短期来说,加入学生会有点用,至少可以娱乐一下,运气好的话, ...

  6. 【Win 10应用开发】认识一下UAP项目

    Windows 10 SDK预览版需要10030以上版本号的Win 10预览版系统才能使用.之前我安装的9926的系统,然后安装VS 2015 CTP 6,再装Win 10 SDK,但是在新建项目后, ...

  7. 【Win 10 应用开发】UI Composition 札记(一):视图框架的实现

    在开始今天的内容之前,老周先说一个问题,这个问题记得以前有人提过的. 设置 Windows.ApplicationModel.Core.CoreApplicationView.TitleBar.Ext ...

  8. 【Win 10 应用开发】Sqlite 数据库的简单用法

    如果老周没记错的话,园子里曾经有朋友写过如何在 UWP 项目中使用 Sqlite数据库的文章.目前我们都是使用第三方封装的库,将来,SDK会加入对 Sqlite 的支持. 尽管目前 UWP-RT 库中 ...

  9. 【Win 10 应用开发】文件读写的三种方案

    本文老周就跟伙伴们探讨一下关于文件读写的方法.总得来说嘛,有三种方案可以用,而且每种方案都各有特色,也说不上哪种较好.反正你得记住老祖宗留给我们的大智慧——事无定法,灵活运用者为上. OK,咱们开始吧 ...

随机推荐

  1. wampserver启动不起来的原因?

    如果没怎么动wamp的配置文件就发现wampserver启动不起来了,那么可能你碰到了iis服务器. 原因是apache的端口占用的是80,而iis的端口占用也是80所以造成了不能启动wampserv ...

  2. 01_ExtJS_HelloWorld

    1, 什么是Ext? Ext是一个Ajax框架,用于在客户端创建丰富多彩的web应用程序界面,是在Yahoo! UI的基础上发展而来的.官方网址:http://www.extjs.com/ 现在改为: ...

  3. 【转】MySQL分库分表环境下全局ID生成方案

    转载一篇博客,里面有很多的知识和思想值得我们去思考. —————————————————————————————————————————————————————————————————————— 在大 ...

  4. HTML5基本标签的使用

    第一次写这种东西,肯定存在许多不足之处,还望大家多多担待,我会继续加油的!我也是一名HTML5的初学者,只是将这几周在课堂上所听到的东西分享给大家. 下面给大家介绍一下H5! 一.<!DOCTY ...

  5. struts1.3整合spring2.5(将spring委托给struts方式)

    前提是配置完struts1.3 导包 spring-2.5.6.jar //spring核心包 spring-webmvc-struts-2.5.5.jar //struts整合spring使用 lo ...

  6. oracle查询用户权限及角色(摘)

    1.查看所有用户: select * from dba_users; select * from all_users; select * from user_users; 2.查看用户或角色系统权限( ...

  7. 彻底弄懂AngularJS中的transclusion

    点击查看AngularJS系列目录 彻底弄懂AngularJS中的transclusion AngularJS中指令的重要性是不言而喻的,指令让我们可以创建自己的HTML标记,它将自定义元素变成了一个 ...

  8. DotNetCore跨平台~linux上还原自主nuget包需要注意的问题

    问题的产生的背景 由于我们使用了jenkins进行部署(jenkins~集群分发功能和职责处理),而对于.net core项目来说又是跨平台的,所以对它的项目拉取,包的还原,项目的编译和项目的发布都是 ...

  9. 初学node.js有感三

    WebStorm下的node.js 一.回顾与继续       在前面,我们知道了node.js的基本框架和思路,在这些原生环境下我们对node.js的设计思想有了比较深刻的认识,并且具有了编写大型程 ...

  10. HDU 5974 数学

    A Simple Math Problem Time Limit: 2000/1000 MS (Java/Others)    Memory Limit: 65536/65536 K (Java/Ot ...