Xamarin Forms - Apresentando o recurso MultiBinding


Hoje vou apresentar o recurso Multi-Binding do Xamarin Forms.

O Multi-binding é um recurso que foi introduzido no Xamarin Forms 4.7 que permite a vinculação de múltiplas fontes a uma única propriedade de destino.

Este recurso nós oferece bastante flexibilidade, pois agora não precisamos criar vários elementos de IU para cada propriedade vinculável, o que melhora o desempenho e torna nosso código mais limpo.

Antes deste recurso para conseguir ligar várias fontes à mesma propriedade, era necessário um esforço extra. Já que o único controle que possui algo semelhante é o Label com a propriedade FormattedText.   

<Label>
    <Label.FormattedText>
        <FormattedString>
            <Span Text="{Binding Nome}"/>
            <Span Text=" "/>
            <Span Text="{Binding Sobrenome}"/>
        </FormattedString>
    </Label.FormattedText>
</Label>

Mas e se precisarmos fazer o mesmo com outro controle que não possui a propriedade FormattedText ?

Para estes casos podemos usar o novo recurso MultiBinding.

Este recurso fornece a capacidade de anexar uma coleção de objetos Binding a uma única propriedade de destino de ligação. Eles são criados com a classe MultiBinding, que avalia todos os seus objetos Binding e retorna um único valor por meio de uma instância de IMultiValueConverter fornecida pelo seu aplicativo. Além disso, a classe MultiBinding reavalia todos os seus objetos Binding quando qualquer um dos dados vinculados for alterado.

A classe MultiBinding define as seguintes propriedades:

A propriedade Bindings é a propriedade de conteúdo da classe MultiBinding e, portanto, não precisa ser definida explicitamente no XAML.

Um MultiBinding deve usar um IMultiValueConverter para produzir um valor para o destino do binding, com base no valor das associações na coleção Bindings.

Além disso a classe MultiBinding também herda as seguintes propriedades da classe BindingBase:

Usando o MultiBinding

Em um projeto Xamarin Forms criado usando o template Blank vamos definir no arquivo MainPage.xaml.cs o código a seguir:

public partial class MainPage : ContentPage
 {
        public string Nome { get; set; } = "Jose Carlos";
        public string Sobrenome { get; set; } = "Macoratti";
        public MainPage()
        {
            InitializeComponent();
            BindingContext = this;
        }
 }

A seguir no arquivo MainPage.xaml vamos usar o MultiBinding com uma Label e com um Button:

<?xml version="1.0" encoding="utf-8" ?>
<ContentPage xmlns="http://xamarin.com/schemas/2014/forms"
             xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
             x:Class="XF_MultiBinding.MainPage">

    <StackLayout>
         <Label>
            <Label.Text>
                <MultiBinding StringFormat="{}{0} {1}">
                    <Binding Path="Nome"/>
                    <Binding Path="Sobrenome" />
                </MultiBinding>

            </Label.Text>
        </Label>

        <Button>
            <Button.Text>
                <MultiBinding
StringFormat="{}{0} {1}">
                    <Binding Path="Nome"/>
                    <Binding Path="Sobrenome" />
                </MultiBinding>
            </Button.Text>

        </Button>
    </StackLayout>

</ContentPage>

Note que no código XAML devemos adicionar o MultiBinding a qualquer propriedade que desejamos usar, e a seguir, especificar o formato usando StringFormat adicionando um {} para escapar da sequência, e a seguir adicionar todas as propriedades que deseja usando o {[sequenceNumber]} para cada propriedade.

O resultado é o seguinte:

Observe que para cada Binding podemos especificar um Converter, ConverterParameter, FallbackValue, etc.

Vamos a seguir mostrar um exemplo usando este recurso onde vamos criar um conversor de tipo que para o nosso contexto vai usar a interface IMultiValueConverter.

Exemplo prático usando MultiBinding

Neste exemplo vamos criar uma página de login onde vamos usar o nome e sobrenome do usuário para login. Vamos criar um conversor usando a interface IMultiValueConverter.

A seguir vamos adicionar o conversor á propriedade MultiBinding de forma a converter a lista de bindings para o valor que desejamos exibir.

Assim no projeto Xamarin Forms vamos criar a pasta ViewModels onde vamos criar a classe MainViewModel:

using Xamarin.Forms;
namespace XF_MultiBinding.ViewModels
{
    public class MainViewModel : BindableObject
    {
        private string _nome;
        private string _sobrenome;
        public string Nome
        {
            get { return _nome; }
            set
            {
                _nome = value;
                OnPropertyChanged();
            }
        }
        public string Sobrenome
        {
            get { return _sobrenome; }
            set
            {
                _sobrenome = value;
                OnPropertyChanged();
            }
        }
    }
}

A classe MainViewModel define duas propriedades Nome e Sobrenome e herda de BindableObject que fornece um mecanismo para propagar as alterações feitas aos dados em um objeto para outro, habilitando validação, coerção de tipo e um sistema de evento.

