Преобразователи значений привязки Xamarin.FormsXamarin.Forms Binding Value Converters

Скачать пример Скачать примерDownload Sample Download the sample

Привязки данных обычно передают данные из исходного свойства в целевое свойство (а иногда из целевого свойства в исходное).Data bindings usually transfer data from a source property to a target property, and in some cases from the target property to the source property. Эта передача проста в том случае, когда исходные и целевые свойства относятся к одному типу или когда один тип может быть преобразован в другой тип путем неявного преобразования.This transfer is straightforward when the source and target properties are of the same type, or when one type can be converted to the other type through an implicit conversion. Если это не так, должно выполняться преобразование типов.When that is not the case, a type conversion must take place.

В статье, посвященной форматированию строк, было показано, как можно использовать свойство StringFormat привязки данных для преобразования любого типа в строку.In the String Formatting article, you saw how you can use the StringFormat property of a data binding to convert any type into a string. Для других типов преобразований необходимо написать специальный код в классе, реализующем интерфейс IValueConverter.For other types of conversions, you need to write some specialized code in a class that implements the IValueConverter interface. (Универсальная платформа Windows содержит аналогичный класс с именем IValueConverter в пространстве имен Windows.UI.Xaml.Data, однако этот IValueConverter находится в пространстве имен Xamarin.Forms.) Классы, реализующие IValueConverter, называются преобразователями величин, а также преобразователями привязки или преобразователями значений привязки.(The Universal Windows Platform contains a similar class named IValueConverter in the Windows.UI.Xaml.Data namespace, but this IValueConverter is in the Xamarin.Forms namespace.) Classes that implement IValueConverter are called value converters, but they are also often referred to as binding converters or binding value converters.

Интерфейс IValueConverterThe IValueConverter Interface

Предположим, что вы хотите определить привязку данных, в которой исходное свойство имеет тип int, а целевым свойством является bool.Suppose you want to define a data binding where the source property is of type int but the target property is a bool. Требуется, чтобы эта привязка данных создавала значение false, если целочисленный источник равняется нулю, и значение true в противном случае.You want this data binding to produce a false value when the integer source is equal to 0, and true otherwise.

Это можно сделать с помощью класса, реализующего интерфейс IValueConverter.You can do this with a class that implements the IValueConverter interface:

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;
    }
}

Вы можете задать экземпляр этого класса для свойства Converter класса Binding или свойства Converter расширения разметки Binding.You set an instance of this class to the Converter property of the Binding class or to the Converter property of the Binding markup extension. Этот класс становится частью привязки данных.This class becomes part of the data binding.

Метод Convert вызывается при перемещении данных из источника в целевое свойство в привязках OneWay или TwoWay.The Convert method is called when data moves from the source to the target in OneWay or TwoWay bindings. Параметр value — это объект или значение из источника привязки данных.The value parameter is the object or value from the data-binding source. Метод должен возвращать значение типа целевого свойства привязки данных.The method must return a value of the type of the data-binding target. Показанный здесь метод приводит параметр value к int, а затем сравнивает их с нулем для получения логического (bool) возвращаемого значения.The method shown here casts the value parameter to an int and then compares it with 0 for a bool return value.

Метод ConvertBack вызывается, когда данные перемещаются из целевого в исходное свойство в привязках TwoWay или OneWayToSource.The ConvertBack method is called when data moves from the target to the source in TwoWay or OneWayToSource bindings. ConvertBack выполняет обратное преобразование. Предполагается, что параметр value является bool из целевого свойства, и выполняется его преобразование в возвращаемое значение int для исходного свойства.ConvertBack performs the opposite conversion: It assumes the value parameter is a bool from the target, and converts it to an int return value for the source.

Если привязка данных также содержит параметр StringFormat, вызывается преобразователь величин, прежде чем результат форматируется как строка.If the data binding also includes a StringFormat setting, the value converter is invoked before the result is formatted as a string.

