使用MVVM-Sidekick开发Universal App(一)

原文:使用MVVM-Sidekick开发Universal App(一)

终于要迈进Universal的大坑了,还有点小激动呢。

CurrencyExchanger 掌中汇率是我前几年发布在Windows Phone商店中的一个应用,当时是WP7版本,下载链接:http://www.windowsphone.com/zh-cn/store/app/%E6%8E%8C%E4%B8%AD%E6%B1%87%E7%8E%87free/84e93a20-cefb-460f-b0d9-a57689b33c10

已经很久没有升级了,最近想学习一下Universal开发,就拿这个练练手吧。之前一直没有系统的写过文章,现在从头把开发中的一些过程记录一下,也是对自己的一个促进。因为是边做边写,肯定会有错误,请大家不吝赐教。

 

一、新建项目

我使用了MVVM-Sidekick框架,这是一个简单但功能强大的MVVM框架,由微软的@韦恩卑鄙 开发,我一直用这个框架开发WP8的程序,前不久作者升级支持了Universal App。

新建项目前需要先安装MVVM-Sidekick的VS扩展插件,在VS2013update2的工具-扩展与更新菜单中搜索mvvm-sidekick就可以找到这个扩展,下载安装即可。安装后会添加项目模板和代码段,比较方便。

github:https://github.com/waynebaby/mvvM-Sidekick

vs插件:http://visualstudiogallery.msdn.microsoft.com/ef9b45cb-8f54-481a-b248-d5ad359ec407

现在可以新建项目了,选择通用应用程序,MVVM-Sidekick Universal App项目模板,输入CurrencyExchanger,等待VS建立项目。这个地方有个需要注意,项目名称不能太长,我第一次输入了一个比较长的名字,结果VS提示名称太长,建立失败了。

 

二、项目结构

现在可以看到VS2013为我们生成了三个项目,

CurrencyExchanger.Windows

CurrencyExchanger.WindowsPhone

CurrencyExchanger.Shared

可以看到我们熟悉的App.xaml文件被放到了Shared项目中,打开看一下,

#if WINDOWS_PHONE_APP
        private TransitionCollection transitions;
#endif

原来有好多条件编译啊,通过这种方式来区分Win8.1和WP8.1,有点坑啊。

在OnLaunched方法中,有这么一行:

//Init MVVM-Sidekick Navigations:
Startups.StartupFunctions.RunAllConfig();

然后我们找到对应的文件看一下,

public static void RunAllConfig()
        {

            typeof(StartupFunctions)
                    .GetRuntimeMethods()
                    .Where(m => m.Name.StartsWith("Config") && m.IsStatic)
                    .Select(
                        m => m.Invoke(null, Enumerable.Empty<object>().ToArray()))
                    .ToArray();

        }

这个方法对View和ViewModel进行了配置,以后新加View的话,MVVM-Sidekick会自动添加所需的ViewModel,并在这个类中进行注册,方便使用。

 

ViewModel文件夹中放着所需的VM,这个文件夹也是在Shared项目中,说明我们可以只用共享的VM去作为不同平台的View的DataContext,实现了共享代码的目的。

 

然后看MainPage_Model.cs这个vm,这个类继承了ViewModelBase<MainPage_Model>,ViewModelBase是MVVM-Sidekick的重要的一个类,所有的vm都要继承这个类。里面有一个属性Title,可以看到还带着一大坨代码,这一大坨代码是怎么出来的呢,MVVM-Sidekick提供了代码段来帮助生成,所以这就是安装VS扩展的好处。

 

通过输入 propvm ,按Tab,就会自动生成一个属性,可以方便的绑定到View上了。

 

然后我们看CurrencyExchanger.WindowsPhone项目中的MainPage.xaml,里面有这么一行:

<Page.Resources>
        <!-- TODO: Delete this line if the key AppName is declared in App.xaml -->

        <vm:MainPage_Model x:Key="DesignVM"/>
    </Page.Resources>

