綁定值轉換器
您現在知道如何使用StringFormat将任何綁定源對象轉換為字元串。但是其他資料轉換呢?也許您正在使用Slider作為綁定源,但目标是期望整數而不是雙精度。或者您可能希望将Switch的值顯示為文本,但您想要“是”和“否”而不是“真”和“假”。
這個工作的工具是一個類 - 通常是一個非常小的類 - 非正式地稱為值轉換器或(有時)綁定轉換器。更正式地說,這樣的類實作了IValueConverter接口。此接口在Xamarin.Forms命名空間中定義,但它類似于Microsoft基于XAML的環境中提供的接口。
示例:有時應用程式需要根據條目中文本的存在啟用或禁用Button。也許按鈕标記為儲存,條目是檔案名。或者按鈕标記為發送,條目包含郵件收件人。除非條目包含至少一個文本字元,否則不應啟用Button。
有幾種方法可以完成這項工作。在後面的章節中,您将看到資料觸發器如何執行它(并且還可以對條目中的文本執行有效性檢查)。但是對于本章,讓我們用一個值轉換器來做。
資料綁定目标是Button的IsEnabled屬性。該屬性屬于bool類型。綁定源是Entry的Text屬性,或者更确切地說是Text屬性的Length屬性。該Length屬性的類型為int。值轉換器需要将int等于0轉換為bool為false,将int轉換為bool為true。代碼很簡單。我們隻需要将它包裝在一個實作IValueConverter的類中。
這是Xamarin.FormsBook.Toolkit庫中的該類,包含using指令。 IValueConverter接口由兩個方法組成,名為Convert和ConvertBack,具有相同的參數。您可以根據需要使類成為通用類或專用類:
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;
}
}
}
當您在資料綁定中包含此類時 - 您将很快看到如何執行此操作 - 隻要值從源傳遞到目标,就會調用Convert方法。
Convert的value參數是要轉換的資料綁定源的值。您可以使用GetType來确定其類型,或者您可以假設它始終是特定類型。在此示例中,假定value參數的類型為int,是以轉換為int不會引發異常。更複雜的值轉換器可以執行更多有效性檢查。
targetType是資料綁定目标屬性的類型。多功能值轉換器可以使用此參數來定制不同目标類型的轉換。 Convert方法應傳回與此targetType比對的對象或值。這個特殊的Convert方法假設targetType是bool。
參數參數是一個可選的轉換參數,您可以将其指定為Binding類的屬性。 (您将在第18章“MVVM”中看到一個示例。)
最後,如果您需要執行特定于文化的轉換,則最後一個參數是您應該使用的CultureInfo對象。
此特定Convert方法的主體假定value為int,并且該方法傳回bool,如果該整數非零,則為true。
ConvertBack方法僅針對TwoWay或OneWayToSource綁定調用。對于ConvertBack方法,value參數是target的值,targetType參數實際上是source屬性的類型。如果您知道将永遠不會調用ConvertBack方法,則可以簡單地忽略所有參數并從中傳回null或0。使用一些值轉換器,實作ConvertBack主體實際上是不可能的,但有時它非常簡單(如本例所示)。
在代碼中使用值轉換器時,将轉換器的執行個體設定為Binding的Converter屬性。您可以選擇通過設定Binding的ConverterParameter屬性将參數傳遞給值轉換器。
如果綁定也具有StringFormat,則值轉換器傳回的值是格式化為字元串的值。
通常,在XAML檔案中,您需要在Resources字典中執行個體化值轉換器,然後使用StaticResource在Binding表達式中引用它。值轉換器不應該保持狀态,是以可以在多個綁定之間共享。
這是使用值轉換器的ButtonEnabler程式:
<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" />
<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>
IntToBoolConverter在Resources字典中執行個體化,并作為Button的IsEnabled屬性上設定的Binding中的嵌套标記擴充引用。
請注意,Text屬性在Entry标記中顯式初始化為空字元串。 預設情況下,Text屬性為null,這意味着Text.Length的綁定Path設定不會産生有效值。
您可能還記得以前的章節中,僅在XAML中引用的Xamarin.FormsBook.Toolkit庫中的類不足以建立從應用程式到庫的連結。 是以,ButtonEnabler中的App構造函數調用Toolkit.Init:
public class App : Application
{
public App()
{
Xamarin.FormsBook.Toolkit.Toolkit.Init();
MainPage = new ButtonEnablerPage();
}
__
}
本章中使用Xamarin.Forms Book.Toolkit庫的所有程式中都會出現類似的代碼。
螢幕截圖确認除非條目包含一些文本,否則不會啟用Button:
如果您隻使用值轉換器的一個執行個體,則無需将其存儲在“資源”字典中。 您可以在Binding标記中執行個體化它,使用target屬性的property-element标簽和Binding的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>
有時,值轉換器可以友善地定義幾個簡單的屬性。 例如,假設您要為Switch的兩個設定顯示一些文本,但您不想使用“True”和“False”,并且您不希望将替代值寫死到值轉換器中。 這是一個BoolToStringConverter,它包含兩個文本字元串的公共屬性:
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;
}
}
}
Convert方法的主體是微不足道的:它隻是根據布爾值參數在兩個字元串之間進行選擇。
類似的值轉換器将布爾值轉換為兩種顔色之一:
namespace Xamarin.FormsBook.Toolkit
{
public class BoolToColorConverter : IValueConverter
{
public Color TrueColor { set; get; }
public Color FalseColor { set; get; }
public object Convert(object value, Type targetType,
object parameter, CultureInfo culture)
{
return (bool)value ? TrueColor : FalseColor;
}
public object ConvertBack(object value, Type targetType,
object parameter, CultureInfo culture)
{
return false;
}
}
}
SwitchText程式為兩個不同的字元串對執行個體化BoolToStringConverter轉換器兩次:一次在Resources字典中,然後在Binding.Converter屬性元素标記内。 最終Label的兩個屬性受BoolToStringConverter和BoolToColorConverter的影響,它們基于Switch的相同IsToggled屬性:
<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="SwitchText.SwitchTextPage"
Padding="10, 0">
<ContentPage.Resources>
<ResourceDictionary>
<toolkit:BoolToStringConverter x:Key="boolToString"
TrueText="Let's do it"
FalseText="Not now" />
<Style TargetType="Label">
<Setter Property="FontSize" Value="Medium" />
<Setter Property="VerticalOptions" Value="Center" />
</Style>
</ResourceDictionary>
</ContentPage.Resources>
<StackLayout>
<!-- First Switch with text. -->
<StackLayout Orientation="Horizontal"
VerticalOptions="CenterAndExpand">
<Label Text="Learn more?" />
<Switch x:Name="switch1"
VerticalOptions="Center" />
<Label Text="{Binding Source={x:Reference switch1},
Path=IsToggled,
Converter={StaticResource boolToString}}"
HorizontalOptions="FillAndExpand" />
</StackLayout>
<!-- Second Switch with text. -->
<StackLayout Orientation="Horizontal"
VerticalOptions="CenterAndExpand">
<Label Text="Subscribe?" />
<Switch x:Name="switch2"
VerticalOptions="Center" />
<Label Text="{Binding Source={x:Reference switch2},
Path=IsToggled,
Converter={StaticResource boolToString}}"
HorizontalOptions="FillAndExpand" />
</StackLayout>
<!-- Third Switch with text and color. -->
<StackLayout Orientation="Horizontal"
VerticalOptions="CenterAndExpand">
<Label Text="Leave page?" />
<Switch x:Name="switch3"
VerticalOptions="Center" />
<Label HorizontalOptions="FillAndExpand">
<Label.Text>
<Binding Source="{x:Reference switch3}"
Path="IsToggled">
<Binding.Converter>
<toolkit:BoolToStringConverter TrueText="Yes"
FalseText="No" />
</Binding.Converter>
</Binding>
</Label.Text>
<Label.TextColor>
<Binding Source="{x:Reference switch3}"
Path="IsToggled">
<Binding.Converter>
<toolkit:BoolToColorConverter TrueColor="Green"
FalseColor="Red" />
</Binding.Converter>
</Binding>
</Label.TextColor>
</Label>
</StackLayout>
</StackLayout>
</ContentPage>
使用兩個相當簡單的綁定轉換器,Switch現在可以顯示兩種狀态所需的任何文本,并可以使用自定義顔色為該文本着色:
既然您已經看過BoolToStringConverter和BoolToColorConverter,您能否将該技術推廣到任何類型的對象? 這是Xamarin.FormsBook.Toolkit庫中的通用BoolToObjectConverter:
public class BoolToObjectConverter<T> : IValueConverter
{
public T TrueObject { set; get; }
public T FalseObject { set; get; }
public object Convert(object value, Type targetType,
object parameter, CultureInfo culture)
{
return (bool)value ? this.TrueObject : this.FalseObject;
}
public object ConvertBack(object value, Type targetType,
object parameter, CultureInfo culture)
{
return ((T)value).Equals(this.TrueObject);
}
}
下一個示例使用此類。