Страница Включение кнопок в примере Демонстрации привязки данных демонстрирует использование этого преобразователя в привязке данных.The Enable Buttons page in the Data Binding Demos sample demonstrates how to use this value converter in a data binding. Экземпляр IntToBoolConverter создается в словаре ресурсов страницы.The IntToBoolConverter is instantiated in the page's resource dictionary. Затем он указывается с помощью расширения разметки StaticResource, чтобы задать свойство в двух привязках данных Converter.It is then referenced with a StaticResource markup extension to set the Converter property in two data bindings. Совместное использование преобразователей данных несколькими привязками данных на странице широко распространено:It is very common to share data converters among multiple data bindings on the page:

<ContentPage xmlns="http://xamarin.com/schemas/2014/forms"
             xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
             xmlns:local="clr-namespace:DataBindingDemos"
             x:Class="DataBindingDemos.EnableButtonsPage"
             Title="Enable Buttons">
    <ContentPage.Resources>
        <ResourceDictionary>
            <local:IntToBoolConverter x:Key="intToBool" />
        </ResourceDictionary>
    </ContentPage.Resources>

    <StackLayout Padding="10, 0">
        <Entry x:Name="entry1"
               Text=""
               Placeholder="enter search term"
               VerticalOptions="CenterAndExpand" />

        <Button Text="Search"
                HorizontalOptions="Center"
                VerticalOptions="CenterAndExpand"
                IsEnabled="{Binding Source={x:Reference entry1},
                                    Path=Text.Length,
                                    Converter={StaticResource intToBool}}" />

        <Entry x:Name="entry2"
               Text=""
               Placeholder="enter destination"
               VerticalOptions="CenterAndExpand" />

        <Button Text="Submit"
                HorizontalOptions="Center"
                VerticalOptions="CenterAndExpand"
                IsEnabled="{Binding Source={x:Reference entry2},
                                    Path=Text.Length,
                                    Converter={StaticResource intToBool}}" />
    </StackLayout>
</ContentPage>

Если преобразователь величин используется на нескольких страницах приложения, можно создать его экземпляр в словаре ресурсов в файле App.xaml.If a value converter is used in multiple pages of your application, you can instantiate it in the resource dictionary in the App.xaml file.

Страница Включение кнопок демонстрирует распространенную потребность, когда Button выполняет операцию на основе текста, который пользователь вводит в представлении Entry.The Enable Buttons page demonstrates a common need when a Button performs an operation based on text that the user types into an Entry view. Если никакие данные не были введены в Entry, Button должен быть отключен.If nothing has been typed into the Entry, the Button should be disabled. Каждый элемент Button содержит привязку данных для его свойства IsEnabled.Each Button contains a data binding on its IsEnabled property. Источником привязки данных является свойство Length свойства Text соответствующего элемента Entry.The data-binding source is the Length property of the Text property of the corresponding Entry. Если это свойство Length не возвращает нуль, преобразователь величин возвращает true и включается элемент Button:If that Length property is not 0, the value converter returns true and the Button is enabled:

Включение кнопокEnable Buttons

Обратите внимание, что свойство Text в каждом Entry инициализируется в пустую строку.Notice that the Text property in each Entry is initialized to an empty string. В этом случае свойство Text имеет значение null по умолчанию, а данные привязки не будут работать.The Text property is null by default, and the data binding will not work in that case.

Некоторые преобразователи величин созданы специально для конкретных приложений, тогда как другие являются общими.Some value converters are written specifically for particular applications, while others are generalized. Если вы знаете, что преобразователь величин будет использоваться только в привязках OneWay, то метод ConvertBack может просто возвращать null.If you know that a value converter will only be used in OneWay bindings, then the ConvertBack method can simply return null.

Метод Convert в приведенном выше примере неявно предполагает, что аргумент value имеет тип int, а возвращаемое значение должно быть типа bool.The Convert method shown above implicitly assumes that the value argument is of type int and the return value must be of type bool. Аналогичным образом метод ConvertBack предполагает, что аргумент value имеет тип bool, и возвращает значение int.Similarly, the ConvertBack method assumes that the value argument is of type bool and the return value is int. Если это не так, возникнет исключение времени выполнения.If that is not the case, a runtime exception will occur.