定义了一个资源,把VM引入进来。

<Grid Background="{ThemeResource ApplicationPageBackgroundThemeBrush}"  DataContext="{StaticResource DesignVM}">
        <TextBlock TextWrapping="Wrap" x:Name="pageTitle" Grid.Column="1"  Text="{Binding Title}" Style="{StaticResource HeaderTextBlockStyle}"/>
    </Grid>

把这个VM作为Grid的DataContext,这样就可以进行绑定了,可以看到有一个TextBlock的Text属性绑定到了VM的Title字段。

 

大体的项目结构就是这样,下面我们就开始升级。说是升级,其实就是重新开发啊5555

 

三、建立所需的Model

货币转换这个app功能就是从雅虎财经获取不同的货币代码直接的汇率,因此首先来建立相应的Model。

在CurrencyExchanger.Shared项目中新建一个Models文件夹,添加一个CurrencyItem.cs,内容如下:

 public class CurrencyItem : BindableBase<CurrencyItem>
    {
        /// <summary>
        /// 货币代码
        /// </summary>
        public string Code
        {
            get { return _CodeLocator(this).Value; }
            set { _CodeLocator(this).SetValueAndTryNotify(value); }
        }
        #region Property string Code Setup
        protected Property<string> _Code = new Property<string> { LocatorFunc = _CodeLocator };
        static Func<BindableBase, ValueContainer<string>> _CodeLocator = RegisterContainerLocator<string>("Code", model => model.Initialize("Code", ref model._Code, ref _CodeLocator, _CodeDefaultValueFactory));
        static Func<string> _CodeDefaultValueFactory = () => { return default(string); };
        #endregion

        /// <summary>
        /// 描述
        /// </summary>
        public string Description
        {
            get { return _DescriptionLocator(this).Value; }
            set { _DescriptionLocator(this).SetValueAndTryNotify(value); }
        }
        #region Property string Description Setup
        protected Property<string> _Description = new Property<string> { LocatorFunc = _DescriptionLocator };
        static Func<BindableBase, ValueContainer<string>> _DescriptionLocator = RegisterContainerLocator<string>("Description", model => model.Initialize("Description", ref model._Description, ref _DescriptionLocator, _DescriptionDefaultValueFactory));
        static Func<string> _DescriptionDefaultValueFactory = () => { return default(string); };
        #endregion

        /// <summary>
        /// 图片名称
        /// </summary>
        public string Image
        {
            get { return _ImageLocator(this).Value; }
            set { _ImageLocator(this).SetValueAndTryNotify(value); }
        }
        #region Property string Image Setup
        protected Property<string> _Image = new Property<string> { LocatorFunc = _ImageLocator };
        static Func<BindableBase, ValueContainer<string>> _ImageLocator = RegisterContainerLocator<string>("Image", model => model.Initialize("Image", ref model._Image, ref _ImageLocator, _ImageDefaultValueFactory));
        static Func<string> _ImageDefaultValueFactory = () => { return default(string); };
        #endregion

    }

View Code

这个Model要继承BindableBase<CurrencyItem>,在MVVM-Sidekick中BindableBase是和ViewModelBase一样重要的几个基类,用于实现可绑定的model,但区别是ViewModelBase中还会放一些Command,而BindableBase顾名思义仅用于绑定属性,不建议在里面放Command这些东西。不要看上面这么一大坨,其实就输入了几个单词而已,都是用propvm生成的。主要是三个属性,货币代码,描述,图片名称。图片用于在显示货币的时候显示一个国旗的图片。

 

与此类似再建立一个货币转换的model,新建CurrencyExchangeItem.cs文件,代码如下:

public class CurrencyExchangeItem : BindableBase<CurrencyExchangeItem>
    {
        /// <summary>
        /// 日期
        /// </summary>
        public DateTime TradeDate
        {
            get { return _TradeDateLocator(this).Value; }
            set { _TradeDateLocator(this).SetValueAndTryNotify(value); }
        }
        #region Property DateTime TradeDate Setup
        protected Property<DateTime> _TradeDate = new Property<DateTime> { LocatorFunc = _TradeDateLocator };
        static Func<BindableBase, ValueContainer<DateTime>> _TradeDateLocator = RegisterContainerLocator<DateTime>("TradeDate", model => model.Initialize("TradeDate", ref model._TradeDate, ref _TradeDateLocator, _TradeDateDefaultValueFactory));
        static Func<DateTime> _TradeDateDefaultValueFactory = () => { return default(DateTime); };
        #endregion

        /// <summary>
        /// 汇率
        /// </summary>
        public double Rate
        {
            get { return _RateLocator(this).Value; }
            set { _RateLocator(this).SetValueAndTryNotify(value); }
        }
        #region Property double Rate Setup
        protected Property<double> _Rate = new Property<double> { LocatorFunc = _RateLocator };
        static Func<BindableBase, ValueContainer<double>> _RateLocator = RegisterContainerLocator<double>("Rate", model => model.Initialize("Rate", ref model._Rate, ref _RateLocator, _RateDefaultValueFactory));
        static Func<double> _RateDefaultValueFactory = () => { return default(double); };
        #endregion

        /// <summary>
        /// 逆向汇率
        /// </summary>
        public double InverseRate
        {
            get { return _InverseRateLocator(this).Value; }
            set { _InverseRateLocator(this).SetValueAndTryNotify(value); }
        }
        #region Property double InverseRate Setup
        protected Property<double> _InverseRate = new Property<double> { LocatorFunc = _InverseRateLocator };
        static Func<BindableBase, ValueContainer<double>> _InverseRateLocator = RegisterContainerLocator<double>("InverseRate", model => model.Initialize("InverseRate", ref model._InverseRate, ref _InverseRateLocator, _InverseRateDefaultValueFactory));
        static Func<double> _InverseRateDefaultValueFactory = () => { return default(double); };
        #endregion

        /// <summary>
        /// 是否为基准货币
        /// </summary>
        public bool IsStandard
        {
            get { return _IsStandardLocator(this).Value; }
            set { _IsStandardLocator(this).SetValueAndTryNotify(value); }
        }
        #region Property bool IsStandard Setup
        protected Property<bool> _IsStandard = new Property<bool> { LocatorFunc = _IsStandardLocator };
        static Func<BindableBase, ValueContainer<bool>> _IsStandardLocator = RegisterContainerLocator<bool>("IsStandard", model => model.Initialize("IsStandard", ref model._IsStandard, ref _IsStandardLocator, _IsStandardDefaultValueFactory));
        static Func<bool> _IsStandardDefaultValueFactory = () => { return default(bool); };
        #endregion

        /// <summary>
        /// 货币数量
        /// </summary>
        public double Amount
        {
            get { return _AmountLocator(this).Value; }
            set { _AmountLocator(this).SetValueAndTryNotify(value); }
        }
        #region Property double Amount Setup
        protected Property<double> _Amount = new Property<double> { LocatorFunc = _AmountLocator };
        static Func<BindableBase, ValueContainer<double>> _AmountLocator = RegisterContainerLocator<double>("Amount", model => model.Initialize("Amount", ref model._Amount, ref _AmountLocator, _AmountDefaultValueFactory));
        static Func<double> _AmountDefaultValueFactory = () => { return default(double); };
        #endregion

        /// <summary>
        /// 基准货币
        /// </summary>
        public CurrencyItem CurrencyBase
        {
            get { return _CurrencyBaseLocator(this).Value; }
            set { _CurrencyBaseLocator(this).SetValueAndTryNotify(value); }
        }
        #region Property CurrencyItem CurrencyBase Setup
        protected Property<CurrencyItem> _CurrencyBase = new Property<CurrencyItem> { LocatorFunc = _CurrencyBaseLocator };
        static Func<BindableBase, ValueContainer<CurrencyItem>> _CurrencyBaseLocator = RegisterContainerLocator<CurrencyItem>("CurrencyBase", model => model.Initialize("CurrencyBase", ref model._CurrencyBase, ref _CurrencyBaseLocator, _CurrencyBaseDefaultValueFactory));
        static Func<CurrencyItem> _CurrencyBaseDefaultValueFactory = () => { return default(CurrencyItem); };
        #endregion

        /// <summary>
        /// 目标货币
        /// </summary>
        public CurrencyItem CurrencyTarget
        {
            get { return _CurrencyTargetLocator(this).Value; }
            set { _CurrencyTargetLocator(this).SetValueAndTryNotify(value); }
        }
        #region Property CurrencyItem CurrencyTarget Setup
        protected Property<CurrencyItem> _CurrencyTarget = new Property<CurrencyItem> { LocatorFunc = _CurrencyTargetLocator };
        static Func<BindableBase, ValueContainer<CurrencyItem>> _CurrencyTargetLocator = RegisterContainerLocator<CurrencyItem>("CurrencyTarget", model => model.Initialize("CurrencyTarget", ref model._CurrencyTarget, ref _CurrencyTargetLocator, _CurrencyTargetDefaultValueFactory));
        static Func<CurrencyItem> _CurrencyTargetDefaultValueFactory = () => { return default(CurrencyItem); };
        #endregion

    }