A seguir crie a pasta Converters no projeto e nesta pasta crie a classe NomeConverter que implementa a interface IMultiValueConverter:

using System;
using System.Globalization;
using Xamarin.Forms;
namespace XF_MultiBinding.Converters
{
    internal class NomeConverter : IMultiValueConverter
    {
        public object Convert(object[] values, Type targetType, object parameter, CultureInfo culture)
        {
            return $"{values[0]} {values[1]}";
        }
        public object[] ConvertBack(object value, Type[] targetTypes, object parameter, CultureInfo culture)
        {
            return null;
        }
    }
}

A seguir no arquivo MainPage.xaml inclua o código a seguir:

<?xml version="1.0" encoding="utf-8" ?>
<ContentPage xmlns="http://xamarin.com/schemas/2014/forms"
             xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
             xmlns:viewmodels="clr-namespace:XF_MultiBinding.ViewModels"
             xmlns:converters="clr-namespace:XF_MultiBinding.Converters"
             x:Class="XF_MultiBinding.MainPage">
    <ContentPage.Resources>
        <ResourceDictionary>
            <converters:NomeConverter x:Key="NomeConverter" />
        </ResourceDictionary>
    </ContentPage.Resources>    
    <ContentPage.BindingContext>
        <viewmodels:MainViewModel />
    </ContentPage.BindingContext>
    <ContentPage.Content>        
        <StackLayout Padding="12">
            <Label Text="Preencha o formulário:" TextColor="DarkBlue" 
                   HorizontalOptions="Start" FontSize="Medium" />
            <Entry Placeholder="Nome"
                   Text="{Binding Nome, Mode=TwoWay}"/>

            <Entry Placeholder="Sobrenome"
                   Text="{Binding Sobrenome, Mode=TwoWay}"/>

            <BoxView HorizontalOptions="FillAndExpand" VerticalOptions="Center" 
                     HeightRequest="1" />
            <Label Text="Nome completo" TextColor="DarkBlue" HorizontalOptions="Start" 
                   FontSize="Medium" />
            <Label>
                <Label.Text>
                    <MultiBinding Converter="{StaticResource NomeConverter}">
                        <Binding Path="Nome"/>
                        <Binding Path="Sobrenome"/>
                    </MultiBinding>
                </Label.Text>
            </Label>

            <BoxView HorizontalOptions="FillAndExpand" VerticalOptions="Center" 
                     HeightRequest="1" />
            <StackLayout VerticalOptions="EndAndExpand" Margin="0,0,0,10">
                <Button Text="Registrar" HeightRequest="50" BackgroundColor="DarkBlue" 
                        TextColor="White" CornerRadius="5" />

                <Label Text="Esqueceu a senha ?" TextColor="DarkBlue" 
                       HorizontalOptions="Center" FontSize="Medium" />

                <StackLayout Orientation="Horizontal" HorizontalOptions="FillAndExpand">
                    <BoxView BackgroundColor="#555" HorizontalOptions="FillAndExpand" 
                             VerticalOptions="Center" HeightRequest="1" />
                    <Label Text="OU" TextColor="#555" HorizontalOptions="Center"  
                           VerticalOptions="Center" FontSize="Medium" />
                    <BoxView BackgroundColor="#555" HorizontalOptions="FillAndExpand" 
                             VerticalOptions="Center" HeightRequest="1"  />
                </StackLayout>

                <Button Text="Log In" HeightRequest="50" BackgroundColor="DarkBlue" 
                        TextColor="White" CornerRadius="5" />

            </StackLayout>
        </StackLayout>

    </ContentPage.Content>
</ContentPage>

Destaques do código :

1 - Criamos o recurso usando o conversor NomeConverter :

  <ContentPage.Resources>
        <ResourceDictionary>
            <converters:NomeConverter x:Key="NomeConverter" />
        </ResourceDictionary>
    </ContentPage.Resources>    

2 - Definimos a vinculação com a MainViewModel :

<ContentPage.BindingContext>
        <viewmodels:MainViewModel />
 </ContentPage.BindingContext>

3 - Incluímos o conversor NomeConverter na propriedade Multibinding da Label que vai apresentar o resultado para exibir o nome completo:

 <Label>
     <Label.Text>
        <MultiBinding Converter="{StaticResource NomeConverter}">
            <Binding Path="Nome"/>
             <Binding Path="Sobrenome"/>
        </MultiBinding>
     </Label.Text>
 </Label>

O resultado da execução usando emulador Genymotion é o seguinte:

Este é um exemplo básico de utilização do recurso MultiBinding onde temos diversas outras possibilidades de aplicação que iremos mostrar em outros artigos sobre o assunto.

Pegue o projeto aqui:  XF_MultiBinding.zip  (sem as referências)

"Eu rogo por eles; não rogo pelo mundo, mas por aqueles que me deste, porque são teus.
E todas as minhas coisas são tuas, e as tuas coisas são minhas; e neles sou glorificado."
João 17:9,10

Referências:


José Carlos Macoratti