介紹
ListView是用於顯示數據列表的視圖,特別是需要滾動的長列表。
CollectionView 是使用不同布局規范顯示數據列表的視圖(eg:可以水平排列),它旨在提供更靈活、更高的性能替代方法ListView,但是UWP下現在支持不是很好。
用例
在您要顯示可滾動數據列表的任何情況下,都可以使用ListView控件, ListView類支持上下文操作和數據綁定。
ListView控件不應與TableView控件混淆。每當您具有選項或數據的非綁定列表時,TableView控件都是一個更好的選擇,因為它允許在XAML中指定預定義的選項。例如,具有大多數預定義選項集的iOS設置應用程序比ListView更適合使用TableView。
ListView類不支持在XAML中定義列表項,您必須使用ItemsSource屬性或與ItemTemplate的數據綁定來定義列表中的項。
ListView最適合由單個數據類型組成的集合。此要求是因為列表中的每一行只能使用一種類型的單元格。 TableView控件可以支持多種單元格類型,因此當您需要顯示多種數據類型時,它是一個更好的選擇。
組件
ListView控件具有許多可用於行使每個平台的本機功能的組件。 這些組件在以下各節中定義。
1、Headers、footers
頁眉和頁腳組件顯示在列表的開頭和結尾,與列表數據分開,頁眉和頁腳可以綁定到與ListView的數據源不同的數據源。
2、Groups
可以對ListView中的數據進行分組,以簡化導航,組通常是數據綁定的,以下屏幕截圖顯示了具有分組數據的ListView:

3、Cells
ListView中的數據項稱為單元格,每個單元對應於一行數據,有內置單元供您選擇,或者您可以定義自己的自定義單元,內置單元格和自定義單元格都可以在XAML或代碼中使用/定義。
- 內置單元格(例如TextCell和ImageCell)對應於本機控件,並且性能特別好。
- TextCell顯示文本字符串,還可以顯示詳細信息文本。詳細信息文本以較小的字體顯示為第二行,帶有強調色。
- ImageCell顯示帶有文本的圖像。 顯示為帶有左側圖像的TextCell。
- 自定義單元格用於呈現復雜數據。 例如,自定義單元格可用於呈現包括專輯和歌手在內的歌曲列表。
以下屏幕快照顯示了具有ImageCell項的ListView:

功能性
ListView類支持多種交互樣式。
- Pull-to-refresh允許用戶下拉ListView刷新內容。
- Context actions上下文操作允許開發人員在單個列表項上指定自定義操作。 例如,您可以在iOS上實施滑動操作,或者在Android上實施長按操作。
- Selection 選擇允許開發人員將功能附加到列表項上的選擇和取消選擇事件。
以下屏幕截圖顯示了具有上下文操作的ListView:

數據源
ItemsSource
ListItem使用ItemsSource屬性填充數據,該屬性可以接受實現IEnumerable的任何集合,填充ListView的最簡單方法是使用字符串數組:
<ListView> <ListView.ItemsSource> <x:Array Type="{x:Type x:String}"> <x:String>mono</x:String> <x:String>monodroid</x:String> <x:String>monotouch</x:String> <x:String>monorail</x:String> <x:String>monodevelop</x:String> <x:String>monotone</x:String> <x:String>monopoly</x:String> <x:String>monomodal</x:String> <x:String>mononucleosis</x:String> </x:Array> </ListView.ItemsSource> </ListView>
此方法將使用字符串列表填充ListView。 默認情況下,ListView將調用ToString並將結果顯示在每一行的TextCell中。
由於ItemsSource已發送到數組,因此內容不會隨着基礎列表或數組的更改而更新。 如果希望ListView在基礎列表中添加,刪除和更改項時自動更新,則需要使用ObservableCollection。 ObservableCollection是在System.Collections.ObjectModel中定義的,與List一樣,除了 它可以將任何更改通知ListView之外:
ObservableCollection<Employee> employees = new ObservableCollection<Employee>(); listView.ItemsSource = employees; //Mr. Mono will be added to the ListView because it uses an ObservableCollection employees.Add(new Employee(){ DisplayName="Mr. Mono"});
Data Binding
數據綁定是將用戶界面對象的屬性綁定到某些CLR對象的屬性(例如,視圖模型中的類)的“膠水”。 數據綁定非常有用,因為它通過替換許多無聊的樣板代碼來簡化用戶界面的開發。
數據綁定通過使對象的綁定值更改而保持同步來進行。 您不必在控件的值每次更改時都編寫事件處理程序,而只需建立綁定並在視圖模型中啟用綁定即可。
有關數據綁定的更多信息,請參見“數據綁定基礎知識”。
Binding Cells
單元格(和單元格的子級)的屬性可以綁定到ItemsSource中對象的屬性。 例如,可以使用ListView呈現員工列表。
創建一個ObservableCollection<Employee>,將其設置為ListView的 ItemsSource,並使用數據填充列表:
public class Employee { public string DisplayName {get; set;} } ObservableCollection<Employee> employees = new ObservableCollection<Employee>(); public ObservableCollection<Employee> Employees { get { return employees; }} public EmployeeListPage() { EmployeeView.ItemsSource = employees; // ObservableCollection allows items to be added after ItemsSource // is set and the UI will react to changes employees.Add(new Employee{ DisplayName="Rob Finnerty"}); employees.Add(new Employee{ DisplayName="Bill Wrestler"}); employees.Add(new Employee{ DisplayName="Dr. Geri-Beth Hooper"}); employees.Add(new Employee{ DisplayName="Dr. Keith Joyce-Purdy"}); employees.Add(new Employee{ DisplayName="Sheri Spruce"}); employees.Add(new Employee{ DisplayName="Burt Indybrick"}); }
注:ObservableCollection不是線程安全的。 修改ObservableCollection會導致UI更新在執行修改的同一線程上進行。 如果該線程不是主UI線程,則將導致異常。
以下代碼段演示了綁定到員工列表的ListView:
<?xml version="1.0" encoding="utf-8" ?> <ContentPage xmlns="http://xamarin.com/schemas/2014/forms" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" xmlns:constants="clr-namespace:XamarinFormsSample;assembly=XamarinFormsXamlSample" x:Class="XamarinFormsXamlSample.Views.EmployeeListPage" Title="Employee List"> <ListView x:Name="EmployeeView" ItemsSource="{Binding Employees}"> <ListView.ItemTemplate> <DataTemplate> <TextCell Text="{Binding DisplayName}" /> </DataTemplate> </ListView.ItemTemplate> </ListView> </ContentPage>
Binding SelectedItem
通常,您將需要綁定到ListView的當前選定項,而不是使用事件處理程序來響應更改,為此,請在XAML中綁定SelectedItem屬性:
<ListView x:Name="listView" SelectedItem="{Binding Source={x:Reference SomeLabel}, Path=Text}"> … </ListView>
假設listView的ItemsSource是字符串列表,SomeLabel將其Text屬性綁定到SelectedItem。
自定義ListView單元格外觀
Xamarin.Forms ListView類用於呈現可滾動列表,可通過使用ViewCell元素對其進行自定義,ViewCell元素可以顯示文本和圖像,指示是/否狀態,並接收用戶輸入。
內置的單元格
Xamarin.Forms帶有可用於許多應用程序的內置單元:
- TextCell控件用於顯示文本,並帶有可選的第二行以顯示詳細文本。
- ImageCell控件與TextCells相似,但在文本左側包含一個圖像。
- SwitchCell控件用於呈現和捕獲打開/關閉或真/假狀態。
- EntryCell控件用於呈現用戶可以編輯的文本數據。
SwitchCell和EntryCell控件在TableView上下文中更常用。
1、TextCell

TextCell在運行時rendered(呈現)為本機控件,因此與自定義ViewCell相比,性能非常好。 TextCell可以自定義以下屬性:
- Text–第一行以大字體顯示的文本。
- Detail–第一行下方顯示的文本,使用較小的字體。
- TextColor –文本的顏色。
- DetailColor –詳細文本的顏色
2、ImageCell
像TextCell一樣,ImageCell可以用於顯示文本和輔助詳細信息文本,並且通過使用每個平台的本機控件,它可以提供出色的性能。 ImageCell與TextCell的不同之處在於,它在文本的左側顯示一個圖像。

當您需要顯示具有可視外觀的數據列表(例如聯系人或電影列表)時,ImageCell很有用。 ImageCell是可自定義的,除了上面四個屬性外,還有
ImageSource – the image to display next to the text
自定義單元格
自定義單元格使您可以創建內置單元格不支持的單元格布局。 例如,您可能要顯示一個帶有兩個權重相等的標簽的單元格。 TextCell不足,因為TextCell的標簽較小。 大多數單元格自定義添加其他只讀數據(例如其他標簽,圖像或其他顯示信息)。
所有自定義單元格都必須派生自ViewCell,后者是所有內置單元格類型使用的相同基類。
Xamarin.Forms在ListView控件上提供了一種緩存行為,該行為可以提高某些類型的自定義單元格的滾動性能。
<?xml version="1.0" encoding="UTF-8"?> <ContentPage xmlns="http://xamarin.com/schemas/2014/forms" xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml" x:Class="demoListView.ImageCellPage"> <ContentPage.Content> <ListView x:Name="listView"> <ListView.ItemTemplate> <DataTemplate> <ViewCell> <StackLayout BackgroundColor="#eee" Orientation="Vertical"> <StackLayout Orientation="Horizontal"> <Image Source="{Binding image}" /> <Label Text="{Binding title}" TextColor="#f35e20" /> <Label Text="{Binding subtitle}" HorizontalOptions="EndAndExpand" TextColor="#503026" /> </StackLayout> </StackLayout> </ViewCell> </DataTemplate> </ListView.ItemTemplate> </ListView> </ContentPage.Content> </ContentPage>
XAML的工作方式如下:
- 自定義單元格嵌套在DataTemplate內部,該數據模板位於ListView.ItemTemplate內部,此過程與使用任何內置單元相同。
- ViewCell是自定義單元格的類型, DataTemplate元素的子級必須是ViewCell類或從ViewCell類派生。
- 在ViewCell內部,布局可以由任何Xamarin.Forms布局管理,在此示例中,布局由StackLayout管理,該布局允許自定義背景色。
ListView中的 <DataTemplate>的單元項有3種寫法
1、寫在同一個頁面:直接在當前ListView中寫<ViewCell>
2、分離,新建一個CellTemplate頁面,繼承自ViewCell,但是里面沒有寫資源Resources,資源需要寫成全局的
3、分離,新建一個CellTemplate頁面,繼承自ContentView,里面可以寫資源Resources。
列表外觀
除了列表中每一行的ViewCell實例之外,Xamarin.Forms ListView還允許您自定義列表的表示形式。
分組
當在連續滾動列表中顯示大量數據時,可能會變得笨拙。在這種情況下,啟用分組可以通過更好地組織內容並激活特定平台的控件來改善用戶體驗,這些控件使導航數據更加容易。
為ListView激活分組后,將為每個組添加標題行。
要啟用分組:
- 創建列表列表(組列表,每個組是元素列表)。
- 將ListView的ItemsSource設置到該列表。
- 將IsGroupingEnabled設置為true。
- 設置GroupDisplayBinding以綁定到用作組標題的組屬性。
- [可選]將GroupShortNameBinding設置為綁定到用作組的簡稱的組的屬性。簡稱用於跳轉列表(iOS的右側列)。
ListView可能會顯示可與列表元素一起滾動的頁眉和頁腳。 頁眉和頁腳可以是文本字符串,也可以是更復雜的布局。 此行為與節組是分開的。
可以將“頁眉”和/或“頁腳”設置為字符串值,也可以將它們設置為更復雜的布局。 還有HeaderTemplate和FooterTemplate屬性,可讓您為支持數據綁定的頁眉和頁腳創建更復雜的布局。
<ListView.Header> <StackLayout Orientation="Horizontal"> <Label Text="Header" TextColor="Olive" BackgroundColor="Red" /> </StackLayout> </ListView.Header> <ListView.Footer> <StackLayout Orientation="Horizontal"> <Label Text="Footer" TextColor="Gray" BackgroundColor="Blue" /> </StackLayout> </ListView.Footer>
滾動條可見性
ListView類具有HorizontalScrollBarVisibility和VerticalScrollBarVisibility屬性,它們獲取或設置一個ScrollBarVisibility值,該值表示水平或垂直滾動條何時可見,可以將這兩個屬性設置為以下值:
- Default:指示平台的默認滾動條行為,並且是HorizontalScrollBarVisibility和VerticalScrollBarVisibility屬性的默認值。
- Always:指示滾動條將可見,即使內容適合視圖。
- Never:表示即使內容不適合視圖,也不顯示滾動條。
行分隔符
默認情況下,iOS和Android上ListView元素之間顯示分隔線。如果您想在iOS和Android上隱藏分隔線,請在ListView上設置SeparatorVisibility屬性,SeparatorVisibility的選項為:
- Default-在iOS和Android上顯示分隔線。
- None-在所有平台上隱藏分隔符。
還可以通過SeparatorColor屬性設置分隔線的顏色。
行高
默認情況下,ListView中的所有行都具有相同的高度,ListView具有兩個可用於更改該行為的屬性:
- HasUnevenRows(不均勻) –真/假值,如果設置為true,則行的高度會有所不同。 默認為false。
- RowHeight –設置HasUnevenRows為false時每行的高度。
在運行時調整行大小
只要HasUnevenRows屬性設置為true,就可以在運行時以編程方式調整單個ListView行的大小,Cell.ForceUpdateSize方法更新單元格的大小,即使當前不可見它也是如此,如以下代碼所示
void OnImageTapped (object sender, EventArgs args) { var image = sender as Image; var viewCell = image.Parent.Parent as ViewCell; if (image.HeightRequest < 250) { image.HeightRequest = image.Height + 100; viewCell.ForceUpdateSize (); } }
OnImageTapped事件處理程序是響應於輕按單元格中的一個Image而執行的,並增加了該單元格中顯示的Image的大小,以便於查看。
注:運行時行大小調整的過度使用會導致性能下降。
交互性
選擇和點擊
通過將ListView.SelectionMode屬性設置為ListViewSelectionMode枚舉的值,可以控制ListView選擇模式:
- Single 表示可以選中一個項目,並突出顯示所選項目,這是默認值。
- None 表示 項無法被選中。
當用戶點擊一個項目時,將觸發兩個事件:
- 選擇新項目時將觸發ItemSelected。
- 點擊項目時觸發ItemTapped。
輕按兩次相同的項目將觸發兩個ItemTapped事件,但只會觸發一個ItemSelected事件。
注:ItemTappedEventArgs類包含ItemTapped事件的事件參數,具有Group和Item屬性,以及一個ItemIndex屬性,該屬性的值表示所竊聽項目的ListView中的索引。 類似地,包含ItemSelected事件的事件參數的SelectedItemChangedEventArgs類具有SelectedItem屬性和SelectedItemIndex屬性,該屬性的值表示所選項目的ListView中的索引。
當SelectionMode屬性設置為Single時,可以選擇ListView中的項目,將觸發ItemSelected和ItemTapped事件,並且SelectedItem屬性將設置為所選項目的值。
當SelectionMode屬性設置為None時,將無法選擇ListView中的項目,將不會觸發ItemSelected事件,並且SelectedItem屬性將保持為null。 但是,在點擊過程中,仍會觸發ItemTapped事件,並且被輕按的項目將短暫突出顯示。
當選擇一個項目並將SelectionMode屬性從Single更改為None時,SelectedItem屬性將設置為null,並且將使用空項觸發ItemSelected事件。
上下文操作
通常,用戶會希望對ListView中的項目執行操作。 在iOS上,您可以滑動以刪除消息。上下文動作可以在C#和XAML中實現。
上下文操作是使用MenuItem元素創建的,MenuItems對象的點擊事件是MenuItems引發的而不是ListView,這與處理單元格的輕拍事件的方式不同,在列表事件中,ListView引發事件而不是單元格,由於ListView引發了事件,因此會為其事件處理程序提供關鍵信息,例如選擇或點擊了哪個項目。
默認情況下,MenuItem無法知道它屬於哪個單元格,MenuItem上的CommandParameter屬性可用於存儲對象,例如MenuItem的ViewCell后面的對象。 可以在XAML和C#中設置CommandParameter屬性。
Xaml:
可以在XAML集合中創建MenuItem元素。 下面的XAML演示了一個自定義單元,其中實現了兩個上下文操作:
<ListView x:Name="ContextDemoList"> <ListView.ItemTemplate> <DataTemplate> <ViewCell> <ViewCell.ContextActions> <MenuItem Clicked="OnMore" CommandParameter="{Binding .}" Text="More" /> <MenuItem Clicked="OnDelete" CommandParameter="{Binding .}" Text="Delete" IsDestructive="True" /> </ViewCell.ContextActions> <StackLayout Padding="15,0"> <Label Text="{Binding title}" /> </StackLayout> </ViewCell> </DataTemplate> </ListView.ItemTemplate> </ListView>
注:Android的NavigationPageRenderer具有可重寫的UpdateMenuItemIcon方法,該方法可用於從自定義Drawable加載圖標。 通過此覆蓋,可以將SVG圖像用作Android上MenuItem實例上的圖標。
可以將多個上下文操作添加到一個單元格,但是只有一個應將IsDestructive設置為true(為true時,項目在iOS上的呈現方式有所不同)
Pull to刷新
要啟用“拉動刷新”功能,請將IsPullToRefreshEnabled設置為true:
刷新期間會出現一個微調框,默認情況下為黑色。 但是,可以通過將RefreshControlColor屬性設置為Color來在iOS和Android上更改微調器顏色:
ListView觸發Refreshing事件以啟動刷新,並且IsRefreshing屬性將設置為true【自動的】,刷新ListView內容所需的任何代碼均應由Refreshing事件的事件處理程序執行,或由RefreshCommand執行的方法執行。 刷新ListView之后,應將IsRefreshing屬性設置為false,或應調用EndRefresh方法,以指示刷新已完成。
注:定義RefreshCommand時,可以指定命令的CanExecute方法來啟用或禁用命令。
eg:
<ListView RefreshCommand="{Binding ComListRefreshCommand}" IsRefreshing="{Binding ComListIsRefreshing}">
在刷新命令中,執行一些操作后,將IsRefreshing設置為false,
private void ComListRefreshCommandAsync() { //.... ComListIsRefreshing = false; //刷新完成 }
檢測滾動
ListView定義了一個Scrolled事件,該事件被觸發以指示發生滾動。 以下XAML示例顯示了一個ListView,它為Scrolled事件設置了一個事件處理程序:
<ListView Scrolled="OnListViewScrolled"> ... </ListView>
void OnListViewScrolled(object sender, ScrolledEventArgs e) { Debug.WriteLine("ScrollX: " + e.ScrollX); Debug.WriteLine("ScrollY: " + e.ScrollY); }
性能
在編寫移動應用程序時,性能至關重要。 用戶已經期望平滑的滾動和快速的加載時間。 無法滿足用戶的期望將使您在應用程序商店中的評分成本下降,或者對於業務線應用程序,這將花費組織時間和金錢。
Xamarin.Forms ListView是用於顯示數據的強大視圖,但是它有一些限制。 使用自定義單元格時,滾動性能可能會受到影響,尤其是當它們包含深度嵌套的視圖層次結構或使用需要復雜測量的某些布局時。 幸運的是,可以使用一些技術來避免性能下降。
緩存策略
ListView通常用於顯示比屏幕顯示更多的數據。 例如,音樂應用程序可能具有包含數千個條目的歌曲庫,為每個條目創建一個項目將浪費寶貴的內存並且性能不佳,不斷創建和銷毀行將要求應用程序不斷實例化和清理對象,這也會導致性能下降。
為了節省內存,每個平台的本機ListView等效項都具有用於重復使用行的內置功能。 僅將屏幕上可見的單元格加載到內存中,並將內容加載到現有單元格中,此模式可防止應用程序實例化數千個對象,從而節省時間和內存。
Xamarin.Forms允許通過ListViewCachingStrategy枚舉重用ListView單元,該枚舉具有以下值:
- RetainElement, // the default value
- RecycleElement,
- RecycleElementAndDataTemplate
注:通用Windows平台(UWP)忽略了RetainElement緩存策略,因為它始終使用緩存來提高性能。 因此,默認情況下,它的行為就像應用了RecycleElement緩存策略一樣。
RetainElement保留元素
RetainElement緩存策略指定ListView將為列表中的每個項目生成一個單元格,並且是默認的ListView行為。在以下情況下應使用它:
- 每個細胞具有大量的結合(20-30 +)。
- 單元模板經常更改。
- 測試表明,RecycleElement緩存策略導致執行速度降低。
在使用自定義單元格時,認識到RetainElement緩存策略的后果很重要。每次創建單元都需要運行任何單元初始化代碼,這可能是每秒多次。在這種情況下,在頁面上很好的布局技術(例如使用多個嵌套的StackLayout實例)會在用戶實時滾動設置並實時銷毀它們時成為性能瓶頸。
RecycleElement回收元素
RecycleElement緩存策略指定ListView將嘗試通過回收列表單元來最小化其內存占用量和執行速度。此模式並不總是可以提供性能上的改進,應該執行測試以確定是否有任何改進。但是,這是首選,應在以下情況下使用:
- 每個單元具有少量至中等數量的結合。
- 每個單元的BindingContext定義所有單元數據。
- 每個單元在很大程度上相似,但單元模板不變。
在虛擬化期間,單元將更新其綁定上下文,因此,如果應用程序使用此模式,則必須確保正確處理了綁定上下文更新。有關單元的所有數據必須來自綁定上下文,否則可能會發生一致性錯誤。通過使用數據綁定顯示單元格數據可以避免此問題。或者,應在OnBindingContextChanged重寫中設置單元格數據,而不是在自定義單元格的構造函數中進行設置,如以下代碼示例所示:
在子類ListView中設置緩存策略
在ListView的子類上從XAML設置CachingStrategy屬性將不會產生所需的行為,因為ListView上沒有CachingStrategy屬性。 此外,如果啟用了XAMLC,則將產生以下錯誤消息:找不到“ CachingStrategy”的屬性,可綁定屬性或事件
解決此問題的方法是在子類ListView上指定一個構造函數,該構造函數接受ListViewCachingStrategy參數並將其傳遞給基類:
public class CustomListView : ListView { public CustomListView (ListViewCachingStrategy strategy) : base (strategy) { } ... }
然后可以使用x:Arguments語法從XAML指定ListViewCachingStrategy枚舉值:
<local:CustomListView> <x:Arguments> <ListViewCachingStrategy>RecycleElement</ListViewCachingStrategy> </x:Arguments> </local:CustomListView>
ListView性能建議
有許多技術可以改善ListView的性能,以下建議可能會改善ListView的性能
- 將ItemsSource屬性綁定到IList <T>集合而不是IEnumerable <T>集合,因為IEnumerable <T>集合不支持隨機訪問。
- 盡可能使用內置單元(例如TextCell / SwitchCell)代替ViewCell。
- 使用更少的元素。例如,考慮使用單個FormattedString標簽而不是多個標簽。
- 顯示非均勻數據(即不同類型的數據)時,用TableView替換ListView。
- 限制使用Cell.ForceUpdateSize方法。如果使用過度,則會降低性能。
- 在Android上,請避免在實例化ListView后設置其行分隔符的可見性或顏色,因為這會導致較大的性能損失。
- 避免基於BindingContext更改單元格布局,更改布局會導致大量的測量和初始化成本。
- 避免深層嵌套的布局層次結構,使用AbsoluteLayout或Grid可以幫助減少嵌套。
- 避免使用除Fill之外的特定LayoutOptions(Fill是最便宜的計算方法)。
- 出於以下原因,避免將ListView放在ScrollView中:
ListView實現自己的滾動。
ListView將不會接收任何手勢,因為它們將由父級ScrollView處理。
ListView可以顯示一個自定義的頁眉和頁腳,可以與列表的元素一起滾動,從而可能提供ScrollView所使用的功能。有關更多信息,請參見頁眉和頁腳。
- 如果需要在單元格中顯示特定的復雜設計,請考慮使用自定義渲染器。
AbsoluteLayout有潛力執行布局而無需調用單個度量,從而使其具有高性能。如果不能使用AbsoluteLayout,請考慮RelativeLayout。如果使用RelativeLayout,則直接傳遞約束將比使用表達式API快得多。此方法更快,因為表達式API使用JIT,並且在iOS上必須解釋樹,這比較慢。表達式API適用於僅在初始布局和旋轉時才需要的頁面布局,但在ListView中(在滾動過程中不斷運行)在列表視圖中會降低性能。
為ListView或其單元格構建自定義渲染器是一種減少布局計算對滾動性能影響的方法。有關更多信息,請參見自定義ListView和自定義ViewCell。