View Code

这个model就是用来显示货币汇率转换的,里面有两个货币的信息还有汇率的信息等等。

 

四、初始化数据

在用户第一次进入app时,应该让用户选择要显示哪些货币的汇率,这样就要给用户提供一个货币列表,这个列表需要我们提前初始化好。

 

新建一个Context类,放一些常用的东东。在Shared项目中新建Utilities目录,添加一个Context.cs文件,做成单例。

public sealed class Context
    {
        static readonly Context instance = new Context();

        static Context()
        {

        }

        private Context()
        {
        }

        /// <summary>
        /// Gets the instance.
        /// </summary>
        /// <value>The instance.</value>
        public static Context Instance
        {
            get
            {
                return instance;
            }
        }
}

View Code

在里面添加一个列表:

public List<CurrencyItem> AllCurrencyItemList { get; set; }

然后一个初始化方法:

public void Init()
        {
            AllCurrencyItemList = new List<CurrencyItem>()
            {
                new CurrencyItem{Code = "AED", Description ="阿联酋迪拉姆", Image="flag_united_arab_emirates"},
new CurrencyItem{Code = "ALL", Description = "阿尔巴尼亚列克", Image="flag_albania"},
……
}

找到App.xaml.cs,在OnLaunched方法中调用此方法:

//Init Context
Context.Instance.Init();

添加货币列表是一个很枯燥的工作,当初我是从雅虎财经网页上扒下的货币代码,又从网页素材网站找到国旗的图片,挨个整理好。当然也可以事先整理成xml来读取。

慢着,我的WP7程序就是支持多语言的,此时当然不能把货币描述直接hard code,而应该从资源文件中按照用户当前的语言来显示。

 

好吧又多了一个问题,多语言。

 

五、可以叫全球化多语言本地化……反正就是可以让用户选择语言

以前的WP7多语言需要自己搞一大坨代码,到了WP8方便了一点,VS会帮助干很多事。但到了Universal,情况又变了。WP8添加资源文件的时候资源文件格式为resx,同时程序会自动添加一个AppResouces.Designer.cs,通过一个全局的ResourceManager去取得资源文件中的字符串,代码中可以直接调用:

String appName = AppResources.AppName;

是不是很方便?

到了Universal里,自动生成的没有了,添加的资源文件格式变成了resw,需要用这种方式来调用:

var loader = new Windows.ApplicationModel.Resources.ResourceLoader();
var string = loader.GetString('Farewell');

是不是很坑?万一字符串写错了就找不到了。

添加多语言文件倒不麻烦,有多语言工具包,链接:http://msdn.microsoft.com/zh-cn/library/windows/apps/xaml/jj572370.aspx

但是调用显得不太友好。所以我仿照WP8的方式新建了一个AppResources.cs,放到Utilities,里面这样写:

public static class AppResources
    {
        public static ResourceLoader CurrentResourceLoader
        {
            get
            {
                return ResourceLoader.GetForCurrentView();
            }
        }

        public static string AppName
        {
            get
            {
                return CurrentResourceLoader.GetString("AppName");
            }
        }
。。。。。。
}

只要保证这里写对,这样以后调用的时候就不怕出错了。

多语言资源文件的添加比较简单,有工具包协助,甚至翻译都可以帮你做好,具体步骤见

http://msdn.microsoft.com/zh-cn/library/windows/apps/xaml/jj572370.aspx

 

需要注意的是,以前的方式需要我们为每种语言建立一个资源文件,现在有多语言工具包就不需要了,只添加一个默认语言的即可,工具包会自动填充其他的语言。比如CurrencyExchanger默认语言是英语,那么步骤就是:

打开Package.appxmanifest文件,把默认语言改成en-US,然后添加一个Strings文件夹,下面添加en-US文件夹,添加一个Resources.resw资源文件,在这里面编辑所需要的字符串。

右键单击CurrencyExchanger.WindowsPhone,选择添加翻译语言,

这样会自动建立一个MultilingualResources文件夹,里面是一大坨xlf后缀的文件,qps-ploc.xlf这个是伪语言,用于测试的,在其他的几个文件上点右键,选择打开方式,选择多语言编辑器,出来这么一个东东:

看到菜单没有,点翻译,Microsoft Translator直接就帮你翻译好了。当然还需要进一步校对,但已经很智能化了。这样就不需要为每种语言建资源文件了,可以从这些xlf文件里找。需要注意的是,如果你的程序选择了zh-CN的默认语言,就不能再有zh-CN.xlf的多语言资源,否则会提示错误,删掉重复的即可。你也可以在xlf文件上右键发送邮件给朋友,翻译完了再导入进来。

 

呼呼,先别管翻译的准不准,代码里我们可以这样初始化货币列表了:

AllCurrencyItemList = new List<CurrencyItem>()
            {
                new CurrencyItem{Code = "AED", Description = AppResources.AED, Image="flag_united_arab_emirates"},
new CurrencyItem{Code = "ALL", Description = AppResources.ALL, Image="flag_albania"},
//new CurrencyItem{Code = "ANG", Description = AppResources.ANG, Image=""},
new CurrencyItem{Code = "ARS", Description = AppResources.ARS, Image="flag_argentina"},
。。。。。
}

因为是从资源文件中读取的货币描述,所以在UI会显示和用户系统匹配的语言。

未完待续。

时间: 2024-09-17 23:16:42

使用MVVM-Sidekick开发Universal App(一)的相关文章

使用MVVM-Sidekick开发Universal App(二)

原文:使用MVVM-Sidekick开发Universal App(二) 上一篇文章已经建立了基本的实体类,并且搞定了多语言的问题,以后在app里用字符串的时候就可以从资源文件中取了.现在继续进行. 一.添加一个页面 CurrencyExchanger首页是一个货币兑换的列表,这个列表比较复杂,我们先不管,先从简单的页面做起.首先要有一个添加货币的页面,显示所有可添加的货币列表.在WP项目中右键添加一个页面,命名为AddCurrencyPage.xaml. 然后MVVM-Sidekick自动添加

Windows Phone 8.1 开发技术概览 (Universal APP)

前一阵真的比较懒 WP8.1 已经出来这么长时间了现在才更新BLOG让大家久等了,今天我先为大家介绍下 WP 8.1的开发框架,什么是微软所推崇的 Universal APP,以及我们要开发 Universal APP的时候要注意哪些内容. 如果是您是一个刚刚接触 WP开发的朋友可以先看下我之前的文章了解一下故事背景:Windows Phone 8 与 windows 8 开发技术概览 首先给大家完善一个概念 - 那就是 Windows Phone 8.1 和 Windows Phone 8.0

开发 Electron app 必知的 4 个 tips

本文讲的是开发 Electron app 必知的 4 个 tips, Electron ,是包括 Avocode 在内的众多 app 采用的技术,能让你快速实现并运行一个跨平台桌面应用.有些问题不注意的话,你的 app 很快就会掉到"坑"里.无法从其它 app 中脱颖而出. 这是我 2016 年 5月 在 Amsterdam 的 Electron Meetup 上演讲的手抄版,加入了对 api 变化的考虑.注意,以下内容会很深入细节,并假设你对 Electron有一定了解. 首先,我是

iOS传感器开发——为APP添加手机密码、指纹进行安全验证

iOS传感器开发--为APP添加手机密码.指纹进行安全验证 一.引言         iPhone5s之后,iPhone硬件上已支持进行指纹识别的功能,相应的,一些新的api也可以应用于APP中,进行用户安全的验证.目前,开发者可以使用的安全验证方式有两种,一种是通过手机密码进行验证,一种是通过识别指纹进行验证. 二.为APP添加安全验证 要使用安全验证的相关api,我们需要引入如下头文件: ? 1 #import <LocalAuthentication/LocalAuthentication

iOS开发中APP怎么用USB与硬件进行通讯 交互数据 求大神

问题描述 iOS开发中APP怎么用USB与硬件进行通讯 交互数据 求大神 本人最近开发一个iOS项目 其中要求通过USB来访问硬件信息 可有人知道APP通过USB怎么与硬件进行数据交互 看了好多文档依然很不解 求大神 解决方案 http://www.zhihu.com/question/38150683

图片-新手开发安卓app的欢迎界面

问题描述 新手开发安卓app的欢迎界面 程序已经写好了 想加一个图片作为欢迎页面 再点击一下进入主页面没什么思路 是要在做一个界面然后加个按钮吗?希望大神解答为什么这样修改之后做程序会停止运行呢?这是修改之前 protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); SearchView searc

