WP7有约(一):课程安排

  你好,老七!

      WP7终于发布了,到目前为止,有关它的新闻和介绍我相信你已经看过不少了,所以这里将会直接跳过,不过在开始之前,我认为还是有必要提醒你做好相关的准备:

  • Expression Blend 4 for Windows Phone和Visual Studio 2010 Express for Windows Phone,你并不需要完整的Expression Studio 4 Ultimate和Visual Studio 2010 Ultimate,不过如果你有的话*可能*会更好。
  • 白开水,大量白开水,接下来你将会与我一起进行大量脑力活动,你需要补充足够的水分才能让大脑更好地工作。
  • 零食,最好是坚果类,薯片也可以,人无法长时间集中精力,也不该迫使自己长时间集中精力,当你感到注意力开始涣散时,不妨抓一把零食放到嘴里嚼,注意别弄到键盘上哦。
  • 最后,也是最重要的,你,没错,是你,仅当你准备好接受新的知识时,你的大脑才会对它们进行积极的处理,否则就会把它们挡在外面。

  那么,你准备好了吗?

      首先,打开Expression Blend,创建一个Windows Phone Panorama Application项目:

图 1

  项目创建好之后,你会看到一个充满整个页面的Panorama控件,里面有两个Panorama项,每个Panorama项里面有一个ListBox,而ListBox里也有了示例数据。你可以调整Artboard的缩放比例,以便显示整个UI:

图 2

  注意,这里所说的整个UI是指手机屏幕所能显示的部分,而Panorama控件具有延伸到屏幕以外区域的特性,所以我们无法一次过把整个Panorama控件尽收眼底,这确实是一件憾事。

  接着,我们来看看Panorama控件,如果你对它的效果没有感性认识,不妨到先看看WP7的6个内置Hub。认识Panorama控件的最简单方法是结合Objects and Timeline面板和Artboard来体验一下:

图 3

  如上图所示,每个Panorama控件都是由一个标题和若干Panorama项构成的,而每个Panorama项又会包含一个标题和一些内容,在这里,这些内容是通过ListBox来展示的,你可以根据实际的需要把它换成任何其它控件。此外,需要说明的是,Panorama控件和Panorama项的标题都已经内化成自身的属性,只需通过Properties面板设置就可以了,无需额外添加TextBlock或者其它控件。现在,我们的Panorama控件包含了两个Panorama项,但从上图可以看到,只有第一个能完全显示出来(由于截图的关系,Artboard的一部分隐藏在滚动条下面),而第二个只能看到一小部分,那么,如何才能显示第二个Panorama项,以便操作上面的控件呢?答案非常简单,只需在Objects and Timeline面板上单击第二个Panorama项就可以了:

图 4

  值得提醒的是,为了在操作时不影响其它Panorama项,我们还可以通过Objects and Timeline面板把其它Panorama项锁定,正如上图所示的那样。在继续阅读下面的内容之前,我强烈建议你稍稍暂停一下,把注意力集中在Objects and Timeline面板上,熟悉一下各个对象之间的关系,试着单击每个对象,然后看看它对应了Artboard上的哪个对象。如果你已经迫不及待想要亲自体验一下Panorama控件的效果,你现在可以按F5了。

  接下来,我们要执行以下任务:

  • 修改Panorama控件的标题
  • 去掉Panorama控件的背景
  • 删除现有的两个Panorama项
  • 添加一个新的Panorama项

  第一个任务非常简单,确保Objects and Timeline面板上的Panorama控件处于选中状态,在Properties面板上的搜索框里输入Title,第一个搜索结果就是我们要找的属性了,修改这个属性的值,然后按回车:

图 5

  第二个任务也挺简单,在Properties面板上的搜索框里输入Back,然后选择No brush就可以了:

图 6

  第三个任务更简单,按下Ctrl键,依次选中两个Panorama项,然后按Del键就可以了。最后一个任务是添加新的Panorama项,打开Assets面板,在搜索框里输入Pan:

图 7

  然后把PanoramaItem拖到Panorama控件上就可以了。注意,你可以把PanoramaItem拖到Objects and Timeline面板的Panorama控件上,也可以拖到Artboard的Panorama控件上,如果Artboard上的控件比较多,并且把Panorama控件挡住了,那么当你把PanoramaItem拖到Artboard上时,有可能会把它误加到其它控件上。这是添加控件的一般方法,针对添加PanoramaItem,我们还有更简单的方法,那就是右击Panorama控件,然后选择Add PanoramaItem就可以了:

图 8

      现在,向Panorama项添加一个TextBlock,内容随你,调整一下位置和大小,然后按F5:

图 9

  一般地,Panorama控件至少包含两个Panorama项,而这里只有一个,属于边界情况,细心观察上图,表面上,右边好像还有一个Panorama项,但当你在屏幕上向左滑动时,你会发现这其实是同一个Panorama项。那么向右滑动呢?情况一样。利用这个特点,我们可以创建一个简易计数器,把Panorama项的TextBlock绑定到一个计数变量上,当我们向左滑动时,计数变量加1,向右时则减1,其效果就像我们拥有一个无限延伸的Panorama控件,而边界情况就是这个计数变量的最大值和最小值,尽管如此,我们也无需太过担心,假设计算变量的类型是Int32,我相信没有人会向左或者向右滑动超过20亿次吧?如果你有兴趣的话,不妨把它当做课后练习。现在,按Back退出应用程序。

 

 

  上课啦!

      上课啦?什么课?哪里上?看到这些问题,有没有一种亲切的感觉?说不定你今天就问了这些问题哦,那时你是不是在找课程表呢?如果课程表就在手机里该多好啊!事不宜迟了,我们自己弄一个吧。

      右击Projects面板里的项目节点,选择Add New Item:

图 10

  在弹出的New Item对话框里选择Windows Phone Pivot Page,输入页面的名字,然后按OK:

图 11

  和Panorama页一样,Pivot页也有一个充满整个页面的Pivot控件,刚创建好的Pivot控件默认附带两个Pivot项,我们可以把它们分别用于星期一和星期二。确保Pivot控件处于选中状态,在Properties面板上寻找Title属性,并把它的值改为"课程表":

图 12

  接着在Objects and Timeline面板上选中第一个Pivot项,在Properties面板上寻找Header属性,并把它的值改为"星期一":

图 13

  完了之后把第二个Pivot项的Header属性值改为"星期二"。此时,你的 Pivot控件应该是这样的:

图 14

  嗯,看起来像个样了,然而,标题下面那么大的一块空位应该怎么处理呢?毫无疑问,以列表的方式呈现一天的课程是比较适合的,但是,我希望列表的每一项除了显示课程名称之外,还能显示上课时间和上课地点。

      在继续设计UI之前,我们需要导入一些示例数据,以便在设计时就能看到最终效果。当然,你也可以让Expression Blend为你生成这些数据,不过,它无法为我们生成课程名称以及适合的上课地点,这样,当你在设计时调整控件外观时就会感到缺了点儿什么,而这正是使用真实数据的好处。

      假设我们要导入下面这个XML文件的数据:

代码 1

  我们可以在Data面板上单击Create sample data按钮,然后选择Import Sample Data from XML…:

图 15

  在弹出的Import Sample Data from XML对话框里,单击Browse按钮浏览并指定数据文件,然后按OK关闭对话框:

图 16

  此时,Expression Blend会很努力地在后台帮你生成一大堆东西,等它做完之后,你会看到Data面板上多了一堆东西,现在,确保Data面板上的List Mode按钮处于按下状态,然后把courseCollection拖到Pivot项标题下面的空白处:

图 17

  此时,Expression Blend会为你创建一个ListBox,并把它的ItemsSource属性绑定到courseCollection上,现在,右击ListBox里的任何地方,然后选择Auto Size/Fill,以便让ListBox充满整个Grid(Pivot项默认有一个Grid子元素):

图 18

  嗯,不错,每个列表项都包含了课程名称、上课时间、下课时间以及上课地点,可是,这些内容各占一行,字体大小也是一样,每个列表项之间又没有明显的间距,显然不是那么好看,下面我们给它调整一下,右击ListBox里的任何地方,然后选择Edit Additional Templates/Edit Generated Items (ItemTemplate)/Edit Current进入列表项模板的编辑状态:

图 19

  此时,Objects and Timeline面板会发生变化,上面的对象不再是我们之前看到的那些,而变成列表项里的对象:

图 20

  从上图不难看出,每个列表项都包含了四个TextBlock,这些TextBlock是用一个StackPanel装着的。现在,你可以发挥你的创造力,把它调整成你喜欢的样子,下面是我的调整结果:

图 21

  此时,Objects and Timeline面板上面的对象应该是这样的:

图 22

  单击上图红框那个箭头退出列表项模板的编辑状态。此时,Objects and Timeline面板回复"原状"了,你可以在上面看到Pivot控件和Pivot项。选中第二个Pivot项,按照上面的步骤把星期二的课程数据导入,并把它拖到Pivot项上(注意,是第二个Pivot项哦),然后调整ListBox的大小,使之充满整个Pivot项。那么,列表项的显示方式怎么办?要重复编辑一次吗?当然不用!我们只需应用刚才那个就可以了。右击ListBox里的任何地方,然后选择Edit Additional Templates/Edit Generated Items (ItemTemplate)/Apply Resource/courseCollectionItemTemplate:

图 23

  此时,你会发现列表项的风格已经变成和前面的一样了:

图 24

  好了,我们现在可以把其它几天的课程都加上去,然后在主页(即第一个页面)添加一个按钮打开这个页面就……慢着!我怎么编辑课程表?

 

 

  编辑课程表

      显然,如果这个课程表不能编辑,那么它就等同花瓶了,所以我们要为它增加编辑功能,包括新建、编辑和删除,我们可以把这些功能放在Application Bar上。在Expression Blend里添加Application Bar非常简单,右击Objects and Timeline面板上的PhoneApplicationPage,然后选择Add ApplicationBar:

图 25

  接着右击ApplicationBar,然后选择Add ApplicationBarIconButton:

图 26

  确保刚才添加的Application Bar按钮处于选中状态,在Properties面板上把它的IconUri属性值改为New,并把它的Text属性值改为"新建":

图 27

  按照上面的步骤添加另外两个按钮,完成之后你的课程表页面应该是这样的:

图 28

  删除功能只需获取选中的课程并把它删除就可以了,新建和编辑则不同,它们都需要另一个页面来处理,我们知道,新建功能和编辑功能在用户界面上的最大区别是前者的页面有内容而后者的没有,所以我们可以为它们创建一个共用页面。

      创建一个Windows Phone Page,并把它命名为NewOrEditCoursePage.xaml:

图 29

  完了之后把ApplicationTitle的Text属性值改为"课程表",但PageTitle保留原样:

图 30

  因为新建功能和编辑功能共用同一个页面,所以PageTitle的Text属性值可能是新建课程或者编辑课程,这将会在打开此页面时通过传入参数设置。标题下面那块空地将会放置四个控件,分别对应课程名称、上课时间、下课时间和上课地点,首尾两个将会是TextBox控件,而中间两个将会是Silverlight for Windows Phone Toolkit的TimePicker控件。

      右击Projects面板上的References节点,选择Add Reference…:

图 31

  在弹出的Add Reference对话框里把C:/Program Files/Microsoft SDKs/Windows Phone/v7.0/Toolkit/Sep10/Bin/Microsoft.Phone.Controls.Toolkit.dll引用进来。添加完引用之后就可以把控件添加到页面了:

图 32

  需要说明的是,TimePicker控件已经自带标题功能,你只需设置它的Header属性就可以了,而普通的TextBox没有标题功能,只能自行添加TextBlock来模拟,为了使它的颜色和TimePicker控件的标题的颜色一样,我们需要把它的Foreground属性值改为PhoneSubtleBrush。此时,我的Objects and Timeline面板是这样的:

图 33

  好了,课程表的用户界面已经设计完了,是不是很想看看运行效果呢?没问题!

      回到CourseTimetablePage页,在Objects and Timeline面板上选中第一个Application Bar按钮,然后在Properties面板上单击Events,并双击Click旁边的编辑框:

图 34

  此时,Expression Blend会打开CourseTimetablePage页的代码隐藏文件,并为Application Bar按钮添加一个事件处理程序方法,我们只需在TODO下面加上一句就可以了:

代码 2

  接着,回到MainPage页,把上面的TextBlock去掉,拖一个Button到中间,然后右击这个Button,选择Navigate To/CourseTimetablePage:

图 35

  好了,按F5吧:

图 36

  当你单击屏幕中间那个按钮时,课程表就会显示:

图 37

  向左或者向右滑动屏幕可以在不同的Pivot项之间来回切换,而向上或者向下滑动屏幕则可以查看当天课程。当你单击Application Bar上的新建按钮时,新建课程表的界面将会显示:

图 38

  你可以输入课程名称和上课地点,当你单击上课时间或者下课时间下面那个TimePicker控件时,设置时间的界面将会显示:

图 39

  你可以通过滑动设置时间,这个界面下面有两个Application Bar按钮,左边那个是确定,右边那个是取消,但为什么这两个图标是一样的?其实它是找不到图标才这样的,如果你下载了它的代码,你会在TimePickerPage.xaml里看到它已经把图标位置硬性规定为/Toolkit.Content/ApplicationBar.Check.png和/Toolkit.Content/ApplicationBar.Cancel.png了,你可以在PhoNEToolkitSample/Toolkit.Content文件夹里找到这两个图标,在项目里创建一个Tooklit.Content文件夹,把它们复制进去,并把它们的Build Action设置为Content,重新运行就能看到了。

      到目前为止,我们只写了一行代码(其实这行代码也可以省掉的),应用程序的功能和操作就基本上体现出来了,此时,你可能会问,在我们设计用户界面的过程里,Expression Blend到底在背后为我们做了什么呢?

 

 

  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

上课地点

 

  把前端和后端连接起来

      还记得Expression Blend是怎么做的吗?它为MainPage页创建一个与之对应的MainViewModel类,并在代码隐藏文件里把后者的实例绑到前者的DataContext属性上,而剩下的事情就交给数据绑定来处理。接下来,我们将会模范这种做法,把前端和后端连接起来。

      我们知道,一个课程表包含若干列,每列都包含了一个标题和一组当天的课程,整个课程表对应于CourseTimetablePage页,里面的每列对应于一个Pivot项,其中,列的标题将会作为Pivot项的标题显示,而列所包含的那组当天的课程将会在Pivot项所包含的ListBox里显示。为了方便理解,我们把它们之间的映射关系制成下表:

页面

页面的抽象模型

CourseTimetablePage页

CourseTimetableViewModel类

Pivot项

CourseTimetableColumnViewModel类

Header属性

Header属性

ListBox控件

Courses属性

 

  操作课程表

      我们知道,新增和修改这两个操作是共用同一个页面的,但它们的内部逻辑又稍微有点不同, 你可以分别为它们创建两个不同的ViewModel类,针对不同的操作为页面创建不同的ViewModel对象,也可以在同一个ViewModel类里通过标记变量和条件语句区分两种不同的逻辑,你还可以像我这样,在ViewModels文件夹里创建一个NewOrEditCourseViewModel抽象类,让它实现INotifyPropertyChanged接口,并创建Title和Course两个属性以及Submit和Discard两个抽象方法:

代码 32

  接着,在NewOrEditCourseViewModel类里创建NewCourseViewModel和EditCourseViewModel两个私有类,并让它们继承NewOrEditCourseViewModel类。下面,我们先看NewCourseViewModel类。

      课程表上的每个课程都会关联到一周的某天,但NewOrEditCoursePage页上却没有地方设置星期几(参见图32),这是为什么呢?Pivot控件的一个特点是每次只能显示一个Pivot项,这意味着整个课程表每次只能显示一天的课程,如果把这天看做上下文,当用户单击Application Bar上的新增按钮时,应用程序就可以从上下文获知应该把课程添加到哪一天,从而为用户省下设置星期几这个步骤。我们可以通过参数传递这个数据,然后在构造函数里使用它创建Course对象:

代码 33

  当用户单击确定时,就会把课程添加到JsonCourseStore,而单击取消的话就什么都不做:

代码 34

      那么,EditCourseViewModel类呢?当用户单击Application Bar上的编辑按钮时,它需要的不是今天星期几,而是用户当前选中的课程是什么,我们又该如何告诉它呢?我们知道,同一天的课程在时间上是互斥的,因为同一时间你不可能在不同教室上课(除非你懂影分身术),换句话说,Course类的Day和StartTime这两个属性组合起来可以成为唯一标识。通过这个唯一标识,我们可以从JsonCourseStore里获取用户当前选中的课程,但是,我们是否把获取到的课程直接赋给EditCourseViewModel的Course属性呢?想想看,Course属性将会和NewOrEditCoursePage页上的控件进行双向绑定,当用户编辑控件的内容时,数据会直接反映在Course属性上,如果Course属性就是从JsonCourseStore里获取到的课程,那么数据就会直接提交到JsonCourseStore。如果你选择这样做,你得先做个备份,假如用户单击取消,你就可以把备份的数据还原回去。另一个做法是从JsonCourseStore里获取用户当前选中的课程,然后克隆一份赋给Course属性,当用户单击确定时,就把Course属性的数据更新过去,而单击取消的话也是什么都不做。这里选择后面那种做法:

代码 35

  创建好NewCourseViewModel和EditCourseViewModel两个私有类之后,我们需要考虑一下如何访问它们的实例,办法可能有很多,其中最简单的是在NewOrEditCourseViewModel类里创建两个静态方法,分别用于创建这两个类的实例:

代码 36

      有了实例之后,我们就要考虑数据绑定的问题了,这个不难处理,我们总共也只有五个绑定需要创建,你可以参照下表修改NewOrEditCoursePage.xaml的内容:

描述

类型

属性

绑定表达式

页面标题

TextBlock

Text

{Binding Title}

课程名称

TextBox

Text

{Binding Course.Name, Mode=TwoWay}

上课时间

TimePicker

Value

{Binding Course.StartTime, Mode=TwoWay}

下课时间

TimePicker

Value

{Binding Course.EndTime, Mode=TwoWay}

上课地点

TextBox

Text

{Binding Course.Location, Mode=TwoWay}

 

  菜单·样品菜色

      还差什么呢?噢,对了,目前我们是通过主页中间的按钮打开课程表的,这显然不好意思拿出来见人,我们还是创建一个正式的主菜单吧。现在让我们切换到Expression Blend,如果你的Expression Blend已经关闭了,你可以右击Solution Explorer的MainPage.xaml,然后选择Open in Expression Blend:

图 55

  菜单的制作方式有很多种,这里选用ListBox:

图 56

  菜单创建好后,右击里面的"课程表",然后选择Navigate To/CourseTimetablePage,此时,Expression Blend会为TextBlock添加一个NavigateToPageAction行为,但这个行为默认是关联到MouseLeftButtonDown事件的,这样,当用户按下"课程表"还没松开手就打开课程表了,而我们的习惯是松手之后才执行相关的操作,为了实现这个效果,我们可以把关联事件改为MouseLeftButtonUp:

图 57

      菜单有了,但样品菜色却被我们弄没了,如果你现在打开CourseTimetablePage.xaml,你将会看到此番情景:

图 58

  之前我们手动创建两个Pivot项,然后把它们分别绑到两个示例数据源,但现在Pivot项是通过数据绑定动态创建的,之前那些示例数据源就排不上用场了,既然用不了,那就删了吧,打开Data面板,右击数据源,然后选择Delete data source:

图 59

  此时,Expression Blend会把SampleData文件夹里的相关文件一并删除。

      在Expression Blend里,没有设计时数据会为调整控件模板和样式带来很大不便,下次你去找设计师调整一下用户界面,他们很可能会对你大骂一顿,那么,有没有办法让它再次显示设计时数据呢?答案是有的,而且不止一种,下面来看其中一种。我们知道,CourseTimetablePage页使用了数据绑定,而绑定表达式只提及了绑定的属性,并未提及属性所在的类型,换句话说,只要属性名字能够匹配,示例数据就应该绑得上去。首先,准备一份XML,注意匹配绑定表达式的属性名字:

代码 48

  接着,在Data面板上把它导入(参见图15),注意,这次要把Enable sample data when application is running选项去掉:

图 60

  导入之后在Data面板上把自动生成的ColumnCollection和CourseCollection分别改为Columns和Courses,以便匹配绑定表达式的属性名字:

图 61

  现在,把Timetable拖到Pivot控件上,此时你会看到鼠标下方有个小提示,告诉你Expression Blend将会把Pivot控件的DataContext属性绑到CoursesSampleDataSource:

图 62

  当你松开鼠标时,就会看到课程名称了,但上课时间、下课时间和上课地点却显示不出来:

图 63

  这是为什么呢?如果你把CourseTimetablePage页面关闭,然后重新打开它,你就会看到问题所在了:

图 64

  这个异常明确地告诉我们调用转换器时抛出InvalidCastException。还记得吗,我们的转换器会把传入对象强制转换成DateTime对象,然后调用它的ToShortTimeString方法,但Expression Blend为示例数据生成的StartTime属性和EndTime属性是字符串类型的!明白为什么会这样,问题就不难解决了:

代码 49

  重新编译项目,然后重新打开CourseTimetablePage页,你就会看到设计时数据了:

图 65

  好了,总算对设计师有个交代了。

  下课了……

it知识库WP7有约(一):课程安排,转载需保留来源!

郑重声明:本文版权归原作者所有,转载文章仅为传播更多信息之目的,如作者信息标记有误,请第一时间联系我们修改或删除,多谢。