繼承原有控制項進行客製化,擴充原有控制項的功能與能力,是個相當重的議題,不過,這也屬於進階性的學習。
了解更多關於 [Xamarin.Android] 的使用方式
了解更多關於 [Xamarin.iOS] 的使用方式
了解更多關於 [Xamarin.Forms] 的使用方式
了解更多關於 [Hello, Android:快速入門] 的使用方式
了解更多關於 [Hello, iOS – 快速入門] 的使用方式
了解更多關於 [Xamarin.Forms 快速入門] 的使用方式
當然,想要擴充原有的控制項可以有其他的選擇,例如,使用附加可綁定屬性、行為等等,在這份筆記中,將會描述如何擴充 Picker 這個控制項,使其具有可綁定的資料來源屬性、選取後的可綁定資料屬性;另外,也可以設定當有選取新的資料項目之後,可以執行所綁定的 Command 命令,這個命令當然也是支援可綁定屬性能力。這樣的話,所擴充出來的 Picker 控制項,就可以直些在 XAML 內進行宣告,與將相關商業處理邏輯寫在 ViewModel 內,也就是說,您不再需要透過 Code Behind 來做這些事情。
這份筆記的專案原始碼如下
客製 Picker 控制項
首先,產生一個類別,使其繼承 Picker,其他的設定如底下程式碼,接下來說明這個新的類別做了甚麼事情。
- 在 BindablePicker 類別內,產生了三個可綁定屬性,分別為 ItemsSource / SelectedItem / SelectedItemCommand
- ItemsSource 可綁定屬性將會用來設定 Picker 要顯示的所有資料清單來源。
- SelectedItem 這個可綁定屬性將會當使用者選取的新的資料項目之後,在 ViewModel 內可以透過這個可綁定屬性取得使用者選項的內容值。
- SelectedItemCommand 可綁定屬性用於綁定當有新的項目被選取的時候,所要執行的 ICommand 命令,這個 ICommand 將會在 ViewModel 內實作出來。
- 在 BindablePicker 建構式中,定義了這個 Picker 控制項的 SelectedIndexChanged 事件,也就是當這個事件被驅動的時候,就會根據當時所選取的 SelectedIndex 索引值,找到實際的項目內容值,並且設定到 SelectedItem。
- 這個原始碼中,有許多關於可綁定屬性的定義,這部分可以參考可綁定屬性的做法相關說明。
- 當 SelectedItem 的值有所變動的時候,會判斷當時是否有綁定 ICommand 物件,若有,則會使用
SelectedItemCommand.Execute(value);
執行這個 ICommand 命令,其中,這個 ICommand 命令被呼叫的時候,會將當時選取的項目值,傳遞到 ICommand 內。
public class BindablePicker : Picker
{
public static readonly BindableProperty ItemsSourceProperty = BindableProperty.Create("ItemsSource",
typeof(IEnumerable), typeof(BindablePicker), null, propertyChanged: OnItemsSourceChanged);
public static readonly BindableProperty SelectedItemProperty = BindableProperty.Create("SelectedItem",
typeof(IEnumerable), typeof(BindablePicker), null, BindingMode.TwoWay, propertyChanged: OnSelectedItemChanged);
public static readonly BindableProperty SelectedItemCommandProperty = BindableProperty.Create("SelectedItemCommand",
typeof(ICommand), typeof(BindablePicker), null);
public BindablePicker()
{
SelectedIndexChanged += (o, e) =>
{
if (SelectedIndex < 0 || ItemsSource == null || !ItemsSource.GetEnumerator().MoveNext())
{
SelectedItem = null;
return;
}
var index = 0;
foreach (var item in ItemsSource)
{
if (index == SelectedIndex)
{
SelectedItem = item;
break;
}
index++;
}
};
}
public ICommand SelectedItemCommand
{
get { return (ICommand)GetValue(SelectedItemCommandProperty); }
set { SetValue(SelectedItemCommandProperty, value); }
}
public IEnumerable ItemsSource
{
get { return (IEnumerable)GetValue(ItemsSourceProperty); }
set { SetValue(ItemsSourceProperty, value); }
}
public Object SelectedItem
{
get { return GetValue(SelectedItemProperty); }
set
{
if (SelectedItem != value)
{
SetValue(SelectedItemProperty, value);
InternalUpdateSelectedIndex();
if(SelectedItemCommand!=null)
{
SelectedItemCommand.Execute(value);
}
}
}
}
public event EventHandler<SelectedItemChangedEventArgs> ItemSelected;
private void InternalUpdateSelectedIndex()
{
var selectedIndex = -1;
if (ItemsSource != null)
{
var index = 0;
foreach (var item in ItemsSource)
{
if (item != null && item.Equals(SelectedItem))
{
selectedIndex = index;
break;
}
index++;
}
}
SelectedIndex = selectedIndex;
}
private static void OnItemsSourceChanged(BindableObject bindable, object oldValue, object newValue)
{
var boundPicker = (BindablePicker)bindable;
if (Equals(newValue, null) && !Equals(oldValue, null))
return;
boundPicker.Items.Clear();
if (!Equals(newValue, null))
{
foreach (var item in (IEnumerable)newValue)
boundPicker.Items.Add(item.ToString());
}
boundPicker.InternalUpdateSelectedIndex();
}
private static void OnSelectedItemChanged(BindableObject bindable, object oldValue, object newValue)
{
var boundPicker = (BindablePicker)bindable;
if (boundPicker.ItemSelected != null)
{
boundPicker.ItemSelected(boundPicker, new SelectedItemChangedEventArgs(newValue));
}
boundPicker.InternalUpdateSelectedIndex();
}
}
在 XAML 內使用新產生的 BindablePicker
想要使用這個新的自訂控制項,您需要宣告一個 XAML 命名空間,在底下,宣告了一個
xmlns:local="clr-namespace:PickerLab"
命名空間。
因此,您可以使用 ` 的方式在 XAML 宣告這個新控制項;在底下的例子中,將透過資料繫結,將上述三個新建立的可綁定屬性,綁定到 ViewModel 內的三個屬性。
例如, Picker 可以選擇的所有清單項目將會透過
ItemsSource="{Binding PickerVM}"
來取得,而使用者選擇完後的資料項目,將會透過 SelectedItem="{Binding PickerSelectedTitle}"
綁定到 ViewModel 內;而要在使用點不同資料之後,執行的 ICommand ,則使用 SelectedItemCommand="{Binding SelectedIndexChangedCommand}"
方式來宣告。<?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:prism="clr-namespace:Prism.Mvvm;assembly=Prism.Forms"
xmlns:local="clr-namespace:PickerLab"
xmlns:behaviors="clr-namespace:Behaviors;assembly=Behaviors"
prism:ViewModelLocator.AutowireViewModel="True"
x:Class="PickerLab.Views.MainPage"
Title="MainPage">
<StackLayout HorizontalOptions="Fill" VerticalOptions="Center">
<Label Text="{Binding Title}" HorizontalOptions="Center"/>
<!--https://developer.xamarin.com/api/type/Xamarin.Forms.Picker/-->
<local:BindablePicker
ItemsSource="{Binding PickerVM}"
HorizontalOptions="Fill"
HeightRequest="34"
SelectedItem="{Binding PickerSelectedTitle}"
SelectedItemCommand="{Binding SelectedIndexChangedCommand}">
</local:BindablePicker>
<Label Text="{Binding PickerSelectedTitle}" HorizontalOptions="Center"/>
</StackLayout>
</ContentPage>
沒有留言:
張貼留言