學習目標
- 知道一下 Collection Views 是什麼
- 三個 View 的差異
- Picker 嘿嘿嘿
本章節要探討的是 Collection type 對應的 Collection View。
在資訊界,各類型的集合是不可缺少的,所有高階語言都支援陣列 (Arrays) 和結構 (Structure),
這兩個集合互相補足 - 陣列通常是相同類型的資料集,結構則是相關但不同類型的資料集。
為了擴充這些基本集合,.NET 添加了幾個有用的類別:
- IEnumerable - 允許在資料集中尋覽
- ICollection - 繼承自 IEnumerable,並增加計數的功能
- IList - 繼承自 ICollection,支援索引 (Indexing)、 新增 (adding) 和移除 (removing)
回到本章節重點 -
Xamarin.Forms 定義了三種跟資料集合有關的 View:
- Picker - 可讓使用者 選擇一個子項目,集合項目一般不超過 12 個
- ListView - 通常有 很多很多 子項目,資料列的呈現通常為 同樣的格式
- TableView - 通常為各種類型 Cell 的集合,用於顯示數據或管理用戶輸入,像是 各類型的表單 或 應用程式設定
三個 Collection View
都內建捲軸 (Scrolling) 功能。
Picker vs. ListView
子項目都允許被點選,
但 Picker 的子項目僅 限於字串 (String),一般資料集較短
而 ListView 可顯示 任何方式 呈現的物件,通常資料集較長
ListView vs. TableView
ListView 和 TableView 很容易搞混,
因為都有使用 Cell 類別:
ListView 的 Cell 通常顯示 相同類型和相同格式 的物件,
TableView 則是 多種類型 Cell 的集合,且每個 Cell 都是獨立的功能。
若將這三個 View 與 C# 的 Data Type 相對應:
- Picker - String Array
- ListView - Object Array,通常是 List<T>,此集合中的各子項目通常會實作 INotifyPropertyChanged Interface (參閱 MVVM 章節)
- TableView - 可以是一個結構 (Structure),也可以是一個類別 (Class),也可能是一個實作 INotifyPropertyChanged 的 ViewModel
先從 Picker 來介紹 (這章節有點漫長QQ)~
Picker
Picker 適合用在項目少的集合,且集合的資料內容只能為字串 (String)底下直接來看範例:
本範例可以先看到兩個屬性:
- Title - 就是 Title...
- Items - IList<string>類型,通常使用XAML中的 <x:String> 初始化
本範例中 Picker 的資料集為各種鍵盤的類型,選擇後改變上方 Entry 的鍵盤類別。
*OnPlatform 寫法已更改,請參考此篇文章
<ContentPage xmlns="http://xamarin.com/schemas/2014/forms" xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml" x:Class="PickerDemo.PickerDemoPage"> <ContentPage.Padding> <OnPlatform x:TypeArguments="Thickness" iOS="0, 20, 0, 0" /> </ContentPage.Padding> <StackLayout Padding="20" Spacing="50"> <Entry x:Name="entry" Placeholder="Type something, type anything" /> <Picker Title="Keyboard Type" SelectedIndexChanged="OnPickerSelectedIndexChanged"> <Picker.Items> <x:String>Default</x:String> <x:String>Text</x:String> <x:String>Chat</x:String> <x:String>Url</x:String> <x:String>Email</x:String> <x:String>Telephone</x:String> <x:String>Numeric</x:String> </Picker.Items> <Picker.SelectedIndex> 6 </Picker.SelectedIndex> </Picker> </StackLayout> </ContentPage>
執行結果
了解 XAML 配置後,再來看點選時的事件與屬性
- SelectedIndexChanged & SelectedIndex
iOS 按 Done 按鍵 或是 Android 按 OK 按鍵,都會觸發 SelectedIndexChanged 事件。
SelectedIndex 是一個值從零開始的屬性,無選擇時預設值為 -1,SelectedIndex 固定放在 Picker.Items 後面 。
本範例的 SelectedIndexChanged:
會先取得 Picker 的 SelectedIndex,再利用 items[SelectedIndex] 取得對應的項目
之後用到一個神奇的方法,利用 型態集合 將 字串映射成屬性,再去設定 Entry.Keyboard。
public partial class PickerDemoPage : ContentPage { public PickerDemoPage() { InitializeComponent(); } void OnPickerSelectedIndexChanged(object sender, EventArgs args) { if (entry == null) return; Picker picker = (Picker)sender; int selectedIndex = picker.SelectedIndex; if (selectedIndex == -1) return; string selectedItem = picker.Items[selectedIndex]; //神奇的方法 PropertyInfo propertyInfo = typeof(Keyboard).GetRuntimeProperty(selectedItem); entry.Keyboard = (Keyboard)propertyInfo.GetValue(null); } }
執行結果:
Data Binding the Picker
Picker 的 Items 屬性
Xamairn 2.3.4 新增 ItemSource 屬性可使用,官網資料如下
https://developer.xamarin.com/guides/xamarin-forms/user-interface/picker/populating-itemssource/
我們發現,SelectedIndex 屬性是 TwoWay binding 的 BindableProperty,
但若要在 SelectedIndex 做 data binding,
其值只取得 Index 是不夠用的...即便可以利用 SeletedItem 取得集合內容,問題出在字串類型也不適合作為 data binding 的值…
最好的解決方案是建立一個 Converter 工具,此工具可將 Index 和某 集合 之間做轉換,
而此 集合 內容就是我們要 Binding 的值,同時此集合也必須 完全對應於 Picker 的項目。
ObjectToIndexConverter
在此,我們建立一個 ObjectToIndexConverter 的 泛型 工具轉換 Index 和我們要 data binding 的值,
我們會將要轉換的 集合 放在 XAML 以方便維護。
ObjectToIndexConverter 類別內定義了一個公開屬性和兩個方法:
- IList<T> items: 要被轉換的 集合。(為了擷取 XAML 上的值被定義為 ContentProperty )
- Convert:value 是泛型物件,並 回傳 物件在 集合 中的 index
- ConvertBack:value 是 集合 中的 index,並 回傳 該索引 項目
using System; using System.Collections.Generic; using System.Globalization; using Xamarin.Forms; namespace Xamarin.FormsBook.Toolkit { [ContentProperty("Items")] public class ObjectToIndexConverter: IValueConverter { public IList Items { set; get; } public ObjectToIndexConverter() { Items = new List (); } public object Convert(object value, Type targetType, object parameter, CultureInfo culture) { if (value == null || !(value is T) || Items == null) return -1; return Items.IndexOf((T)value); } public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture) { int index = (int)value; if (index < 0 || Items == null || index >= Items.Count) return null; return Items[index]; } } }
實際使用 Converter
範例功能是使用 Picker 來為 Label 選擇字體大小:
Picker 是 Target,Label 的屬性是 Source,由於是 TwoWay binding,所以當更改 Target 值時,Source 也會跟著更動。
*Oplatform 寫法已更改,請參考此篇文章
<ContentPage xmlns="http://xamarin.com/schemas/2014/forms" xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml" xmlns:toolkit= "clr-namespace:Xamarin.FormsBook.Toolkit;assembly=Xamarin.FormsBook.Toolkit" x:Class="PickerBinding.PickerBindingPage"> <ContentPage.Padding> <OnPlatform x:TypeArguments="Thickness" iOS="0, 20, 0, 0" /> </ContentPage.Padding> <StackLayout Padding="20" Spacing="50"> <Label x:Name="label" Text="Sample Text" FontSize="16" /> <Picker Title="Font Size"> <Picker.Items> <x:String>Font Size = 8</x:String> <x:String>Font Size = 10</x:String> <x:String>Font Size = 12</x:String> <x:String>Font Size = 14</x:String> <x:String>Font Size = 16</x:String> <x:String>Font Size = 20</x:String> <x:String>Font Size = 24</x:String> <x:String>Font Size = 30</x:String> </Picker.Items> <Picker.SelectedIndex> <!-- Source --> <Binding Source="{x:Reference label}" Path="FontSize"> <!-- 那個要轉換的 集合 --> <Binding.Converter> <toolkit:ObjectToIndexConverter x:TypeArguments="x:Double"> <x:Double>8</x:Double> <x:Double>10</x:Double> <x:Double>12</x:Double> <x:Double>14</x:Double> <x:Double>16</x:Double> <x:Double>20</x:Double> <x:Double>24</x:Double> <x:Double>30</x:Double> </toolkit:ObjectToIndexConverter> </Binding.Converter> </Binding> </Picker.SelectedIndex> </Picker> </StackLayout> </ContentPage>
執行結果:
沒有留言:
張貼留言
注意:只有此網誌的成員可以留言。