一、创建Application Bar
Application Bar和WinForm界面中菜单栏、状态栏等界面组成部分一样,是移动运用界面的一个组成部分,只是默认情况下是空的不可见的,开发人员可以根据需要创建Application Bar的实例并添加功能按钮。
为运用添加Application Bar有两种方式,Xmal方式和后台代码的方式。
Xmal方式很简单,创建一个Windows Phone项目后,打开MainPage.xaml文件,能够发现默认就有<phone:PhoneApplicationPage.ApplicationBar>这一段Xaml,只是被注释掉了,我们只需要去掉注释即可。
如果通过代码的方式来添加方法如下,查看MainPage的基类PhoneApplicationPage,
发现ApplicationBar实际上是界面的一个组成部分,只是在我们不定义的情况下为Null而已。
进一步查看IApplicationBar的基类,发现ApplicationBar实际上由两部分组成:Buttons和MenuItems。
所以我们只需要给ApplicationBar属性赋值即可。代码如下:
ApplicationBar = new ApplicationBar(); ApplicationBar.Buttons.Add(new ApplicationBarIconButton(new Uri("/Images/appbar.feature.email.rest.png", UriKind.Relative)) { Text = "按钮1" }); ApplicationBar.MenuItems.Add(new ApplicationBarMenuItem("目录项1"));
需要注意的地方
虽然添加Application Bar的方式很简单,但是还是有几个地方是我们容易忽略的。
- Buttons的个数是有限制的,最多为4个,多于4个会编译出错。当Buttons不够用时,可以用MenuItems来扩展,而MenuItems的个数是没有限制,当大于5个时,会出现滚动条。
- 图片添加到项目后必须将Build Action设置成Content并且将Copy to Output Directory设置成Copy if newer或者Copy always,否则图片显示不了
- 安装完Windows Phone SDK,系统默认提供了一组最优化的图标供开发人员使用,在类似如下位置:C:\Program Files\Microsoft SDKs\Windows Phone\v7.1\Icons
- 如果需要自定义图标,为了达到好的效果,需要满足一定的条件,具体可查询MSDN, http://msdn.microsoft.com/en-us/library/ff431806(v=VS.92).aspx
二、本地化和多语言
和.net进行桌面等开发一样,可以利用资源文件实现本地化的需求。
我们先来实现界面中TextBlock文本的本地化,通用的步骤如下:
1. 项目中添加一个默认的资源文件,取名AppResource.resx,这个文件的名称是可以自己定义的,接着添加3个字符串资源,注意必须将访问修饰符设置成public,否则界面绑定的时候编译会出现异常,如下图:
为了体现本地化的效果,我们再添加一个资源文件,取名AppResource.en-US.resx,注意这里的文件名形式,AppResource与默认资源文件名称一样,en-US代表English (United States),如下图:
Windows Phone支持的语言表和语言的代码表可以参考如下资料:
http://msdn.microsoft.com/en-us/library/hh202918(v=vs.92).aspx
2. 创建资源文件的包装类LocalizedStrings.cs,供界面元素绑定(后面介绍为什么要包装类以及怎么样去掉这个包装类),代码如下:
public class LocalizedStrings { private static WPDemo.AppResource localizedresources = new WPDemo.AppResource(); public WPDemo.AppResource Localizedresources { get { return localizedresources; } } }
也有类似如下版本的:
public class LocalizedStrings { public string ContentText { get { return AppResource.ContentText; } } public string ButtonText { get { return AppResource.ButtonText; } } public string MenuItemText { get { return AppResource.MenuItemText; } } }
第二种方式相当于将具体的资源包装成具体的属性,使用起来更直接,但是资源太多了写起来比较繁琐,也有点多此一举,我推荐第一种方式。
3. 为了界面中能绑定,需要将LocalizedStrings类作为资源加载进来,因为资源多个界面都会使用,所以作为全局资源比较合适,因此打开App.xaml文件,添加如下代码:
<Application.Resources> <local:LocalizedStrings xmlns:local="clr-namespace:WPDemo" x:Key="LocalizedStrings" /> </Application.Resources>
4. 用文本工具如记事本打开项目文件WPDemo.csproj,在<SupportedCultures>元素中添加要支持的语言,在本实例中,默认的资源文件AppResource.resx实际为中文版资源,在<SupportedCultures>元素中只需要加入en-US;即可,代码如下:
<SupportedCultures>en-US;</SupportedCultures>
这么设置后实际上准确的说,系统应该支持英语和非英语,听着大家觉得废话,我的意思是想说,当我们将我们的设备设置成英语时, AppResource.en-US.resx文件的资源会起作用,当设置成其他一切语言时默认资源文件AppResource.resx会起作用。设置界面如下:
如果在项目文件的<SupportedCultures>节点中,不配置任何内容语言,那么即使在如上界面中配置成English(United States),界面仍然显示成中文而不会自动读取AppResource.en-US.resx文件的资源,因为没配置的情况下说明,说明系统不支持英文,系统只会从默认的资源文件AppResource.resx中读取资源。我觉得微软在这个地方也许可以改进一下,记事本打开项目文件再配置的过程是不是可以免了,因为假如用户语言选的是English(United States),完全可以自动查找有没有AppResource.en-US.resx这个资源文件,有的话当然就读这个文件,没有就读AppResource.resx完事!大家觉得呢?当然设计者这样做也可能是基于其他方面的考虑!
5. 最后就是界面中使用绑定的方式加载资源文件中的字符串了,打开默认主界面文件MainPage.xaml,在名为ContentPanel的Grid元素中添加一个TextBlock元素后代码如下:
<Grid x:Name="ContentPanel" Grid.Row="1" Margin="12,0,12,0"> <TextBlock x:Name="txtRes" Text="{Binding Path=Localizedresources.ContentText, Source={StaticResource LocalizedStrings}}"></TextBlock> </Grid>
注意,如果包装类LocalizedStrings采用的是第二种写法,代码就应该是如下形式:
Text="{Binding Path=ContentText, Source={StaticResource LocalizedStrings}}"
好了,如上步骤就是本地化的通用方式,下面运行程序,测试一下,发现已经达到了我们的预期!
上面提到包装类LocalizedStrings,大家可能会疑惑,这玩意儿有什么用,有点多此一举的感觉,其实我也这么认为,但去网上一搜,发现基本上本地化的示例全都一成不变的这么搞的!
为什么?
实际操作一下吧,先把LocalizedStrings.cs文件从项目中排除,然后打开App.xaml文件,去掉包装类的引用,加上如下语句:
<local:AppResource xmlns:local="clr-namespace:WPDemo" x:Key="LocalizedStrings" />
目的很明确,不通过包装类直接引用资源文件类AppResource,与之对应,界面的元素就应该改成如下:
<TextBlock x:Name="txtRes" Text="{Binding Path=ContentText, Source={StaticResource LocalizedStrings}}"></TextBlock>
如果采用的是包装类的第二种写法,这里就不用管了,因为ContentText在包装类中和类AppResource中一样,直接是成员属性。具体可以打开AppResource.Designer.cs文件查看。
到现在,感觉差不多了,编译,OK,没有任何错误。F5运行程序,却抛出了异常:
No matching constructor found on type 'WPDemo.AppResource'
提示资源类型中没有匹配的构造函数,我们打开AppResource.Designer.cs,发现有如下默认构造函数
[global::System.Diagnostics.CodeAnalysis.SuppressMessageAttribute("Microsoft.Performance", "CA1811:AvoidUncalledPrivateCode")] internal AppResource() { }
你应该已经发现了,原因就在于构造函数默认的访问修饰符是internal,资源文件和程序文件编译后不在同一个程序集,将internal该成public后重新运行,发现和有包装类时效果一样了。
三、Application Bar的本地化和多语言
有人肯定会纳闷为什么要把Application Bar单独拿出来,其实原因很简单,它和界面中其他内容元素的本地化有些不一样。查看Msdn,明确说明Application Bar不是silverlight控件,其属性不能像TextBlock等其他控件一样进行数据绑定,要实现是本地化,只能是编码的方式了。
先在界面中添加如下代码
<phone:PhoneApplicationPage.ApplicationBar> <shell:ApplicationBar IsVisible="True" IsMenuEnabled="True"> <shell:ApplicationBarIconButton IconUri="/Images/appbar.feature.email.rest.png" Text="btn1"/> <shell:ApplicationBarIconButton IconUri="/Images/appbar.refresh.rest.png" Text="btn2"/> <shell:ApplicationBar.MenuItems> <shell:ApplicationBarMenuItem Text="mi1"/> <shell:ApplicationBarMenuItem Text="mi2"/> </shell:ApplicationBar.MenuItems> </shell:ApplicationBar> </phone:PhoneApplicationPage.ApplicationBar>
按钮和目录的Text属性如果写成如下形式编译将无法通过:
Text=Localizedresources.ContentText, Source={StaticResource LocalizedStrings}}"
看来只能在代码中通过属性来本地化了,我们拿第一个按钮来测试一下我们的想法,按常规思维,我们给按钮加上x:Name属性,如下:
<shell:ApplicationBarIconButton Text="btn1" IconUri="/Images/appbar.feature.email.rest.png" Text="btn1"/>
在MainPage()函数中添加如下代码:
btn1.Text = AppResource.ButtonText;
编译没有问题,运行后发现抛出NullReferenceException异常,为什么?Google一下,能看的资源有限(就Google能搜点有用的东西,关键时候总被和谐,上火!),大概意思就是说ApplicationBar不是派生自FrameworkElement,因而不是页面visual tree的一部分,代码中无法通过x:Name来引用。真不知道是不是Bug?反正是不能这样搞那只能换个思路了,居然我们明明知道ApplicationBar是有两个按钮的,那么我们可以尝试一下如下方式:
((ApplicationBarIconButton)ApplicationBar.Buttons[0]).Text = AppResource.ButtonText;
运行代码,发现果然可以!
第一小节中我们提到添加ApplicatonBar有两种方式,刚才本地化是用的只是第一种方式,如果用第二种方式也就是代码的方式添加ApplicatonBar就不存在上面的问题了,把MainPage.xaml中ApplicationBar部分的代码注释掉,在MainPage()函数中添加如下代码:
ApplicationBar = new ApplicationBar(); ApplicationBar.Buttons.Add(new ApplicationBarIconButton(new Uri("/Images/appbar.feature.email.rest.png", UriKind.Relative)) { Text = AppResource.Button1Text }); ApplicationBar.MenuItems.Add(new ApplicationBarMenuItem(AppResource.MenuItem1Text));