學習目標
- BindingMode 三種綁定方向
- StringFormat 資料顯示的格式
- BindingConverter 客製化資料轉換工具
這篇篇幅會較長一點,主要是延續上一小節 DataBinding,需要學習的周邊知識。
The Binding Mode正常來說若有一個 Label 的字體大小要隨著 Slider 更改,綁定會像這樣寫:
<Label FontSize="{Binding Source={x:Reference slider}, Path=Value}" /> <Slider x:Name="slider" Maximum="100" />
但我們將 Target 和 Source 反過來綁定看看:
<Label x:Name="label" /> <Slider Maximum="100" Value="{Binding Source={x:Reference label}, Path=FontSize}" />看起來好像沒啥意義的寫法,但這段是可以成功執行的,Slider 可以更改 Label 字的大小。
為什麼???
這就是我們接著要談的 Binding Mode -
做 Data Binding 時可以選擇綁定模式,更改 Target 和 Source 影響的方向。
Binding Mode 共有四種:
- Default
- OneWay - 單向綁定 (Source 影響 Target)
- OneWayToSource - 反向綁定 (Target 影響 Source)
- TwoWay - 雙向綁定 (Target Source 互相影響)
大部分的 Bindable Mode 預設都是 OneWay,
但底下這些是例外,預設為 TwoWay:
Class
|
Property that is
TwoWay
|
Slider
|
Value
|
Stepper
|
Value
|
Switch
|
IsToggled
|
Entry
|
Text
|
Editor
|
Text
|
SearchBar
|
Text
|
DatePicker
|
Date
|
TimePicker
|
Time
|
回到本小節一開始的例子,就是因為 Slider 預設為 TwoWay,
所以即使我們反過來將 Lable 設為 Source,還是能執行更改字體大小的功能。
來看 Binding Mode 的寫法:
<ContentPage xmlns="http://xamarin.com/schemas/2014/forms" xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml" x:Class="BindingModes.BindingModesPage" Padding="10, 0"> <ContentPage.Resources> <ResourceDictionary> <Style TargetType="StackLayout"> <Setter Property="VerticalOptions" Value="CenterAndExpand" /> </Style> <Style TargetType="Label"> <Setter Property="HorizontalOptions" Value="Center" /> </Style> </ResourceDictionary> </ContentPage.Resources> <StackLayout VerticalOptions="Fill"> <StackLayout> <!--Default--> <Label Text="Default" FontSize="{Binding Source={x:Reference slider1}, Path=Value}" /> <Slider x:Name="slider1" Maximum="50" /> </StackLayout> <StackLayout> <!--OneWay--> <Label Text="OneWay" FontSize="{Binding Source={x:Reference slider2}, Path=Value, Mode=OneWay}" /> <Slider x:Name="slider2" Maximum="50" /> </StackLayout> <StackLayout> <!--OneWayToSource--> <Label Text="OneWayToSource" FontSize="{Binding Source={x:Reference slider3}, Path=Value, Mode=OneWayToSource}" /> <Slider x:Name="slider3" Maximum="50" /> </StackLayout> <StackLayout> <!--TwoWay--> <Label Text="TwoWay" FontSize="{Binding Source={x:Reference slider4}, Path=Value, Mode=TwoWay}" /> <Slider x:Name="slider4" Maximum="50" /> </StackLayout> </StackLayout> </ContentPage>
當然第三個 OneWayToSource 字大小是不會動的:
要動起來可以寫成:
<Label x:Name="label3" Text="OneWayToSource" /> <Slider Maximum="50" Value="{Binding Source={x:Reference label3}, Path=FontSize, Mode=OneWayToSource}" />OneWayToSource,當更改 Target (Slider) 時,會去更動 Source (Label)。
String Formatting
在 Data binding 時,資料格式除了在 C# 處理外,XAML 也提供了簡易的寫法:
舉例來說,假設資料格式是 System.DateTime,但前端需要顯示 簡短日期 (yyyy/MM/dd):
<Label Text="{Binding Source={x:Reference datepicker}, Path=Date, StringFormat='Today is {0:d}'}" /> <DatePicker x:Name="datepicker" />
為什麼是 "Path"?
Data Binding 學到這裡可能有個疑惑,會什麼值的來源要叫做 Path 而不是 Property?
原因是,來源值並非只能為 Property,直接看底下範例就能理解,Path 的寫法可以相當多樣化。
但,這麼寫會增加維護上的難度… 所以 Path 還是 盡量保持為來源的 Property 就好。
*Oplatform 寫法已更改,請參考此篇文章
<ContentPage xmlns="http://xamarin.com/schemas/2014/forms" xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml" xmlns:globe="clr-namespace:System.Globalization;assembly=mscorlib" x:Class="BindingPathDemos.BindingPathDemosPage" x:Name="page"> <!--Padding--> <ContentPage.Padding> <OnPlatform x:TypeArguments="Thickness" iOS="10, 20, 10, 0" Android="10, 0" WinPhone="10, 0" /> </ContentPage.Padding> <!--Resource--> <ContentPage.Resources> <ResourceDictionary> <Style x:Key="baseStyle" TargetType="View"> <Setter Property="VerticalOptions" Value="CenterAndExpand" /> </Style> <Style TargetType="Label" BasedOn="{StaticResource baseStyle}"> <Setter Property="FontSize" Value="Large" /> <Setter Property="HorizontalTextAlignment" Value="Center" /> </Style> <Style TargetType="Slider" BasedOn="{StaticResource baseStyle}" /> </ResourceDictionary> </ContentPage.Resources> <StackLayout BindingContext="{x:Reference page}"> <!--取得 page 的 Top padding 值--> <Label Text="{Binding Path=Padding.Top, StringFormat='The top padding is {0}'}" /> <!--利用 Tree Trace 取值--> <Label Text="{Binding Path=Content.Children[4].Value, StringFormat='The Slider value is {0:F2}'}" /> <Label Text="{Binding Source={x:Static globe:CultureInfo.CurrentCulture}, Path=DateTimeFormat.DayNames[3], StringFormat='The middle day of the week is {0}'}" /> <Label Text="{Binding Path=Content.Children[2].Text.Length, StringFormat='The preceding Label has {0} characters'}" /> <Slider /> </StackLayout> </ContentPage>
若是來源到目標的轉型較為複雜,或是夾雜著自己寫的運算式,
我們就能利用 IValueConverter 來客製化轉型小工具。
The ConvertBack method is called only for TwoWay or OneWayToSource bindings。
底下範例為,當 Entry 有輸入值時,Button 才能按下 (有點類似必填功能):
要準備的 Converter 就是 int (Entry.Text.Length) => bool (Button.IsEnabled):
using System; using System.Globalization; using Xamarin.Forms; namespace Xamarin.FormsBook.Toolkit { public class IntToBoolConverter : IValueConverter { public object Convert(object value, Type targetType, object parameter, CultureInfo culture) { return (int)value != 0; } public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture) { return (bool)value ? 1 : 0; } } }
XAML:
<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="ButtonEnabler.ButtonEnablerPage" Padding="10, 50, 10, 0"> <ContentPage.Resources> <ResourceDictionary> <toolkit:IntToBoolConverter x:Key="intToBool" /> </ResourceDictionary> </ContentPage.Resources> <StackLayout Spacing="20"> <Entry x:Name="entry" Text="" Placeholder="text to enable button" /> <!--注意 Converter 的用法,這邊先將 Converter 做成資源檔再使用--> <Button Text="Save or Send (or something)" FontSize="Medium" HorizontalOptions="Center" IsEnabled="{Binding Source={x:Reference entry}, Path=Text.Length, Converter={StaticResource intToBool}}" /> </StackLayout> </ContentPage>
執行結果:
兩個小技巧:
1. 如果 Converter 只用到一次,就不用寫資源檔了
<Button Text="Save or Send (or something)" FontSize="Large" HorizontalOptions="Center"> <Button.IsEnabled> <Binding Source="{x:Reference entry}" Path="Text.Length"> <Binding.Converter> <toolkit:IntToBoolConverter /> </Binding.Converter> </Binding> </Button.IsEnabled> </Button>
2. 更加彈性的 Converter
假設我們要將 Switch 的值顯示出的 "Let's do it" or "Not now",而不是預設的 "True" or "False",
我們可以在 Converter 放入自訂屬性,XAML 使用時去設定其值:
namespace Xamarin.FormsBook.Toolkit { public class BoolToStringConverter : IValueConverter { //自訂屬性 public string TrueText { set; get; } public string FalseText { set; get; } public object Convert(object value, Type targetType, object parameter, CultureInfo culture) { return (bool)value ? TrueText : FalseText; } public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture) { return false; } } }
<toolkit:BoolToStringConverter x:Key="boolToString" TrueText="Let's do it" FalseText="Not now" />
你好 我到這段有個小小的疑問
回覆刪除Style這個標籤 不會自動跳出來耶
都要自己打全部...屬性標籤也是
但其他的會出現也可以選
VS針對XAML的Intelisense似乎還沒有很完整...
刪除但目前2.5已經比上一版本好多了,
只能期待Xamarin慢慢改進...