WP7有约(二):课后作业
编辑作业本
作业本支持的操作和课程表一样,包括新建、编辑、删除、保存所有更改和撤销所有更改,其中,新建和保存以ApplicationBarIconButton的方式放在Application Bar上,撤销所有更改以ApplicationBarMenuItem的方式放在Application Bar上,而编辑和删除则放在上下文菜单里:
图 29
为什么这样安排?当老师布置作业时,我们会掏出作业本记下作业,下课之后,当我们要做作业时,我们会掏出作业本看看要做哪些作业,换句话说,新建、保存和显示作业内容这三个功能已经可以满足用户绝大多数的需求了。新建和保存作为最常用的两个操作自然应该放在最显眼的位置,删除和撤销所有更改这两个操作基本上不会用到,至于编辑,一般情况下我们只是用来修改作业的完成状态,由于编辑和删除是针对特定作业的,我们把它们放在上下文菜单里,当用户长按某项作业时将会显示出来,而撤销所有更改则隐藏在Application Bar的菜单里。
接着,创建一个Windows Phone Page,并把它命名为NewOrEditAssignmentPage.xaml,这个页面会在用户单击Application Bar上的新建按钮或者上下文菜单上的编辑菜单项时显示。完了之后把ApplicationTitle的Text属性值改为"作业本",但PageTitle保留原样:
图 30
那么,这个页面应该放些什么控件呢?想想看,创建一个完整的Assignment对象需要哪些数据?Id是自动生成的,课程名称可以从上下文获取,创建日期可以从DateTime的Today属性获取,剩下的就是截止日期、作业内容和完成状态了。截止日期可以使用SL for WP Toolkit的DatePicker控件,作业内容可以使用TextBox控件(上面的标题需要额外添置TextBlock控件),而完成状态则可以使用CheckBox控件:
图 31
看到这里,你可能会问,为什么不把其它信息也显示出来呢?你可以这样做,但是,请注意,这个页面的主要目的是收集而不是显示信息,我们应该尽可能简化用户的输入过程,在这里放置控件显示其它信息,尤其是可编辑的控件,可能会耗费用户额外的注意力,比如说,有些用户会下意识地检查所有数据是否输入正确。创建作业的过程应该是既简单又快速的,而我们也希望用户能有这样的感受,但耗费用户额外的注意力意味着增加整个操作过程的时间,从而可能导致用户的感受和我们期望的刚好相反,这是我们不希望看到的。
ViewModel类方面,我们将会仿效课程表的做法,创建NewOrEditAssignmentViewModel、NewAssignmentViewModel和EditAssignmentViewModel三个类:
图 32
我们知道,NewOrEditAssignmentPage页有两个模式,一个是新建模式,另一个是编辑模式,前者对应NewAssignmentViewModel类,而后者则对应EditAssignmentViewModel类。当用户新建一项作业时,NewAssignmentViewModel类可以从DateTime的Today属性获取创建日期,但它没法获取课程名称,所以我们需要通过参数传给它:
代码 35
为什么DueDate属性也要设置呢?想想看,如果我们不给它设置一个值,由于DateTime是值类型,将被自动初始化为"1/1/0001",当用户看到页面上的DatePicker控件显示这样一个日期可能会感到不友好,再者,老师布置下来的作业一般不会当天交(课堂作业除外),而第二天交的情况则比较常见(当然,计算下一个"上课日"可能更加合理)。而当用户编辑一项作业时,EditAssignmentViewModel类将会从数据源里查找这项作业的数据,但前提是我们把作业的Id告诉它:
代码 36
需要说明的是,Assignment类的Id属性是只读的,而Assignment类原来的构造函数会在每次调用时创建一个新的Id,这导致了我们无法使用现有的Id,所以我们需要在Assignment类里添加下面这个构造函数:
代码 37
创建好ViewModel类之后,我们就可以着手处理它们和NewOrEditAssignmentPage页之间的关联了。首先是设置数据绑定,需要设置的控件以及对应的绑定表达式如下表所示:
描述 |
类型 |
属性 |
绑定表达式 |
页面标题 |
TextBlock |
Text |
{Binding Title} |
截止日期 |
DatePicker |
Value |
{Binding Assignment.DueDate, Mode=TwoWay} |
作业内容 |
TextBox |
Text |
{Binding Assignment.Content, Mode=TwoWay} |
完成状态 |
CheckBox |
IsChecked |
{Binding Assignment.IsCompleted, Mode=TwoWay} |
表 3
接着,重写OnNavigatedTo方法:
代码 38
最后,为两个按钮创建事件处理程序:
代码 39
由于这部分内容和上节课的大同小异,这里就不详细解释了。
接下来是实现前面提到的五个操作。首先是最常用的新建和保存。保存操作非常简单,我们只需为它创建一个事件处理程序就行了:
代码 40
而新建则有点难度,我们需要获取课程名称,怎么获取?我们知道,课程名称实际上就是Pivot项的标题,也就是AssignmentListViewModel的Title属性,只要我们知道当前显示的是哪个AssignmentListViewModel对象就可以了。为此,我们需要在AssignmentBookViewModel类里添加一个SelectedListIndex属性:
代码 41
并为它和Pivot控件的SelectedIndex属性设置双向绑定。此外,需要说明的是,为了使用RaisePropertyChanged方法,我们需要让AssignmentBookViewModel类继承NotificationObject类。有了这些准备,我们就可以创建事件处理程序了:
代码 42
需要说明的是,如果课程表里面没有课程,Pivot控件就不会创建Pivot项,所以在做进一步处理之前,我们需要判断AssignmentLists里面有没有东西。
好了,又到看效果的时候了!按F5运行应用程序,在主菜单里单击"作业本"菜单项进入作业本:
图 33
单击Application Bar上的新建按钮创建一项作业:
图 34
单击确定返回:
图 35
Oh,My Lady Gaga!我的作业呢??