app store-iOS开发提交app时出现证书出错的问题

问题描述 iOS开发提交app时出现证书出错的问题 项目已经要上线了,但是最后一步上传app的时候,xcode可以archive,但是打包ipa和 上传到app store的时候,一直提示证书遗失,我真的快疯了(ps:我自己创建的证书并不能导出p12文件,也就是说没有私人秘钥,但是我跟着流程创建的csr签名文件了,为什么还是不能生成p12文件呢),下面给图 解决方案 有没有人啊,好着急啊 解决方案二: 查看 钥匙串 中 登录->证书,查看发布证书是否正常,有没有可能是过期了,或被revoke掉了

毕业设计 java 安卓-关于开发安卓app远程控制电脑的毕业设计的问题

问题描述 关于开发安卓app远程控制电脑的毕业设计的问题 我毕业设计想做一个app.能实现远程控制电脑的功能.本人java基础很一般.希望能有人指点下.这个毕设所需要的语言技术有哪些.需要从哪些知识学起.需要学习哪些相关知识. 解决方案 http://www.360doc.com/content/13/0327/08/7891085_274170612.shtml 解决方案二: 就学Java就行了.手机端的开发上网找些Android的视频看看就好了.远程控制在电脑端也需要开发个Java程序,好像

《Android App开发入门:使用Android Studio 2.X开发环境》——第 1章 使用 Android Studio 开发 Android App

第 1章 使用 Android Studio 开发 Android App 1-1 创建第一个 Android App 项目 1-2 在计算机的仿真器上执行 App 1-3 Android Studio 快速上手 1-4 Android 项目的构成 本章将介绍如何使用 Android Studio 集成开发环境开发 Android App.我们将先说明在 Android Studio 中如何添加.创建 Android App,接着说明如何将完成的程序( App)放在仿真器上执行与测试.让读者先体