Compartilhar via


Conversores de valor de vinculação

Browse sample. Navegue pelo exemplo

As associações de dados da Interface do Usuário do Aplicativo Multiplataforma do .NET (.NET MAUI) geralmente transferem dados de uma propriedade de origem para uma propriedade de destino e, em alguns casos, da propriedade de destino para a propriedade de origem. Essa transferência é simples quando as propriedades de origem e de destino são do mesmo tipo ou quando um tipo pode ser convertido para outro por meio de uma conversão implícita. Quando não é esse o caso, é necessário realizar uma conversão de tipo.

No artigo Formatação de cadeia de caracteres, você viu como pode usar a StringFormat propriedade de uma associação de dados para converter qualquer tipo em uma cadeia de caracteres. Para outros tipos de conversões, você precisa escrever código especializado em uma classe que implementa a interface IValueConverter. As classes que implementam IValueConverter são denominadas conversores de valor, mas também são conhecidas como conversores de associação ou conversores de valor de associação.

Conversores de valor de vinculação

Digamos que você queira definir uma associação de dados em que a propriedade de origem é do tipo int, mas a propriedade de destino é um bool. Você quer que essa associação de dados produza um valor de false quando a origem do inteiro for igual a 0 e, caso contrário, true. Isso pode ser alcançado com uma classe que implementa a 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;
    }
}

Em seguida, defina uma instância dessa classe para a Converter Binding propriedade da classe ou para a Converter propriedade da extensão de Binding marcação. Essa classe se torna parte da associação de dados.

O método Convert é chamado quando dados são passados da origem para o destino nas associações OneWay ou TwoWay. O parâmetro value é o objeto ou o valor da origem da associação de dados. O método deve retornar um valor com o tipo do destino da associação de dados. O método mostrado aqui converte o parâmetro value para um int e, em seguida, o compara com 0 para um valor retornado de bool.

O método ConvertBack é chamado quando dados são passados do destino para a origem nas associações TwoWay ou OneWayToSource. ConvertBack realiza a conversão oposta: ele pressupõe que o parâmetro value é um bool do destino e o converte em um valor retornado de int para a fonte.

Observação

Se uma associação de dados também incluir uma configuração, o conversor de valores será chamado antes que o resultado seja formatado como uma StringFormat cadeia de caracteres.

O exemplo a seguir demonstra como usar esse conversor de valor em uma associação de dados:

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

    <StackLayout Padding="10, 0">
        <Entry x:Name="entry1"
               Text=""
               Placeholder="enter search term"
               VerticalOptions="Center" />
        <Button Text="Search"
                HorizontalOptions="Center"
                VerticalOptions="Center"
                IsEnabled="{Binding Source={x:Reference entry1},
                                    Path=Text.Length,
                                    Converter={StaticResource intToBool}}" />
        <Entry x:Name="entry2"
               Text=""
               Placeholder="enter destination"
               VerticalOptions="Center" />
        <Button Text="Submit"
                HorizontalOptions="Center"
                VerticalOptions="Center"
                IsEnabled="{Binding Source={x:Reference entry2},
                                    Path=Text.Length,
                                    Converter={StaticResource intToBool}}" />
    </StackLayout>
</ContentPage>

Neste exemplo, o IntToBoolConverter é instanciado no dicionário de recursos da página. Em seguida, ele é referenciado com uma StaticResource extensão de marcação para definir a Converter propriedade em duas associações de dados. É muito comum compartilhar conversores de dados entre várias ligações de dados na página. Se um conversor de valores for usado em várias páginas do seu aplicativo, você poderá instanciá-lo no dicionário de recursos no nível do aplicativo.

Este exemplo demonstra uma necessidade comum quando um executa uma operação com base no texto que o usuário digita em um Button Entry modo de exibição. A Text propriedade de cada um Entry é inicializada para uma cadeia de caracteres vazia, porque a propriedade é null por padrão e a Text associação de dados não funcionará nesse caso. Se nada tiver sido digitado no Entry, o Button deverá ser desabilitado. Cada Button contém uma associação de dados em sua propriedade IsEnabled. A origem da associação de dados é a propriedade Length da propriedade Text do Entry correspondente. Se essa propriedade Length não for 0, o conversor de valor retornará true e Button será habilitado:

Enable buttons.

Observação

Se você souber que um conversor de valor será usado apenas em associações de OneWay, o método ConvertBack poderá simplesmente retornar null.

O Convert método mostrado acima assume que o argumento é do tipo e o value valor de retorno deve ser do tipo boolint . Da mesma forma, o método ConvertBack supõe que o argumento value é do tipo bool e que o valor retornado é int. Se não for esse o caso, ocorrerá uma exceção de runtime.

Você pode escrever conversores de valor de forma que eles sejam mais generalizados e aceitem vários tipos de dados diferentes. Os métodos Convert e ConvertBack podem usar os operadores as ou is com o parâmetro value ou podem chamar GetType nesse parâmetro para determinar seu tipo e, em seguida, fazer algo apropriado. O tipo esperado do valor retornado de cada método é determinado pelo parâmetro targetType. Às vezes, conversores de valor são usados com associações de dados de diferentes tipos de destino. Nesse caso, o conversor de valor pode usar o argumento para executar uma conversão para o targetType tipo correto.

Se a conversão que está sendo executada for diferente para culturas diferentes, use o parâmetro culture para essa finalidade.

Propriedades do conversor de vinculação

As classes do conversor de valor podem ter propriedades e parâmetros genéricos. O conversor de valor a seguir converte um da origem em um bool objeto do tipo T para o destino:

public class BoolToObjectConverter<T> : IValueConverter
{
    public T TrueObject { get; set; }
    public T FalseObject { get; set; }

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

O exemplo a seguir demonstra como esse conversor pode ser usado para exibir o valor de um Switch modo de exibição. Embora seja comum instanciar conversores de valor como recursos em um dicionário de recursos, este exemplo demonstra uma alternativa. Aqui, cada conversor de valor é instanciado entre Binding.Converter marcas de elemento de propriedade. O x:TypeArguments indica o argumento genérico e TrueObject e FalseObject são configurados como objetos desse tipo:

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

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

    <StackLayout Padding="10, 0">
        <StackLayout Orientation="Horizontal"
                     VerticalOptions="Center">
            <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="Center">
            <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="Center">
            <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>

Neste exemplo, no último dos três Switch pares e pares, o argumento genérico é definido como um Style, e objetos inteiros Style são fornecidos para os valores de TrueObject e Label FalseObject. Eles substituem o estilo implícito de Label definido no dicionário de recursos, portanto, as propriedades do estilo são atribuídas explicitamente ao Label. Ativar/desativar o Switch faz com que o Label correspondente reflita a alteração:

Switch indicators.

Observação

Também é possível usar gatilhos para implementar alterações na interface do usuário com base em outras exibições. Para obter mais informações, confira Gatilhos.

Parâmetros do conversor de ligação

A classe Binding define uma propriedade ConverterParameter e a extensão de marcação Binding também define uma propriedade ConverterParameter. Se essa propriedade for definida, o valor será passado para os métodos Convert e ConvertBack como o argumento parameter. Mesmo que a instância do conversor de valores seja compartilhada entre várias associações de dados, o ConverterParameter pode ser diferente para executar conversões diferentes.

O uso da ConverterParameter propriedade pode ser demonstrado com um programa de seleção de cores. O exemplo a seguir mostra o RgbColorViewModel, que tem três propriedades do tipo float chamado Red, Greene Blue que ele usa para construir um Color valor:

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

    public event PropertyChangedEventHandler PropertyChanged;

    public float Red
    {
        get { return color.Red; }
        set
        {
            if (color.Red != value)
            {
                Color = new Color(value, color.Green, color.Blue);
            }
        }
    }

    public float Green
    {
        get { return color.Green; }
        set
        {
            if (color.Green != value)
            {
                Color = new Color(color.Red, value, color.Blue);
            }
        }
    }

    public float Blue
    {
        get { return color.Blue; }
        set
        {
            if (color.Blue != value)
            {
                Color = new Color(color.Red, color.Green, value);
            }
        }
    }

    public Color Color
    {
        get { return 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);
            }
        }
    }

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

