天天看點

第十六章:資料綁定(七)綁定值轉換器

綁定值轉換器

您現在知道如何使用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);
    }
}           

下一個示例使用此類。

繼續閱讀