WP7有约(三):课堂重点
[2] WP7有约(三):课堂重点
[3] WP7有约(三):课堂重点
[4] WP7有约(三):课堂重点
[5] WP7有约(三):课堂重点
[6] WP7有约(三):课堂重点
标签
到目前为止,我们的笔记本只不过是一个很普通的笔记本,虽然我们提供了与标签相关的用户界面,但这部分功能还没真正实现出来。那么,如何实现这部分功能?
我们知道,当用户单击Application Bar上的显示标签按钮时,显示标签的ListBox将会滑出来,里面列出当前课程的标签,用户可以从中选择一个标签,此时,ListBox将会滑出去,笔记本的内容也将根据选中的标签进行筛选。从这里不难看出,我们需要在NoteListViewModel类里添加两个属性,一个用于存放当前课程的标签,另一个用于存放当前选中的标签:
代码 35
它们分别绑到ListBox的ItemsSource和SelectedItem两个属性:
代码 36
那么,当用户选好标签之后,我们如何筛选笔记?还记得我们是如何根据课程名称筛选笔记的吗?我们直接把课程筛选条件告诉CollectionViewSource,当数据源发生改变时,CollectionViewSource将会自动筛选,因此,我们不妨考虑把标签筛选条件整合进去,让CollectionViewSource一并处理:
代码 37
需要说明的是,当用户还没选择任何标签时,SelectedTag属性的值为null,此时,我们应该按照不做标签筛选的情况处理,否则看看笔记的标签是否包含用户选中的标签。看到这里,你可能会问,当用户选择一个标签时,数据源并未发生任何改变啊,CollectionViewSource应该不会自动筛选吧?这个问题问得好!事实上,它不会自动筛选,我们需要手动刷新一下它生成的视图:
代码 38
那么,如何初始化标签列表?
想想看,什么时候需要初始化标签列表?每次打开笔记本的时候肯定需要初始化标签列表,但除此之外呢?当用户新建或编辑笔记时,可能引入新的标签;当用户编辑或删除笔记时,可能删除现有标签,这些都会导致重新计算标签列表。既然计算标签列表的代码需要在这么多地方使用,我们当然应该把它提取到一个单独的方法里:
代码 39
计算标签列表的思路非常简单,首先,选取当前课程的笔记(忽略没有标签的),接着,从中提取标签,为了避免前/后空格的影响,这里使用Trim方法做了处理,然后,去掉空字符串以及重复的标签,最后,把标签添加到Tags属性。
现在,请思考一下,我们应该在哪调用ComputeTags方法?有些同学可能会说,这还不简单,分别在NoteListViewModel类的构造函数和三个操作的事件处理程序里调用不就行了?在NoteListViewModel类的构造函数和删除操作的事件处理程序里调用是没问题的,但在新建和编辑两个操作的事件处理程序里调用就有问题了,为什么呢?举个例子吧:
代码 40
你觉得上面代码的最后一行是在NewOrEditNotePage页显示之前还是之后执行呢?答案是之前,这意味着标签列表的计算在用户编辑笔记之前就完成了,这显然不是我们期望的效果。怎么办?
想想看,每次从NewOrEditNotePage页返回都会发生什么事呢?触发NoteBookPage页的Loaded事件!于是,我们可以把代码39里的最后一行放在Loaded事件处理程序里,不过,这样做会导致一个问题,每次从主菜单打开NoteBookPage页时,当前课程的标签列表会被计算两次,第一次是在NoteListViewModel类的构造函数里,第二次是在Loaded事件处理程序里,因为NoteBookPage页每次显示都会触发Loaded事件。怎么处理这个问题?最简单的办法是通过一个bool变量区分NoteBookPage页是否已经打开过。不过,既然我们的目的只是为了在标签发生改变时做些事情,为什么不直接监听Note对象的PropertyChanged事件呢?要实现这个效果,有三个事儿需要我们做的:
- 监听现有Note对象的PropertyChanged事件。
- 监听App.NoteStore.Items的CollectionChanged事件,一旦有新的Note对象添加进来就监听它的PropertyChanged事件。
- 监听App.NoteStore.Items的CollectionChanged事件,一旦现有的Note对象被删除就移除PropertyChanged事件的监听。
我们可以在PropertyChanged事件处理程序里调用ComputeTags方法。
虽然这种做法听起来有点复杂,但它避免了第一种做法的问题。本质上,这两种做法是等效的,只是一个在前台处理,另一个在后台处理,而正是这个角度的转变让我们对它们有了更进一步的了解。想想看,用户并非每次新建/编辑笔记之后都会单击Application Bar上的显示标签按钮,一个比较常见的使用情景用户把当天的笔记都输入了,然后通过标签的筛选来复习特定的内容,这样的话,在用户每次新建/编辑笔记之后重新计算标签列表显然没有必要。事实上,如果用户没有单击Application Bar上的显示标签按钮,我们根本没有必要计算标签列表,换句话说,我们可以把代码39里的最后一行放在显示标签按钮的Click事件处理程序里,从而实现按需计算:
代码 41
需要注意的是,当用户单击Application Bar上的显示标签按钮时,如果用户还没创建任何课程,直接调用ComputeTags方法将会引发异常,所以我们需要在调用之前判断一下。不过,这种做法也有个问题,试想一下,如果用户多次单击Application Bar上的显示标签按钮,其间没有新建/编辑任何笔记,那么,除了第一次计算标签内容是必要的,后面几次都是多余的。那么,如何才能避免多余的计算?看到这里,你可能会说,为什么不把后两种做法结合起来试一下呢,比如说,我们可以通过一个bool变量标识是否需要计算,然后在PropertyChanged事件处理程序里把它的值设为true,而在ComputeTags方法里,仅当这个变量的值为true时才执行计算,执行完毕之后把它的值设为false。嗯,这个主意不错,我就把它留给你当课后作业吧。
好了,不知不觉又到看效果的时候了!按F5运行应用程序,新建一条笔记:
图 38
单击Application Bar上的显示标签按钮:
图 39
单击页面空白处收回标签列表。再新建一条笔记:
图 40
这次,我们给它两个标签,其中一个标签是现有的,另一个是新的,并且分隔符后面有个空格。单击确定返回,然后单击Application Bar上的显示标签按钮:
图 41
再新建一条笔记:
图 42
现在,单击Application Bar上的显示标签按钮:
图 43
选择buying behavior:
图 44
再次单击Application Bar上的显示标签按钮,选择sales strategy:
图 45
非常好!不过,现在有个问题,我想显示所有笔记怎么办?