Вы можете написать более общие преобразователи величин, которые принимают несколько разных типов данных.You can write value converters to be more generalized and to accept several different types of data. Методы Convert и ConvertBack могут использовать операторы as или is с параметром value либо вызывать GetType для этого параметра, чтобы определить его тип, а затем выполнять соответствующие действия.The Convert and ConvertBack methods can use the as or is operators with the value parameter, or can call GetType on that parameter to determine its type, and then do something appropriate. Ожидаемый тип возвращаемого значения метода задается параметром targetType.The expected type of each method's return value is given by the targetType parameter. В некоторых случаях преобразователи величин используются с привязками данных разных целевых типов: преобразователь величин может использовать аргумент targetType для выполнения преобразования для правильного типа.Sometimes, value converters are used with data bindings of different target types; the value converter can use the targetType argument to perform a conversion for the correct type.

Если выполняемое преобразование отличается для разных языков и региональных параметров, используйте для этой цели параметр culture.If the conversion being performed is different for different cultures, use the culture parameter for this purpose. Аргумент parameter для Convert и ConvertBack рассматривается далее в этой статье.The parameter argument to Convert and ConvertBack is discussed later in this article.

Свойства преобразователя привязкиBinding Converter Properties

Классы преобразователей величин могут иметь свойства и универсальные параметры.Value converter classes can have properties and generic parameters. Этот конкретный преобразователь величин преобразует bool из источника в объект типа T для целевого свойства:This particular value converter converts a bool from the source to an object of type T for the target:

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 ? TrueObject : FalseObject;
    }

    public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture)
    {
        return ((T)value).Equals(TrueObject);
    }
}

Страница Переключение индикаторов демонстрирует, как он может использоваться для отображения значения представления Switch.The Switch Indicators page demonstrates how it can be used to display the value of a Switch view. Хотя, как правило, экземпляры преобразователей величин создаются как ресурсы в словаре ресурсов, на этой странице показан альтернативный вариант: экземпляр каждого преобразователя величин создается между тегами элемента и свойства Binding.Converter.Although it's common to instantiate value converters as resources in a resource dictionary, this page demonstrates an alternative: Each value converter is instantiated between Binding.Converter property-element tags. x:TypeArguments указывает универсальный аргумент, а для TrueObject и FalseObject задаются объекты этого типа:The x:TypeArguments indicates the generic argument, and TrueObject and FalseObject are both set to objects of that type:

<ContentPage xmlns="http://xamarin.com/schemas/2014/forms"
             xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
             xmlns:local="clr-namespace:DataBindingDemos"
             x:Class="DataBindingDemos.SwitchIndicatorsPage"
             Title="Switch Indicators">
    <ContentPage.Resources>
        <ResourceDictionary>
            <Style TargetType="Label">
                <Setter Property="FontSize" Value="18" />
                <Setter Property="VerticalOptions" Value="Center" />
            </Style>

            <Style TargetType="Switch">
                <Setter Property="VerticalOptions" Value="Center" />
            </Style>
        </ResourceDictionary>
    </ContentPage.Resources>

    <StackLayout Padding="10, 0">
        <StackLayout Orientation="Horizontal"
                     VerticalOptions="CenterAndExpand">
            <Label Text="Subscribe?" />
            <Switch x:Name="switch1" />
            <Label>
                <Label.Text>
                    <Binding Source="{x:Reference switch1}"
                             Path="IsToggled">
                        <Binding.Converter>
                            <local:BoolToObjectConverter x:TypeArguments="x:String"
                                                         TrueObject="Of course!"
                                                         FalseObject="No way!" />
                        </Binding.Converter>
                    </Binding>
                </Label.Text>
            </Label>
        </StackLayout>

        <StackLayout Orientation="Horizontal"
                     VerticalOptions="CenterAndExpand">
            <Label Text="Allow popups?" />
            <Switch x:Name="switch2" />
            <Label>
                <Label.Text>
                    <Binding Source="{x:Reference switch2}"
                             Path="IsToggled">
                        <Binding.Converter>
                            <local:BoolToObjectConverter x:TypeArguments="x:String"
                                                         TrueObject="Yes"
                                                         FalseObject="No" />
                        </Binding.Converter>
                    </Binding>
                </Label.Text>
                <Label.TextColor>
                    <Binding Source="{x:Reference switch2}"
                             Path="IsToggled">
                        <Binding.Converter>
                            <local:BoolToObjectConverter x:TypeArguments="Color"
                                                         TrueObject="Green"
                                                         FalseObject="Red" />
                        </Binding.Converter>
                    </Binding>
                </Label.TextColor>
            </Label>
        </StackLayout>

        <StackLayout Orientation="Horizontal"
                     VerticalOptions="CenterAndExpand">
            <Label Text="Learn more?" />
            <Switch x:Name="switch3" />
            <Label FontSize="18"
                   VerticalOptions="Center">
                <Label.Style>
                    <Binding Source="{x:Reference switch3}"
                             Path="IsToggled">
                        <Binding.Converter>
                            <local:BoolToObjectConverter x:TypeArguments="Style">
                                <local:BoolToObjectConverter.TrueObject>
                                    <Style TargetType="Label">
                                        <Setter Property="Text" Value="Indubitably!" />
                                        <Setter Property="FontAttributes" Value="Italic, Bold" />
                                        <Setter Property="TextColor" Value="Green" />
                                    </Style>                                    
                                </local:BoolToObjectConverter.TrueObject>

                                <local:BoolToObjectConverter.FalseObject>
                                    <Style TargetType="Label">
                                        <Setter Property="Text" Value="Maybe later" />
                                        <Setter Property="FontAttributes" Value="None" />
                                        <Setter Property="TextColor" Value="Red" />
                                    </Style>
                                </local:BoolToObjectConverter.FalseObject>
                            </local:BoolToObjectConverter>
                        </Binding.Converter>
                    </Binding>
                </Label.Style>
            </Label>
        </StackLayout>
    </StackLayout>
</ContentPage>

В последних трех парах Switch и Label универсальный аргумент имеет значение Style и все объекты Style предоставляются для значений TrueObject и FalseObject.In the last of the three Switch and Label pairs, the generic argument is set to Style, and entire Style objects are provided for the values of TrueObject and FalseObject. Они переопределяют неявный стиль для Label, заданный в словаре ресурсов, поэтому свойства в этом стиле явно назначаются Label.These override the implicit style for Label set in the resource dictionary, so the properties in that style are explicitly assigned to the Label. Включение и выключение Switch приводит к тому, что изменения отражаются в соответствующем Label:Toggling the Switch causes the corresponding Label to reflect the change:

Переключение индикаторовSwitch Indicators

Triggers можно также использовать для реализации аналогичных изменений в интерфейсе пользователя на основе других представлений.It's also possible to use Triggers to implement similar changes in the user-interface based on other views.

Параметры преобразователя привязкиBinding Converter Parameters

Класс Binding определяет свойство ConverterParameter, и расширение разметки Binding также определяет свойство ConverterParameter.The Binding class defines a ConverterParameter property, and the Binding markup extension also defines a ConverterParameter property. Если это свойство задано, то значение передается в методы Convert и ConvertBack как аргумент parameter.If this property is set, then the value is passed to the Convert and ConvertBack methods as the parameter argument. Даже если экземпляр преобразователя величин совместно используется несколькими привязками данных, параметр ConverterParameter может отличаться для выполнения немного отличающихся преобразований.Even if the instance of the value converter is shared among several data bindings, the ConverterParameter can be different to perform somewhat different conversions.

Использование ConverterParameter демонстрируется на примере программы выбора цветов.The use of ConverterParameter is demonstrated with a color-selection program. В этом случае RgbColorViewModel имеет три свойства типа double с именем Red, Green и Blue, которые используются для создания значения Color:In this case, the RgbColorViewModel has three properties of type double named Red, Green, and Blue that it uses to construct a Color value:

public class RgbColorViewModel : INotifyPropertyChanged
{
    Color color;
    string name;

    public event PropertyChangedEventHandler PropertyChanged;

    public double Red
    {
        set
        {
            if (color.R != value)
            {
                Color = new Color(value, color.G, color.B);
            }
        }
        get
        {
            return color.R;
        }
    }

    public double Green
    {
        set
        {
            if (color.G != value)
            {
                Color = new Color(color.R, value, color.B);
            }
        }
        get
        {
            return color.G;
        }
    }

    public double Blue
    {
        set
        {
            if (color.B != value)
            {
                Color = new Color(color.R, color.G, value);
            }
        }
        get
        {
            return color.B;
        }
    }

    public Color Color
    {
        set
        {
            if (color != value)
            {
                color = value;
                PropertyChanged?.Invoke(this, new PropertyChangedEventArgs("Red"));
                PropertyChanged?.Invoke(this, new PropertyChangedEventArgs("Green"));
                PropertyChanged?.Invoke(this, new PropertyChangedEventArgs("Blue"));
                PropertyChanged?.Invoke(this, new PropertyChangedEventArgs("Color"));

                Name = NamedColor.GetNearestColorName(color);
            }
        }
        get
        {
            return color;
        }
    }

    public string Name
    {
        private set
        {
            if (name != value)
            {
                name = value;
                PropertyChanged?.Invoke(this, new PropertyChangedEventArgs("Name"));
            }
        }
        get
        {
            return name;
        }
    }
}

Значения свойств Red, Green и Blue задаются в диапазоне от 0 до 1.The Red, Green, and Blue properties range between 0 and 1. При этом требуется, чтобы компоненты отображались как шестнадцатеричные значения из двух цифр.However, you might prefer that the components be displayed as two-digit hexadecimal values.

Чтобы отобразить их в виде шестнадцатеричных значений в XAML, они должны быть умножены на 255, преобразованы в целое число, а затем отформатированы со спецификацией X2 в свойстве StringFormat.To display these as hexadecimal values in XAML, they must be multiplied by 255, converted to an integer, and then formatted with a specification of "X2" in the StringFormat property. Преобразователь величин может обработать первые две задачи (умножение на 255 и преобразование в целое число).The first two tasks (multiplying by 255 and converting to an integer) can be handled by the value converter. Чтобы сделать преобразователь величин максимально универсальным, можно указать множитель с помощью свойства ConverterParameter, которое означает, что он вводит методы Convert и ConvertBack как аргумент parameter:To make the value converter as generalized as possible, the multiplication factor can be specified with the ConverterParameter property, which means that it enters the Convert and ConvertBack methods as the parameter argument:

public class DoubleToIntConverter : IValueConverter
{
    public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
    {
        return (int)Math.Round((double)value * GetParameter(parameter));
    }

    public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture)
    {
        return (int)value / GetParameter(parameter);
    }

    double GetParameter(object parameter)
    {
        if (parameter is double)
            return (double)parameter;

        else if (parameter is int)
            return (int)parameter;

        else if (parameter is string)
            return double.Parse((string)parameter);

        return 1;
    }
}

Convert преобразует из double в int с умножением на значение parameter; ConvertBack делит аргумент value целого числа на parameter и возвращает результат double.The Convert converts from a double to int while multiplying by the parameter value; the ConvertBack divides the integer value argument by parameter and returns a double result. (В программе, показанной ниже, преобразователь величин используется только в связи с форматированием строк, поэтому ConvertBack не используется.)(In the program shown below, the value converter is used only in connection with string formatting, so ConvertBack is not used.)

Тип аргумента parameter может отличаться в зависимости от того, определена ли привязка данных в коде или в XAML.The type of the parameter argument is likely to be different depending on whether the data binding is defined in code or XAML. Если свойство ConverterParameter объекта Binding задается в коде, вполне вероятно, ему будет присвоено числовое значение:If the ConverterParameter property of Binding is set in code, it's likely to be set to a numeric value:

binding.ConverterParameter = 255;

Свойство ConverterParameter имеет тип Object, поэтому компилятор C# интерпретирует литерал 255 как целое число и присваивает это значение свойству.The ConverterParameter property is of type Object, so the C# compiler interprets the literal 255 as an integer, and sets the property to that value.

При этом в XAML ConverterParameter, вероятнее всего, будет задано следующим образом:In XAML, however, the ConverterParameter is likely to be set like this:

<Label Text="{Binding Red,
                      Converter={StaticResource doubleToInt},
                      ConverterParameter=255,
                      StringFormat='Red = {0:X2}'}" />

255 выглядит как число, но, поскольку ConverterParameter имеет тип Object, средство синтаксического анализа XAML обрабатывает 255 как строку.The 255 looks like a number, but because ConverterParameter is of type Object, the XAML parser treats the 255 as a string.

По этой причине преобразователь величин, показанный выше, включает отдельный метод GetParameter, который обрабатывает ситуации, когда parameter принадлежит к типу double, int или string.For that reason, the value converter shown above includes a separate GetParameter method that handles cases for parameter being of type double, int, or string.

Страница Выбор цвета RGB создает экземпляр DoubleToIntConverter в словаре ресурсов, за которым следует определение двух неявные стилей:The RGB Color Selector page instantiates DoubleToIntConverter in its resource dictionary following the definition of two implicit styles:

<ContentPage xmlns="http://xamarin.com/schemas/2014/forms"
             xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
             xmlns:local="clr-namespace:DataBindingDemos"
             x:Class="DataBindingDemos.RgbColorSelectorPage"
             Title="RGB Color Selector">
    <ContentPage.Resources>
        <ResourceDictionary>
            <Style TargetType="Slider">
                <Setter Property="VerticalOptions" Value="CenterAndExpand" />
            </Style>

            <Style TargetType="Label">
                <Setter Property="HorizontalTextAlignment" Value="Center" />
            </Style>

            <local:DoubleToIntConverter x:Key="doubleToInt" />
        </ResourceDictionary>
    </ContentPage.Resources>

    <StackLayout>
        <StackLayout.BindingContext>
            <local:RgbColorViewModel Color="Gray" />
        </StackLayout.BindingContext>

        <BoxView Color="{Binding Color}"
                 VerticalOptions="FillAndExpand" />

        <StackLayout Margin="10, 0">
            <Label Text="{Binding Name}" />

            <Slider Value="{Binding Red}" />
            <Label Text="{Binding Red,
                                  Converter={StaticResource doubleToInt},
                                  ConverterParameter=255,
                                  StringFormat='Red = {0:X2}'}" />

            <Slider Value="{Binding Green}" />
            <Label Text="{Binding Green,
                                  Converter={StaticResource doubleToInt},
                                  ConverterParameter=255,
                                  StringFormat='Green = {0:X2}'}" />

            <Slider Value="{Binding Blue}" />
            <Label>
                <Label.Text>
                    <Binding Path="Blue"
                             StringFormat="Blue = {0:X2}"
                             Converter="{StaticResource doubleToInt}">
                        <Binding.ConverterParameter>
                            <x:Double>255</x:Double>
                        </Binding.ConverterParameter>
                    </Binding>
                </Label.Text>
            </Label>
        </StackLayout>
    </StackLayout>
</ContentPage>    

Значения свойств Red и Green отображаются с помощью расширения разметки Binding.The values of the Red and Green properties are displayed with a Binding markup extension. Свойство Blue, тем не менее, создает экземпляр класса Binding для демонстрации способа задания явного значения double для свойства ConverterParameter.The Blue property, however, instantiates the Binding class to demonstrate how an explicit double value can be set to ConverterParameter property.

Результат выглядит так:Here's the result:

Средство выбора цвета RGBRGB Color Selector