您的位置:知识库 » 手机开发

WP7有约(一):课程安排

作者: Allen Lee  来源: 博客园  发布时间: 2010-11-19 09:52  阅读: 5448 次  推荐: 0   原文链接   [收藏]  

 

  Expression Blend如何提供设计时数据的支持?

      首先我们来看看Expression Blend为我们生成了哪些文件:

图 40

  由于我们的数据是从XML文件导入的,所以你会看到一个XSD文件,这是从我们那个XML文件生成的XML Schema,这个XSD文件将会用来生成相关的类,这些类都放在对应的C#文件里,而我们的数据最终是以XAML的形式存在的。当我们导入XML数据时,Expression Blend不但为我们生成这些文件,还在App.xaml的Application.Resource里添加了相应的对象:

代码 3

  为什么添加到App.xaml而不是某个页面的XAML文件里呢?这是因为我们在Import Sample Data from XML对话框里选择了Define in Project(参见图16),如果我们当时选择Define in This document,它就会添加到某个页面的XAML文件里。那么,Expression Blend又是如何得知我们的数据保存在哪个XAML文件里呢?答案就在courses类的构造函数里:

代码 4

  这个URI看起来有点古怪,如果你用Visual Studio打开这个项目,你将会看到这个XAML文件的Build Action是Page,事实上,它会被编译成BAML,并且嵌到Iridescent程序集里,这种古怪的URI就是引用程序集内嵌资源的表示方式。

      接着,当我们从Data面板把courseCollection拖到Pivot项上时(参见图17),Expression Blend会把它所属的MondayCoursesSampleData绑到Pivot控件的父容器的DataContext属性上,在当前Pivot项的子容器里创建一个ListBox,并把courseCollection绑到它的ItemsSource属性上:

代码 5

  还记得Import Sample Data from XML对话框里有个选项是Enable sample data when application is running吗?当时它是选中的,如果我们把这个选项去掉,DataContext属性前面就会多一个"d:":

代码 6

  这个前缀告诉编译器在生成最终程序集时忽略这个属性,这样你就不会在程序运行的时候看到这些数据了。

      如果你细心观察Projects面板,可能会发现几个我们从未提及过的文件:

图 41

  它们是干嘛的呢?事实上,这些文件在我们刚创建完项目时就存在了,还记得最初的Panorama页吗(参见图3和图4),里面的Panorama项是有数据的,而这些数据就是来自MainViewModelSampleData.xaml文件的。那么,这些数据又是如何关联到Panorama控件的呢?打开MainPage.xaml,在文件的顶部,你会发现它的蛛丝马迹:

代码 7

  这里使用的不是我们常见的Binding,而是d:DesignData,正如你所看到的,它用来指定数据文件的位置,但这些数据是在设计时使用的,所以你会看到DataContext前面有个"d:",事实上,我们把这些带有"d:"前缀的属性称为设计时属性。和这些数据相关的类分别定义在MainViewModel.cs和ItemViewModel.cs文件里。如果你打开MainViewModel.cs,你会发现里面的LoadData方法包含了另一份不同的数据,为什么,它们分别用来干嘛的?我给你截两个图,看你能否找到什么蛛丝马迹:

代码 8

代码 9

  看到了吗,XAML文件里每个ItemViewModel对象的LineOne属性值都是"design XXX",而C#文件的则是"runtime XXX",事实上,这已经道出它们的用途了,XAML文件里的数据是设计时使用的,而C#文件里的则是运行时使用的。但是,MainPage.xaml的根元素的DataContext属性前面有"d:"前缀啊,之前不是说编译器会在生成程序集的时候忽略它吗,那应用程序又如何找到运行时使用的数据呢?答案就在MainPage.cs里:

代码 10

  App类的ViewModel是一个静态属性,它的任务只是创建一个MainViewModel对象,当MainPage的构造函数被调用时,会把这个MainViewModel对象绑到自己的DataContext属性上,当Loaded事件触发时,如果数据还没装载,就调用LoadData方法装载数据。

      从上面的讨论不难看出,Expression Blend的确为我们做了很多,而这些知识将会协助我们完成后面的开发任务。

  保存课程表

      说了那么多前端的东西,是时候看看后端了。课程表软件说到底就是一个管理课程表数据的软件,所以数据存储是一个非常重要的环节。如果说用户界面的设计是Expression Blend的强项,那么业务逻辑的开发就是Visual Studio的地盘,既然接下来的着眼点是后端,当然要切换到Visual Studio啦。

      右击Projects面板里的解决方案节点,选择Edit in Visual Studio:

图 42

  此时,Visual Studio会打开,接下来,我们将会在Visual Studio里完成后端部分的开发。

      首先,创建一个Models文件夹,在里面添加一个Course类,并让它实现INotifyPropertyChanged接口:

代码 11

  接着,我们需要为它添加如下属性:

属性名字

属性类型

备注

Name

string

课程名称

Day

string

星期几

StartTime

DateTime

上课时间

EndTime

DateTime

下课时间

Location

string

上课地点

表 1

  这些属性实现起来并不难,但我们不能使用C# 3.0的自动属性来实现,因为我们需要在属性的set访问器里调用OnPropertyChanged方法,下面我们选择其中一个来实现:

代码 12

  如果你觉得在创建这些属性时手动输入所有代码太麻烦,你也可以创建一个代码段,然后通过代码段来添加属性。

      接下来我们需要考虑一下以什么方式来存储课程表的数据,如果不考虑使用第三方类库的话,我们至少有JSON序列化、XML序列化以及LINQ to XML等几种常见的方式,而无论我们今天的选择是什么,将来都有可能换用别的方式,既然我们已经预见了变化,就应该通过抽象把它们隔离开来。

      创建一个Services文件夹,在里面创建一个ICourseStore接口,那么,我们又应该如何设计这个接口呢?毫无疑问,它应该以某种形式满足CRUD四种需求,那么,这种形式又是怎样的呢?回忆一下上一节的内容以及前面的用户界面,你觉得我们会对这个接口有什么期望?显然,如果我们能够获取一组Course对象,并把它们绑到Pivot项里的ListBox就好了。这个好办,我们可以在ICourseStore接口里声明一个Courses属性,类型是ObservableCollection<Course>,这样,数据绑定可以协助我们满足"R"的需求,当我们从用户界面上选中一个课程,单击Application Bar上的删除按钮,我们可以从ListBox上获取当前选中的Course对象,并从Courses属性里删除,这样,"D"的需求也得到满足了,剩下的就是"C"和"U"了,它们有一个共同的地方,就是都要打开一个新的页面,不同的是前者对应一个新的Course对象,而后者则对应当前选中的Course对象,当用户单击确定之后,这些改动就会提交到Courses属性,换句话说,四种需求都得到满足了。但是,这里好像没有提到把数据保存到独立存储区啊?没错,所以ICourseStore接口还需要提供一个Commit方法,把数据提交到独立存储区。此外,我们还可以提供一个Rollback方法,把Courses属性上的数据还原为独立存储区上的数据,这样,如果用户不小心把课程表改乱了,也无需通过重启应用程序来重新加载独立存储区上的数据。好了,ICourseStore接口确定下来了:

代码 13

  那么,接口的实现呢?刚才我们提到了三种常见的存储方式,我个人比较喜欢LINQ to XML,不过今天我想试一下JSON序列化。

      首先,在Services文件夹里创建一个JsonCourseStore类,并让它实现ICourseStore接口:

代码 14

  接着,引用以下类库和命名空间:

类库/命名空间

System.Servicemodel.Web.dll

System.IO

System.IO.IsolatedStorage

System.Runtime.Serialization.Json

表 2

  好了,现在可以开始实现JsonCourseStore类了。当应用程序启动时,会调用JsonCourseStore类的构造函数,从独立存储区把课程表的数据反序列化到Courses属性里;而当用户执行回滚操作时,会调用Rollback方法,从独立存储区把课程表的数据反序列化出来,替换Courses属性的现有数据。感觉上,这两个方法所做的事情不尽相同,然而,当你开始实际的编码时,就会发现它们的代码其实是一样的,按照惯例,我们会把这部分代码提取到一个辅助方法里:

代码 15

  这样,构造函数和Rollback方法只需调用这个辅助方法就行了。值得提醒的是,在应用程序首次启动时,课程表的数据文件还不存在,此时,我们只需为Courses属性创建一个空的集合就行了。当用户执行提交操作时,会调用Commit方法,把Courses属性序列化到独立存储区:

代码 16

  好了,数据存储的开发也完成了,但是,我们怎么把它和前面设计的用户界面关联起来呢?这正是接下来要讲的。

 

0
0
标签:WP7

手机开发热门文章

    手机开发最新文章

      最新新闻

        热门新闻