Xamarin.Forms - Apresentando o Data Binding


  Hoje veremos como criar uma aplicação multiplataforma onde foco será como definir a interface com o usuário na aplicação Xamarin Forms usando o DataBinding.

Um dos maiores benefícios do Xamarin.Forms é que ele permite desenvolver aplicativos móveis para as plataformas Android, iOS e Windows Phone. Assim, podemos usar o Xamarin integrado ao VS 2015 e a linguagem C# para criar aplicações para várias plataformas.

O principal foco do Xamarin.Forms é o desenvolvimento de UIs móveis onde telas e seus controles, suas imagens, animações e interações do usuário são executadas nativamente. Um dos principais recursos usados para a criação de UIs móveis é o Data Binding o vinculação de dados.

As vinculações de dados, ou data bindings, permitem que as propriedades de dois objetos sejam ligadas de modo que uma alteração numa provoca uma alteração na outra.

Este recurso conecta as propriedades de dois objetos chamados source (origem) e target (destino).

O Data Binding sempre têm uma origem(source) e um destino(target)

A origem é um propriedade de um objeto, geralmente uma que se altera dinamicamente em tempo de execução. Quando Essa propriedade for alterada, o data binding atualiza automaticamente o destino, que é uma propriedade de um outro objeto.

Os componentes do Data Binding :

- Target(Destino) - É o objeto que vai usar o resultado da vinculação (binding);
- Target Property(Propriedade de destino) - A propriedade de destino do objeto que irá utilizar o resultado;
- Source (Origem) - O objeto que fornece um valor para o objeto de destino usar;
- Path(Caminho) - Um caminho que localiza o valor dentro do objeto de origem;

A definição do data binding é um processo feito em duas etapas :

  1. A propriedade BindingContext do objeto target precisa ser definida para o objeto source;
  2. Uma vinculação precisa ser estabelecida entre o target e o source. Via XAML isso é feito usando a tag Binding. (Via código o método SetBinding precisa ser chamado no objeto target para ligar a propriedade daquele objeto à propriedade do objeto source;)

A propriedade target deve ser uma propriedade ligável, o que significa que o objeto de destino deve derivar de BindableObject.

Na marcação, esses mesmos dois passos também são necessários, exceto que a extensão de marcação Binding toma o lugar da chamada SetBinding e da classe Binding. No entanto, não existe uma técnica simples para definir o BindingContext do objeto target.

Às vezes isto é definido a partir do arquivo code-behind, às vezes usando um StaticResource ou uma extensão de marcação estática (x:Static), e às vezes como o conteúdo da tag propriedade-elemento BindingContext.

As vinculações (bindings) são usadas na maioria das vezes para ligar o visual de um programa com um modelo de dados subjacente, geralmente em uma implementação do padrão MVVM (Model-View-ViewModel).

Na maioria das vezes o databinding é definido via código XAML mas podemos também fazer isso via código. Neste caso uma maneira de definir o databinding é:

- Definir a propriedade BindingContext do objeto de destino(target) para se referir ao objeto de origem (source)

- Chamar SetBinding no objeto de destino (target) para especificar ambas as propriedades de origem e destino.

Exemplo:

  Label label = new Label
  {
     Text = "Macoratti .net",
     FontSize = Device.GetNamedSize(NamedSize.Large, typeof(Label)),
     VerticalOptions = LayoutOptions.CenterAndExpand,
     HorizontalOptions = LayoutOptions.Center
  };

  Slider slider = new Slider
  {
     VerticalOptions = LayoutOptions.CenterAndExpand
  };

  // Define o BindingContext: destino é a Label, origem é o Slider
  label.BindingContext = slider;
  // Vincula a propriedae: target é Opacity; source é Value.
  label.SetBinding(Label.OpacityProperty, "Value" );

  Padding = new Thickness(10, 0);

  Content = new StackLayout
  {
       Children = { label, slider }
  };

Vejamos a seguir vou mostrar dois exemplos de uso do recurso Data Binding usando XAML.

Requisitos necessários :

Criando a solução Xamarin.Forms no Visual Studio 2015

Abra o Visual Studio Community 2015 e clique em New Project;

Selecione Visual C#, o template Cross Plataform e a seguir Blank App (Xamarin.Forms Portable);

NotaA opção Portable (Portable Class Library - PCL ) - Inclui todo o código comum em uma biblioteca de vínculo dinâmico (DLL) que pode então ser referenciada a partir de outros projetos;

Informe o nome XamarinFormsDataBinding e clique no botão OK;

Ao clicar no botão OK, será criada uma solução contendo 4 projetos. (Dependendo do seu ambiente pode haver alguma variação nos projetos.)

  • 1 Projeto Comum contendo a maior parte do código da aplicação : (Este é o projeto Portable cuja compilação pode ser compartilhada com outras plataformas)
  • 1 Projeto para plataforma Android
  • 1 Projeto para plataforma iOS
  • 1 Projeto para Windows 8.1 ou para Windows 10
  • 1 Projeto para plataforma Windows Phone

