第十六章:数据绑定(七)

简介:

绑定值转换器

您现在知道如何使用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:
2018_09_27_101110
如果您只使用值转换器的一个实例,则无需将其存储在“资源”字典中。 您可以在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现在可以显示两种状态所需的任何文本,并可以使用自定义颜色为该文本着色:
2018_09_27_101727
既然您已经看过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);
    }
}

下一个示例使用此类。

目录
相关文章
|
7月前
|
C# 数据库 数据格式
C#二十八 数据绑定
C#二十八 数据绑定
44 0
|
前端开发
前端工作总结266-数据绑定逻辑处理
前端工作总结266-数据绑定逻辑处理
67 0
|
前端开发 JavaScript Android开发
|
前端开发 JavaScript Android开发
|
前端开发 JavaScript Android开发
|
前端开发 Android开发 Windows
|
前端开发 JavaScript Android开发
|
前端开发 JavaScript Android开发
|
JavaScript Android开发
|
JavaScript 前端开发 Android开发

热门文章

最新文章