這篇文章將會探討如何在 Xamarin.Forms 中,設計原生 ListView 具有多選功能,如同下圖所示。在 Xamarin.Forms 中的 ListView 控制項,本身是沒有預設提供多選功能,僅提供單選並且可以觸發一個點選的事件,不過,若要讓 ListView 具有多選紀錄的功能,我們就需要另外設計一個 UI,當使用者選擇多筆紀錄之後,接著觸發這個 UI,就可以根據剛剛選擇完成的紀錄,進行相關的商業邏輯處理。
因此,在底下的程式碼中,使用者可以點選 ListView 的不同紀錄,一旦點選之後,該紀錄就會被選取到了,並且該紀錄的背景顏色會呈現綠色,代表該紀錄已經被選取了;當然,您也可以再度點選剛剛的紀錄,這筆紀錄就會取消選取,記錄背景顏色就會恢復成為灰色。最後,若多選紀錄操作完成之後,我們可以點選導航工具列的按鈕 結束 這個按鈕,這個時候在 ViewModel 內相對應的 DelegateCommand 委派方法,就會開始計算這次使用者總共點選了幾筆紀錄,並且顯示在螢幕的上方。
本篇文章的專案原始碼位於 XFMulSelLV
本篇文章的專案原始碼位於 XFMulSelLV
建立 ListView 的頁面 View 檢視
- 首先,請打開 MainPage.xaml 檔案,使用底下 XAML 宣告標記替換掉
- 在這個 ListView 控制項中,其實就是一般我們在設計 ListView 所用到的 XAML 語法相同,不過,我們在 ViewCell 中,您會看到有兩個 BoxView 控制項,第一個 BoxView 控制項的顏色是灰色,而第二個 BoxView 控制項的顏色是綠色,同時,我們也看到第二個 BoxView 的 XAML 標記宣告為
<BoxView Color="LightGreen" IsVisible="{Binding IsSelected}"/>
,這表示了,這個 BoxView 是否會顯示出來,將會取決於當時這筆紀錄在 ViewModel 中的 IsSelected 屬性值是否為 True。
<?xml version="1.0" encoding="utf-8" ?>
<ContentPage xmlns="http://xamarin.com/schemas/2014/forms"
xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
xmlns:Behaviors="clr-namespace:Prism.Behaviors;assembly=Prism.Forms"
xmlns:prism = "clr-namespace:Prism.Mvvm;assembly=Prism.Forms"
prism:ViewModelLocator.AutowireViewModel= "True"
x:Class="XFMulSelLV.Views.MainPage"
Title="集合資料多選">
<ContentPage.ToolbarItems>
<ToolbarItem Text="結果"
Command="{Binding ShowResultCommand}"/>
</ContentPage.ToolbarItems>
<Grid>
<Label Text="{Binding Title}"/>
<ListView
Margin="0,20,0,0"
ItemsSource="{Binding PeopleCollection}"
SelectedItem="{Binding PersonSelected}"
HasUnevenRows="True"
SeparatorVisibility="None"
>
<ListView.Behaviors>
<Behaviors:EventToCommandBehavior
EventName="ItemTapped"
Command="{Binding PersonTappedCommand}"/>
</ListView.Behaviors>
<ListView.ItemTemplate>
<DataTemplate>
<ViewCell>
<Grid>
<BoxView Color="LightGray"/>
<BoxView Color="LightGreen"
IsVisible="{Binding IsSelected}"/>
<StackLayout
Orientation="Vertical"
HorizontalOptions="Fill" VerticalOptions="Start"
>
<Label
Text="{Binding Name}"
FontSize="30"
/>
<Label
Text="{Binding Age}"
FontSize="20"
/>
</StackLayout>
</Grid>
</ViewCell>
</DataTemplate>
</ListView.ItemTemplate>
</ListView>
</Grid>
</ContentPage>
建立 ListView 的頁面 ViewModel 檢視模型
- 首先,請打開 MainPageViewModel.cs 檔案,使用底下 C# 程式碼替換掉
- 我們在這個檔案中,宣告了一個 ListView 的紀錄節點類別,Person,在這個類別中,有著姓名、年紀與是否選取這三個屬性。另外,為了要能夠讓您的選取動作,可以正常更新 IsSelected 屬性,進而使用當時的 IsSelected 值,更新頁面 UI 顯示,這個時候,請記得務必要將 Person 類別,要能夠實作 INotifyPropertyChanged 介面。
- 我們設計頁面 ViewModel 的導航事件 OnNavigatedTo,在這裡進行 ListView 要顯示的記錄之初始化動作,我們在這裡產生出 100 筆的紀錄。
- 我們有在頁面中,使用 ListView.Behaviors 來宣告一個 ListView 的事件,觸發發生後,要執行 ViewModel 中 PersonTappedCommand 命令,而我們在這個 PersonTappedCommand 命令委派方法中,執行這個敘述
PersonSelected.IsSelected = !PersonSelected.IsSelected;
,因此,若這筆紀錄尚未被選取,則 PersonSelected.IsSelected 就是 false,此時,我們將 PersonSelected.IsSelected 的值變更成為 true,因為 PersonSelected.IsSelected 值有變動了,就會觸發 INPC (Notification Property Changed) 的綁定事件,此時,頁面上就會更新 PersonSelected.IsSelected 為 true 的狀態下,將 ViewCell 內的第二個 BoxView 顯示出來。
public class Person : INotifyPropertyChanged
{
public event PropertyChangedEventHandler PropertyChanged;
public string Name { get; set; }
public int Age { get; set; }
public bool IsSelected { get; set; }
}
public class MainPageViewModel : INotifyPropertyChanged, INavigationAware
{
public event PropertyChangedEventHandler PropertyChanged;
public ObservableCollection<Person> PeopleCollection { get; set; } = new ObservableCollection<Person>();
public Person PersonSelected { get; set; }
public string Title { get; set; }
public DelegateCommand PersonTappedCommand { get; set; }
public DelegateCommand ShowResultCommand { get; set; }
private readonly INavigationService _navigationService;
public MainPageViewModel(INavigationService navigationService)
{
_navigationService = navigationService;
PersonTappedCommand = new DelegateCommand(() =>
{
PersonSelected.IsSelected = !PersonSelected.IsSelected;
});
ShowResultCommand = new DelegateCommand(() =>
{
var fooCount = 0;
foreach (var item in PeopleCollection)
{
if(item.IsSelected == true)
{
fooCount++;
}
}
Title = $"共選擇 {fooCount} 筆紀錄";
});
}
public void OnNavigatedFrom(NavigationParameters parameters)
{
}
public void OnNavigatingTo(NavigationParameters parameters)
{
}
public void OnNavigatedTo(NavigationParameters parameters)
{
PeopleCollection.Clear();
for (int i = 0; i < 100; i++)
{
PeopleCollection.Add(new Person
{
Name = $"Name{i}",
Age = i,
IsSelected = false
});
}
}
}