精通 Windows Store App / Windows Phone App 的 ListView / ListBox 控制項 (2)
在前一篇中瞭解了 ListView / ListBox 控制項的基本用法,不過如果要實際開發 app 其實是不太夠用的,所以這篇再做深入一點 (但也不會太困難) 的介紹。
處理不單純的資料結構
前一篇裡我們舉的例子都是顯示簡單的字串資料,但很多時候都要處理更複雜的資料結構,比方說像 twitter 類型的 app 那樣顯示 tweet 列表,面對這樣的狀況,我們通常會寫一個物件類別來表示資料結構,像是這樣:(我過份簡化了)
我們用這個資料結構表示一則訊息的資料結構,包括 id、顯示名稱、訊息、大頭照 URL 以及發文時間。接著就是要設計一下 ItemTemplate 的呈現方式,至少要呈現大頭照、名字、訊息還有發文時間,所以我們修改了 XAML 檔案成像是下面這樣:
跟前一篇 ListView / ListBox 裡的 ItemTemplate 有很大的不同,這裡放入了一個 <Image />
以及三個 <TextBlock />
控制項用來顯示訊息資料,而最大的不同是,這些控制項的內容綁定資料的方式也有稍稍不同,這裡的 Binding 語法都加了參數,先簡單地想,它就是對應到資料結構的成員,所以 {Binding Message}
就是把資料中的 Message
成員內容作綁定,所以若填入的資料如下:
那顯示的畫面會是:
Windows Store App
顯示訊息資料後的 ListView (Windows Store App)
Windows Phone App
顯示訊息資料後的 ListBox (Windows Phone App)
只要簡單地使用 {Binding}
的語法就能輕鬆處理複雜的資料結構,要注意的是,被綁定的成員若不是字串型別,就會自動對該成員呼叫 .ToString()
取得字串內容,以這裡的示範為例,PostTime
雖然是一個 DateTime
的資料型別,但依然能做綁定,而且透過 ToString()
方法取得顯示的字串內容。
選取 ListView / ListBox 的項目
通常使用 ListView / ListBox 時,不僅會顯示資料,有時可能會搭配一些操作,最典型的例子就是選取其中一筆資料,可能是進入下一層畫面看更詳細的內容,或者是其它的操作。要完成這個動作很簡單,只要在 ListView / ListBox 的 XAML 中加入 SelectionChanged
的事件處理即可。
這樣只是完成在 XAML 中設定了 SelectionChanged
事件的處理函式名稱,所以在 XAML 的 code-behind 中就必須宣告一下這個函式:
在這個事件處理函式中,我們可以從 sender
取得 ListView 的實體,然後透過它來取得選取到的資料,由於已經做了 ItemsSource
的綁定,所以選取的資料型別就會是容器內資料的型別 (以這裡為例,選取到的資料就是 MessageModel
的資料型別),所以只要透過 ListView 實體的 SelectedIndex
或是 SelectedItem
就可以取得被選取的資料實體。
上面這段程式碼還多做了一件事,那就是把 SelectedIndex
設成 -1
用來移開選取的焦點,這個作法是為了:讓已經被選取的資料還有被選取的機會。為什麼要這樣做呢?假設今天要做的使用情境是:使用者點選了其中一筆訊息資料,此時換到另一頁顯示更詳細的內容,閱讀完畢可以按下 back 的按鈕回到列表頁,若沒有把選取的焦點移開,此時 ListView / ListBox 的選取焦點還在剛才選取的項目上,這樣點選同一個項目就不會觸發 SelectionChanged
的事件 (顧名思義),所以才多做了一個把 SelectedIndex
設成 -1 的動作,而若是做了這個步驟,就要注意它依然會觸發一次 SelectionChanged
事件,而 SelectedIndex
是 -1 時並無意義,於是用一個判斷式避開它。
主動通知改變
現在我們已經會使用 ListView / ListBox 顯示各種資料列表,也知道如何簡單地處理選取列表中單一項目的事件,看起來好像一切都很美好,但其實還有一個小問題:如果這個資料列表在綁定後,內容被修改後,其實不會要求 ListView / ListBox 去重新顯示被修改的資料,我們可以先做個小實驗,先在畫面中加入一個按鈕,按下去後就修改資料列表的內容:
照著上面的程式碼修改,畫面上多了一個按鈕,在程式啟動後,你可以試著按按看按鈕,原本預期第一筆資料的名稱欄位應該會改變,但其實並不會發生。這都是因為:
- 資料雖然改變了,但
List
資料結構不知道內容有改。 - 既然
List
不覺得有什麼改變,ListView 當然也不知道要做什麼事。
所以解決這個問題最簡單的方式,就是讓資料結構會根據內容改變而發出通知,並且選用會有所反應的容器。根據這個原則,首先修改 MessageModel.cs 這個檔案:
為了讓 MessageModel
的資料被修改後會發出通知,我們讓它實作 System.ComponentModel.INotifyPropertyChanged
介面,讓它發出通知的方式符合 .NET 的設計,並且在每一個成員 (Property) 被修改 (set) 時,產生一個 PropertyChanged
事件,這樣就完成了「發出通知」的任務。
但光是這樣還不夠,因為 List
容器並不會處理 PropertyChanged
事件,所以我們必須選用另一個容器 -- System.Collections.ObjectModel.ObservableCollection
,所以在 ListView / ListBox 的資料綁定程式碼便改寫成:
不要忘了將原本用 List<MessageModel>
容器的部份都改為使用 ObservableCollection<MessageModel>
,這時你就會發現按鈕的行為符合預期了。
待續
透過這篇文章的介紹,瞭解如何處理複雜的資料結構、選取資料的方式以及讓 ListView / ListBox 能夠感受到資料內容的改變,這樣的寫法雖然已經可以活用 ListView / ListBox 控制項了,不過都要在程式執行後才能看到資料顯示的樣子,沒有好好利用 Visual Studio 提供的 XAML designer 編輯畫面,甚至是開發工具內附的 Blend for Visual Studio 來設計像是 ItemTemplate 的樣式。下一篇文章我們將會介紹,這樣的架構設計如何讓程式開發與畫面設計可以整合地更好更方便。