Nota: Durante o processo de criação dos projetos, ao criar o projeto para o iOS será apresentada a janela : Xamarin Mac Agent Instructions , com instruções para identificar e localizar o Mac (a própria máquina ou em uma rede local)Se você não tiver um Mac (como eu) clique no botão OK e a seguir clique no botão Close. Com isso não poderemos compilar nem testar projetos iOS no Visual Studio.

Podemos também usar a janela de propriedades da solução marcar a opção - Multiple startup projects - e selecionar quais o projetos vamos fazer o deploy e executar :

Como não tenho um Mac (ainda) e a compilação e testes da aplicação ficará prejudicada vou remover o projeto iOS da solução criada. Para isso clique com o botão sobre o projeto e a seguir clique em Remove. (Também vou remover o projeto Windows Phone).

Irei focar no projeto comum que possui a classe App.cs e que irá conter o código compartilhado e que vamos usar neste artigo. Assim o código poderá ser executado em qualquer plataforma (iOs, Android, Windows Phone).

1 -  Data Binding entre duas Views

Incluindo uma nova página XAML no projeto : Pagina1.axml

No menu Project clique em Add New Item;

Selecione o template Cross Plataform -> Forms Xaml Page e informe o nome Pagina1.xaml e clique em Add;

Após incluir a nova página XAML no projeto abrindo o arquivo Pagina1.xaml  veremos o seguinte código:

<?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="XamarinFormsDataBinding.Pagina2">

  <StackLayout>
    <Label Text="ROTAÇÃO"
           BindingContext="{x:Reference Name=slider}"
           Rotation="{Binding Path=Value}"
           FontAttributes="Bold"
           FontSize="Large"
           HorizontalOptions="Center"
           VerticalOptions="CenterAndExpand" />

    <Slider x:Name="slider"
            Maximum="360"
            VerticalOptions="CenterAndExpand" />

    <Label BindingContext="{x:Reference slider}"
          Text="{Binding Value,StringFormat='O ângulo é {0:F0} graus'}"
          FontAttributes="Bold"
          FontSize="Large"
          HorizontalOptions="Center"
          VerticalOptions="CenterAndExpand" />
  </StackLayout>

</ContentPage>

Vamos entender o código :

O controle Slider contém um atributo x:Name que é referenciado pelas duas views Label usando a extensão de marcação x:Reference.

Nota: Uma extensão de marcação, denotada pelo par de chaves ({})  é uma maneira especial de estender a funcionalidade do XAML.

A extensão de vinculação x:Reference define uma propriedade chamada Name para atribuir o nome do elemento referenciado, neste caso o Slider.

Porém, a classe ReferenceExtension que define a marcação de extensão x:Reference também define um atributo ContentProperty para Name o que significa que ele não é explicitamente requerido. Por esse motivo a segunda referência não inclui o atributo "Name="

1-   BindingContext="{x:Reference Name=slider}"
2-   BindingContext="{x:Reference slider}"

A extensão de marcação Binding pode ter muitas propriedades, e a ContentProperty para Binding é Path que pode ser omitida somente se ela for o primeiro item em Binding.

Assim a extensão de marcação Binding fornece um valor de propriedade vinculada a dados especial que é adiada até a execução. Isto significa que, no momento em que você está escrevendo seu código, e durante tempo de compilação , os valores para estas propriedades específicas não são conhecidos. Supõe-se que durante a execução um valor será atribuído às propriedades de alguma forma, e , essa forma é através de um BindingContext no Xamarin.Forms.

Assim o primeiro exemplo usa "Path=" e o segundo o omite:

1- Rotation="{Binding Path=Value}"
2- Text="{Binding Value, StringFormat='O ângulo é {0:F0} graus'}"

As propriedades podem ser definidas em uma linha ou múltiplas linhas.

Observe que no segundo Binding usamos StringFormat. Como no Xamarin.Forms, as vinculações não realizam qualquer tipo de conversão implícita, se você precisar exibir um objeto não string como uma string, você tem que fornecer um conversor ou usar StringFormat.

Executando o projeto e alterando o valor do Slider iremos obter o seguinte resultado:

2 - TwoWay Data Binding

Incluindo uma nova página XAML no projeto - Pagina2.axml

No menu Project clique em Add New Item;

Selecione o template Cross Plataform -> Forms Xaml Page e informe o nome Pagina2.xaml e clique em Add;

Após incluir a nova página XAML no projeto abrindo o arquivo Pagina2.xaml  veremos o seguinte código:

<?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="XamarinFormsDataBinding.Pagina1">

  <StackLayout Padding="0,20,0,0" BackgroundColor="#531380">   

    <Label Text="Data Binding - Demo" FontAttributes="Bold" HorizontalOptions="Center" />
    <Label Text="Nome :" />
    <Entry Text="{Binding Nome, Mode=TwoWay}" />
    <Label Text="Sobrenome : " />
    <Entry Text="{Binding Sobrenome, Mode=TwoWay}" />
    <StackLayout Padding="0,20,0,0" Orientation="Horizontal">
      <Label Text="Seu nome é :" />
      <Label Text="{Binding Nome}" />
    </StackLayout>
    <StackLayout Orientation="Horizontal">
      <Label Text="Seu sobrenome é :" />
      <Label Text="{Binding Sobrenome}" />
    </StackLayout>
  </StackLayout>
 
</ContentPage>

A seguir vamos definir a propriedade BindingContent da classe Pagina1, o objeto target, no arquivo code-behind Pagina1.xaml.cs para a classe DetailsViewModel, o objeto source:

using Xamarin.Forms;

namespace XamarinFormsDataBinding
{
    public partial class Pagina1 : ContentPage
    {
        public Pagina1()
        {
            InitializeComponent();
            BindingContext = new DetailsViewModel();
        }
    }
}

A propriedade BindingContext de cada objeto target pode ser definida individualmente e, é um propriedade especial que é herdada por todos os seus filhos. O BindingContext é uma propriedade especial encontrada na classe BindableObject que permite que tais vinculação de dados possam ocorrer.

Essa classe é usada como uma classe base para todas as classes no Xamarin.Forms que representam elementos visuais . Isto significa que todas estas classes derivadas tem acesso à propriedade BindingContext . Em algum momento durante a vida de sua página XAML (se estiver usando a ligação de dados), você terá a necessidade de procurar por dados, e, a maneira mais comum de fazer isso é atribuir à propriedade BindingContext um valor durante a sua construção.

Além disso, quando a BindingContext na ContentPage é definida para DetailsViewModel todos os filhos de ContentPage possuem a mesma BindingContext e pode vincular às propriedades públicas de DetailsViewModel.

A propriedade Mode é usada para especificar a direção na qual o valor das mudanças da propriedade vai se propagar:

  • OneWay - propaga as alterações a partir de source para target;
  • TwoWay - propaga as mudanças em ambas direções, assegurando que os objetos source e target sempre estejam sincronizados;
  • OneWayToSource - propaga as alterações a partir de target para source e é usado para propriedades somente-leitura;

Implementando a notificação da mudança de propriedade : INotifyPropertyChanged

Por padrão o objeto target somente recebe o valor do objeto source quando o binding é criado. Para manter a UI sincronizada com a fonte de dados, precisamos uma maneira de notificar o objeto target quando o objeto source for alterado.

Este mecanismo é fornecido pela interface INotifyPropertyChanged que deve ser implementada pela classe DetailsViewModel afim de fornecer as notificações para qualquer controle vinculado quando a o valor da propriedade vinculada mudar.

Nota: A interface INotifyPropertyChanged é uma interface simples e poderosa que facilita a atualização automática dos elementos da UI a partir da BindingContext e vice-versa. Ela contém apenas uma manipulador de eventos para PropertyChanged.

A seguir temos a implementação da interface INotifyPropertyChanged :

using System.ComponentModel;

namespace XamarinFormsDataBinding
{
    public class DetailsViewModel : INotifyPropertyChanged
    {
        string nome, sobrenome;

        public string Nome
        {
            get
            {
                return nome;
            }
            set
            {
                if (nome != value)
                {
                    nome = value;
                    OnPropertyChanged("Nome");
                }
            }
        }

        public string Sobrenome
        {
            get
            {
                return sobrenome;
            }
            set
            {
                if (sobrenome != value)
                {
                    sobrenome = value;
                    OnPropertyChanged("Sobrenome");
                }
            }
        }

        public event PropertyChangedEventHandler PropertyChanged;

        protected virtual void OnPropertyChanged(string propertyName)
        {
            var changed = PropertyChanged;
            if (changed != null)
            {
                PropertyChanged(this, new PropertyChangedEventArgs(propertyName));
            }
        }
    }
}

A classe dispara o evento PropertyChanged sempre que as propriedades Nome e Sobrenome mudarem. Assim no exemplo, ao informar o nome e o sobrenome eles são exibidos simultaneamente nas views Labels conforme mostra a figura abaixo do projeto em execução:

Dessa forma, o Data Binding é usado para sincronizar a UI com sua fonte de dados e simplificar como o Xamarin.Forms exibe e interage com dados.

Desde que o objeto de origem (source) implemente a interface INotifyPropertyChanged, as mudanças no objeto origem são enviadas automaticamente para o objeto de destino (target) pela estrutura de ligação, e as mudanças no objeto de destino podem ser opcionalmente enviadas para o objeto de origem.

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

Porque pela graça sois salvos, por meio da fé; e isto não vem de vós, é dom de Deus.
Não vem das obras, para que ninguém se glorie;
Efésios 2:8,9

Referências:


José Carlos Macoratti