課程內容
Ø編頁
ØList Picker
Ø展開List Box 控制項中的條目
本章的Book Reader應用程式為Jane Austen的經典小說《傲慢與偏見》提供一種專門為手機進行最佳化的閱讀體驗。書的字型來自Gutenberg項目(www.gutenberg.org),由於其著作權已經到期,因此它為美國人民提供了免費的電子書。如果你打算髮布一個包含Gutenberg項目的應用程式,確認你已經理解了Gutenberg項目的許可證。詳見www.gutenberg.org。
為了達到最好的閱讀體驗,本應用程式允許使用者自訂前景色彩、背景色、字型大小,甚至是字型集。Book Reader提供了簡便的頁面導航,允許使用者跳轉到任何章節或者任何頁碼。剛開始可能不那麼明顯,但是實現本應用程式的最大挑戰是編頁,即在字型設定的基礎上為整本書的內容分頁。當然,我們可以將整本書的內容放置在具有捲軸頁面中,但這並不能夠帶來好的使用者體驗。另外,由於UI元素的大小限制,使用其他的方法也未必可行。因此,本應用程式一次只顯示一個頁面。使用者可以通過點擊螢幕來翻頁,或者點擊應用程式欄上的按鈕來回退頁面。
The Main Page
圖25.1顯示了應用程式欄展開後的首頁面,應用程式欄上具有回退一頁按鈕、跳轉到任何章節或者頁碼的按鈕和改變設定的按鈕。應用程式欄地區同時也顯示了當前頁碼和總頁碼(這是基於當前字型設定情況的)。
圖 25.1 首頁面圖,預設使用類似Amazon Kindle的顏色模式,專門為閱讀提供足夠的對比。
注意:
➔ 本應用程式的分頁機制由PaginatedDocument使用者控制項來完成,在本章的最後詳述。
➔ 命名為Footer的list box控制項出現在應用程式欄中,因為它被放置在該地區的下方,而且應用程式欄的不透明度設定為0。
➔ 25.2所示的充滿章節的list box,使用了一種非常重要但很難發現的方法,使得list box中的條目按照其寬度進行展開,填滿該地區。這就可以在不給定寬度的條件下,使得每個條目中的元素(如頁碼)達到靠右對齊。
圖25.2 使用HorizontalContentAlignment進行展開的list box,所以在不給定條目寬度的條件下,使得頁碼達到靠右對齊。
➔ 書本是以文字檔的形式包含進來的,其Build Action設定為Content,就和前一章中的資料庫檔案一樣。檔案的名稱為1342.txt,與Project Gutenberg網站上下載來的文檔一致。
➔ 本應用程式使用了如下的設定:讀者當前的頁碼被儲存為字元索引,即為包含了整本書的內容的頁面的第一個字元建立索引。這是因為如果字型設定不同,那麼與書本位置相關的頁碼也會不同。有了這個方法以後,使用者在書本中的真正位置就被儲存下來了。
➔ 添加到章節列表中的索引值對是一種方便使用的類型,因為它包含了兩個獨立的字串屬性,資料範本可以將其綁定。其中,“Key”是靠左對齊的章區段標頭,“value”是靠右對齊的頁碼。
The Settings Page
Book Reader的設定頁面幾乎與Notepad應用中的設定頁面相同。其不同之處在於頂端的font picker,25.3所示。該font picker由Silverlight for Windows Phone Toolkit中的list picker控制項建立而來。
圖25.3 font picker在WYSIWYG picker中顯示的10種字型
這裡,List picker基本上就是一個combo box。它開始的時候看上去像text box,但是被點擊的時候,它允許使用者從列表中選擇一個值。資料範本同時綁定每個text block的FontFamily和Text屬性,在列表中顯示每個字串。List picker支援兩種不同的列表展示方式:內聯模式和全模式。25.3所示,內聯模式中,該控制項通過流暢的動畫來對記錄進行展開和合攏。25.4所示,在全模式中,該控制項顯示全屏的記錄列表。
圖25.4 配置為全模式的Book Reader中font picker
當我嘗試在Windows Phone 應用程式中使用ComboBox控制項時,為什麼顯得很奇怪?
ComboBox是一個核心的Silverlight控制項,它頻繁地使用在網頁中,但是,它沒有為Windows Phone提供合適的設計風格。所以它一般不會被使用(該控制項應該移除來避免困惑)。如果你想使用combo box的話,就用list picker來替代吧。
如果記錄的數量少於5個的話,List picker預設使用內聯模式;不然的話,它會使用全模式。這與Windows Phone設計準則是一致的。但是,我們可以通過設定ItemCountThreshold的值來強制其中的一個模式。只要記錄的數量少於等於ItemCountThreshold,List picker會保持內聯模式。Book Reader的font picker保持10種字型的內聯模式,所以其屬性值設定為10.
List picker定義了Header及其相關的HeaderTemplate屬性,定義了ItemTemplate屬性,用於自訂每個記錄內聯模式中的外觀顯示效果。即使我們使用全模式,對於list picker的外觀來說,這些屬性也是很重要的。對於全屏的列表,list picker同樣定義了獨立的FullModeHeader和FullModeItemTemplate屬性。25.4所示,全螢幕模式的list picker利用了這兩個屬性。如果我們不指定FullModeItemTemplate,全模式會使用ItemTemplate。
使用全模式時,List pickers不能包含UI元素!
如果我們直接把諸如text blocks或者toolkit中的ListPickerItem控制項這些UI元素放置於list picker中,那麼,在全螢幕模式顯示時,會拋出異常。那是因為該控制項嘗試將每個記錄加入到額外的全螢幕模式列表中,但是單個UI元素一次只能放置於一個地方。解決方案是在list picker中放置非可視化的資料記錄,然後使用模板來控制每個記錄的外觀。
避免將內聯模式的list picker放置於scroll viewer的底部!
List picker在這種情況下不適合使用。當第一次展開時,螢幕內容不會被移動,這是為了確保內容保留在螢幕上。然後,當嘗試著用捲軸來查看其他內容時,list picker會摺疊起來。為了達到最好的效能,內聯模式list picker下的元素應該將CacheMode設定為BitmapCache,那是因為list picker的下拉和收縮能夠改變這些元素的位置。
List Pickers and Picker Boxes
雖然單個list picker控制項提供了兩種不同的體驗,但是一些Windows Phone方面的文章將全模式的list picker稱為分離的picker box控制項,它保留了內聯模式體驗中的term list picker。這正是為什麼第19章“Animation Lab”中使用的自訂控制項稱為PickerBox的原因。
The PaginatedDocument User Control
為了決定分頁產生的位置,PaginatedDocument使用者控制項必須測量當前字型設定下,每個字元的寬度和高度。執行測量的唯一方式是將文本放置於text block中,並且檢查其ActualWidth和ActualHeight屬性的值。因此,PaginatedDocument將執行以下三個步驟的演算法:
1. 尋找文檔中每個不同的字元(《傲慢與偏見》只包含了85個不同的字元)。
2. 通過向text block中放置每個字元來測量其寬度和高度,一次放置一個。所有字元的高度都是一樣的(因為這裡的高度是線高度,包含了填充和其它),因此,高度只需要測量一次。
3. 從頭至尾瀏覽整篇文檔,使用預先測量好的字元寬度來計算每個換行的地點。有了這個資訊,以及之前測量的高度,我們就可以知道每個換頁的地點。由於收縮部分單詞的需求,決定分行符號需要一些小技巧。
基於計算得到的換頁地點和換行地點,該控制項為每一行文字增加一個text block來得到所有的頁面。
注意:
➔ 換行與換頁的索引分別儲存在各自的列表中。儲存換頁的列表是換行列表的子集,這種關係在一個頁面需要渲染時變得尤為清晰。
➔ 在UpdatePagination中,將儘可能多的工作交給後台線程來做。因為實際的測量工作必須在UI線程中完成,但是,兩個後台輔助線程用來將一個後台線程過渡為主線程,然後再將其轉回後台線程。
➔ 本控制項對於輸入的文本有一些假設,工程中包含的《傲慢與偏見》的文檔經過了預先處理,使得下面的假設成立:
1. 分行符號(\n)表示強制的換行,它只在一段文字的末尾出現(原文使用固定的行寬度,因此定期放置\n就可以,這就無法完成輸出的動態調整)。
2. 斷行符號符(\r)代表新一章的開始。有了它的協助,這就完成了章節集合總數的自動統計,使得可以將其顯示在首頁面的list box上。