Os Redvalores de propriedade , Greene podem variar entre 0 e Blue 1. No entanto, talvez você prefira que os componentes sejam exibidos como valores hexadecimais de dois dígitos. Para exibi-los como valores hexadecimais em XAML, eles devem ser multiplicados por 255, convertidos em um inteiro e, em seguida, formatados com uma especificação de "X2" na propriedade StringFormat. A multiplicação por 255 e a conversão para um inteiro podem ser realizadas pelo conversor de valores. Para tornar o conversor de valor tão generalizado quanto possível, o fator de multiplicação pode ser especificado com a propriedade ConverterParameter, o que significa que ele insere os métodos Convert e ConvertBack como o argumento parameter:

public class FloatToIntConverter : IValueConverter
{
    public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
    {
        return (int)Math.Round((float)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 float)
            return (float)parameter;
        else if (parameter is int)
            return (int)parameter;
        else if (parameter is string)
            return float.Parse((string)parameter);

        return 1;
    }
}

Neste exemplo, o Convert método converte de a float para int enquanto multiplicando pelo parameter valor. O ConvertBack método divide o argumento inteiro value por parameter e retorna um float resultado.

O tipo do argumento provavelmente será diferente dependendo se a associação de parameter dados é definida em XAML ou código. Se a propriedade ConverterParameter de Binding estiver definida no código, será provável que ela esteja definida como um valor numérico:

binding.ConverterParameter = 255;

A propriedade ConverterParameter é do tipo Object, portanto, o compilador de C# interpreta o literal 255 como um número inteiro e define a propriedade com esse valor.

No entanto, em XAML, é provável que o ConverterParameter seja definido assim:

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

Enquanto 255 se parece com um número, porque ConverterParameter é do tipo Object, o analisador XAML trata 255 como uma cadeia de caracteres. Por esse motivo, o conversor de valores inclui um método separado GetParameter que manipula casos por parameter serem do tipo float, intou string.

O exemplo XAML a seguir instancia FloatToIntConverter em seu dicionário de recursos:

<ContentPage xmlns="http://schemas.microsoft.com/dotnet/2021/maui"
             xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
             xmlns:local="clr-namespace:DataBindingDemos"
             x:Class="DataBindingDemos.RgbColorSelectorPage"
             Title="RGB Color Selector">
    <ContentPage.BindingContext>
        <local:RgbColorViewModel Color="Gray" />
    </ContentPage.BindingContext>
    <ContentPage.Resources>
        <Style TargetType="Slider">
            <Setter Property="VerticalOptions" Value="Center" />
        </Style>

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

        <local:FloatToIntConverter x:Key="floatToInt" />
    </ContentPage.Resources>

    <StackLayout Margin="20">
        <BoxView Color="{Binding Color}"
                 HeightRequest="100"
                 WidthRequest="100"
                 HorizontalOptions="Center" />
        <StackLayout Margin="10, 0">
            <Label Text="{Binding Name}" />
            <Slider Value="{Binding Red}" />
            <Label Text="{Binding Red,
                                  Converter={StaticResource floatToInt},
                                  ConverterParameter=255,
                                  StringFormat='Red = {0:X2}'}" />
            <Slider Value="{Binding Green}" />
            <Label Text="{Binding Green,
                                  Converter={StaticResource floatToInt},
                                  ConverterParameter=255,
                                  StringFormat='Green = {0:X2}'}" />
            <Slider Value="{Binding Blue}" />
            <Label>
                <Label.Text>
                    <Binding Path="Blue"
                             StringFormat="Blue = {0:X2}"
                             Converter="{StaticResource floatToInt}">
                        <Binding.ConverterParameter>
                            <x:Single>255</x:Single>
                        </Binding.ConverterParameter>
                    </Binding>
                </Label.Text>
            </Label>
        </StackLayout>
    </StackLayout>
</ContentPage>

Os valores das propriedades Red e Green são exibidos com uma extensão de marcação Binding. A Blue propriedade, no entanto, instancia a Binding classe para demonstrar como um valor explícito float pode ser definido como ConverterParameter propriedade:

RGB color selector.