WP7有約(二):課后作業

作者: Allen Lee  來源: 博客園  發布時間: 2010-12-20 23:06  閱讀: 2866 次  推薦: 1   原文鏈接   [收藏]  
 

  作業本

      上節課布置的作業有做嗎?沒人吭聲啊,看來大家都忘了哦,沒事,我們這次弄個作業本出來,大家就有地方記作業了。在開始設計應用程序之前,我們先來看看通常的作業本是怎樣記作業的:

圖 1

  從上圖可以看到,作業本有點像日記本,每次記錄時都會寫下當天的日期,每天的作業又會根據課程進行歸類。慢著!我怎么知道這些作業什么時候交?一般情況下,中小學生的作業都是第二天上課時交的,但大學生就不同了,他們的作業可能第二天交,也可能一周之后交,有時甚至幾周之后才交,更重要的是,不同的作業可能在不同的時間交。換句話說,我們的應用程序還需要支持記錄交作業的時間。此外,每當完成一項作業,我們可以在旁邊做個記號,這樣,當我們打開作業本時,即使作業再多也能馬上知道哪些還沒做完。

      現在,用Visual Studio打開項目,在Models文件夾里創建一個Assignment類,和上節課的Course類一樣,它也需要實現INotifyPropertyChanged接口。由于我們有很多類都需要實現INotifyPropertyChanged接口,為了避免不必要的重復,你可以考慮創建一個類專門實現這個接口,然后讓有需要的類繼承這個類。這個需求似乎比較常見,因此Prism提供了一個NotificationObject類,我們只需繼承它就行了:

代碼 1

  繼承之前別忘了引用Bin\Phone\Microsoft.Practices.Prism.dll類庫和Microsoft.Practices.Prism.ViewModel命名空間哦。根據前面的討論,Assignment類應該包含以下屬性:

屬性名字

屬性類型

備注

Id

Guid

唯一標識

CourseName

string

課程名稱

StartDate

DateTime

創建日期

DueDate

DateTime

截止日期

Content

string

作業內容

IsCompleted

bool

完成狀態

表 1

  我們知道,Id屬性作為唯一標識,其值一旦生成就不會改變,因此我們只需在構造函數里初始化它就行了:

代碼 2

  而其它屬性則需要在它們的set訪問器里調用從NotificationObject類繼承過來的RaisePropertyChanged方法,比如說,我們可以這樣實現IsCompleted屬性:

代碼 3

  看到這里,你可能會說,作業的狀態應該不止"已完成"和"未完成"兩種啊,比如說,當老師剛把作業布置下來時,它應該是"未開始";當我們開始做某項作業時,它應該是"進行中";有時候準備工作還沒好,我們不得不把作業推遲,此時它應該是"已推遲";有時候老師可能大發慈悲說某些作業不用做了,此時它應該是"已取消",等等等等。照這樣說,我們是否也該考慮把現在的兩個日期細化為"計劃開始日期"、"計劃結束日期"、"實際開始日期"和"實際結束日期",然后加上一個"作業進度"什么的?千萬不要這樣,沒有學生愿意采用這么細致的作業管理方案,再說這樣做也會分散他們的注意、加重他們的負擔,作業本的主要目的只有一個,就是讓學生對要做哪些作業一目了然,所有功能的設計都應該圍繞這點展開,所有功能的取舍也應該以此為標準。

  保存作業本

      數據存儲方面,我打算仿效課程表的做法,通過JSON序列化把作業本的數據保存到獨立存儲區,實現這個并不難,你可以照搬課程表的做法,創建一個IAssignmentStore接口和一個JsonAssignmentStore類。當你實現完JsonAssignmentStore類之后,你將會發現它和JsonCourseStore類有99.9%的代碼是相同的,事實上,你可以把JsonCourseStore.cs文件復制一份,并重命名為JsonAssignmentStore.cs,然后把里面的"Course"字眼都替換成"Assignment"就可以了。不過,這種重復著實讓人不爽啊,看來是時候重構一下了。

      ICourseStore接口和IAssignmentStore接口的區別只在于集合元素的類型和集合屬性的名字,前者可以通過泛型統一起來,至于后者,我們可以把屬性的名字統一為Items,這樣,兩個接口就能統一起來了:

代碼 4

  而實現方面,我們可以創建一個JsonDataStore<T>類,并讓它實現IDataStore<T>接口:

代碼 5

  需要說明的是,之前我們把文件名硬編碼在JsonXXXStore類里,那是因為它對于JsonXXXStore類來說是固定的、一對一的,而現在的JsonDataStore<T>類不再僅僅對應一個文件,因此我們把它保存在一個私有字段里。其它的和JsonAssignmentStore類沒有太大出入。

      看到這里,有些同學可能會問,ICourseStore接口和JsonCourseStore類已經投入使用了,現在換用IDataStore<T>接口和JsonDataStore<T>類會不會造成很大影響?這個問題問得好,如果你確實不想修改其它代碼,那你可以把JsonCourseStore類改造成JsonDataStore<Course>類的"馬甲":

代碼 6

  需要說明的是,我們通過繼承JsonDataStore<Course>類獲得Rollback和Commit兩個方法的實現,此外,由于其它代碼是通過ICourseStore接口間接使用JsonCourseStore類的實例的,于是我們保留了ICourseStore接口,并把Courses屬性重定向到Items屬性。

      不過,就項目現在的規模而言,我們可以把重構做的更徹底一些,我們可以把ICourseStore.cs和JsonCourseStore.cs兩個文件刪除,如果你想保險一點,可以先把它們從項目排除出去,然后重新編譯,此時Visual Studio會告訴你找不到ICourseStore接口和JsonCourseStore類,分別把它們替換成IDataStore<Course>接口和JsonDataStore<Course>類,調用后者的構造函數時記得提供文件名,即Courses.json,重新編譯,此時Visual Studio會顯示一堆錯誤,全部都是說找不到Courses屬性的,把它們都替換成Items屬性,重新編譯,好了,如果那兩個文件還沒刪除的話,現在可以安全刪除了。

  原型

      現在是時候考慮一下用戶界面了,仔細觀察我們的作業本(圖1),是否覺得這種布局方式有種似曾相識的感覺?如果你一直關注WP7的相關消息,你可能已經看過類似的用戶界面了——People Hub的聯系人列表。下面我們把它們兩個放在一起看看:

圖 2

  從上圖可以看到,作業列表和聯系人列表剛好能夠對應起來,課程名稱對應姓氏首字母,作為分組標題,而作業內容則對應聯系人,作為分組內容。看到這里,你可能會問,WP7的Silverlight貌似沒有這樣的控件啊,難道要我們自己動手弄一個?原本是沒有的,不過十一月發布的SL for WP Toolkit已經增加了這個控件,名字叫做LongListSelector。上節課我們使用了Silverlight for Windows Phone Toolkit的TimePicker控件,當時引用的是九月份發布的版本,現在你可以下載新的版本,然后重新引用一下。

      仔細觀察上圖,你會發現作業列表上面有個日期沒法對應到聯系人列表,我們該怎么處理這個日期呢?這個問題問得好,事實上,這正是作業列表和聯系人列表的最大區別,我們知道,聯系人列表只有一份,但作業列表卻會有很多份,每份都會有一個不同的日期,這些作業列表共同組成了一本作業本。如果把每份作業列表看作一個由標題和LongListSelector控件組成的頁面,那么整個作業本就可以看作由N個這樣的頁面組成的應用程序了,但我們不必真的創建N個這樣的頁面,我們可以仿效課程表的做法,利用Pivot控件的特點,讓每個Pivot項顯示一份作業列表,這樣Pivot項的標題可以用來顯示作業列表上面的日期,而標題下面則通過LongListSelector控件顯示每個課程的作業。不過,這樣的設計是否真的妥當呢?

      試想一下,如果我們首先通過日期來劃分Pivot項,接著通過課程來劃分作業,那么每次我們要新建作業的時候,我們可能得先創建一個Pivot項,如果對應今天的Pivot項還沒有的話,接著指定作業所屬的課程,最后才填寫和作業相關的信息,這個過程顯然有點繁瑣,我們應該盡可能簡化其中的步驟。說到這里,有些同學可能會建議,不如讓應用程序自動創建今天的Pivot項,這樣至少可以省掉一個步驟。嗯,這個主意值得考慮,不過,并非每天都會有作業,比如說,今天是星期天,我進入作業本只是看一下這個周末有哪些作業,但應用程序卻自動為我創建了今天的的Pivot項,而這并非我想要的,這意味著應用程序不得不在退出的時候把這個空的Pivot項刪除。事實上,對于大學生來說,尤其是大三、大四的,今天有課明天沒有是很常見的,難道要讓用戶設置哪天有課哪天沒課,或者干脆直接解釋課程表的數據,看看哪天有課哪天沒課?從上面討論不難看出,日期這個因素很不穩定,不太適合用來劃分Pivot項,但課程就不同了,一旦課程表創建好了,作業本上會有哪些課程的作業也就定下來了,既然這樣,何不把分組的順序換一下?如果我們通過課程來劃分Pivot項,那就不用考慮Pivot項的創建和刪除了,因為用戶在訪問作業本的過程中會涉及到哪些課程是確定的,此外,當用戶新建作業時也無需額外的步驟來指定今天的日期,因為這可以從DateTime的Today屬性獲取,這樣我們就為用戶省下兩個步驟了。從這里我們可以看到,應用程序的設計絕對不是把控件堆砌起來顯示數據就完事了,它包含的是一組完整的用戶體驗,而不同的組織方式可能會產生完全不一樣的用戶體驗,有時候多一兩個步驟好像沒什么大不了,但假如這一兩個步驟要重復十次的話,用戶就要額外執行十幾二十個這樣的步驟了,要么你為用戶省下這些步驟,要么你讓競爭對手為用戶服務。

      現在讓我們切換到Expression Blend,創建一個Windows Phone Pivot Page,并把它命名為AssignmentBookPage.xaml,完了之后把Pivot控件的Title屬性設為"作業本",把兩個Pivot項的Header屬性分別設為"數學"和"英語",最后把一個LongListSelector控件拖到第一個Pivot項里:

圖 3

  接下來我們要為LongListSelector控件定制作業的顯示方式,而執行這個任務的最佳場所是Expression Blend,但要發揮Expression Blend的潛能,我們需要準備一些示例數據,那么我們是否可以像上節課那樣導入一些XML數據,然后把它們拖到LongListSelector控件上呢?很遺憾,不行,因為LongListSelector控件對于需要進行分組顯示的數據源有特別要求。你可能以為我們只需把一個作業集合賦給ItemsSource屬性,然后指定集合元素的某個屬性作為分組依據,LongListSelector控件就會自動為我們分組,但事實并非如此,LongListSelector控件要求我們先把數據分組好,然后把這些分組湊成一個集合賦給ItemsSource屬性,而且硬性規定每個分組至少實現IEnumerable接口,否則初始化時將會因為轉換失敗而拋出InvalidCastException異常,此外,為了便于顯示分組標題,每個分組最好有個屬性保存標題的內容,那么我們如何創建這樣的數據源?其實創建這樣的數據源并不難,LINQ的group XXX by YYY完全可以勝任這項任務,難處在于我們還想讓它在Expression Blend的設計器上顯示,所以我們得費一點兒周折了。

      首先,切換到Visual Studio,在ViewModels文件夾里創建一個AssignmentListViewModel類,并讓它繼承NotificationObject類:

代碼 7

  接著,創建一個GetAssignments方法,返回一些Assignment對象:

代碼 8

  然后,再創建一個AssignmentGroups屬性,通過LINQ選取全部數學作業并根據創建日期進行分組:

代碼 9

  做好這些準備工作之后,我們就可以著手把示例數據關聯到用戶界面上了。打開AssignmentBookPage.xaml文件,創建一個資源字典,并在里面創建一個AssignmentListViewModel對象:

代碼 10

  好了之后就把第一個Pivot項的DataContext屬性設為上面創建的AssignmentListViewModel對象,并把LongListSelector控件的ItemsSource屬性綁到這個對象的AssignmentGroups屬性:

代碼 11

      此時,如果你切換到Expression Blend,它會提示你重新加載文件,因為剛才我們在Visual Studio里做了修改。加載完畢之后,你會看到LongListSelector控件里多了一些東西:

圖 4

  從上圖可以看出,示例數據已經綁上去了,但為什么顯示出來的是"Iridescent.Models.Assignment",而且每個都是一樣?這是因為LongListSelector控件并不知道如何顯示Assignment對象,所以直接調用它們的ToString方法獲取可以顯示的內容,而我們在創建Assignment類的時候并未重寫ToString方法,所以LongListSelector控件調用的是從Object類繼承下來的版本,這個版本返回的是對象的類型的完全限定名,也就是我們剛才看到的"Iridescent.Models.Assignment"。那么分組標題又哪去了?事實上,分組標題并未顯示出來,因為LongListSelector控件并不知道分組的哪個屬性表示分組標題。換句話說,LongListSelector控件壓根不知道如何使用我們提供的數據,而把使用方法告訴它正是我們的責任。

1
0
 
標簽:WP7
 
 

文章列表

arrow
arrow
    全站熱搜
    創作者介紹
    創作者 大師兄 的頭像
    大師兄

    IT工程師數位筆記本

    大師兄 發表在 痞客邦 留言(0) 人氣()