WPF - Data Binding - Revisando técnicas úteis


Neste artigo vou apresentar um resumo sobre o algumas técnicas úteis usando o databinding no WPF em cenários específicos de forma a refrescar sua memória e lhe fornecer subsídios para que você possa trabalhar com os controles WPF em cenários onde a vinculação de dados é necessária.

Todos os exemplos foram feitos usando o Visual Basic 2010 Express Edition.

Selecione o menu File -> New Project e a seguir escolha o modelo WPF_Application com o nome DataBinding_Cenarios;

Os componentes do DataBinding

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

1- Binding com uma fonte relativa

A propriedade de vinculação RelativeSource permite que você especifique um objeto de origem através de sua relação com o controle de destino.

No código de exemplo a seguir temos uma vinculação da propriedade BackGround de um TextBox com sua propriedade Text. Quando você digita o nome de uma cor no TextBox, o controle usa esta cor para definir a sua cor de fundo.

<Window x:Class="MainWindow"
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
    Title="MainWindow" Height="350" Width="525">
    <Grid>
        <TextBox Margin="10" Height="30" VerticalAlignment="Top"
                Background="{Binding RelativeSource={RelativeSource Self}, Path=Text}"/>
    </Grid>
</Window>

Uma fonte relativa pode também ser um:

2- Binding com classes no code-behind

Suponha que um programa chamado ListaPessoa defina uma classe Pessoa que possui as propriedades Nome e SobreNome. (Note que a classe deve ter um construtor vazio para que o XAML possa criar as instâncias).

Para definir a classe Pessoa selecione o menu Project -> Add Class e informe o nome Pessoa.vb para o arquivo da classe. O código da classe Pessoa é visto a seguir:

Public Class Pessoa
   
Private _nome As String
    Private _sobrenome As String


    Public Property Nome() As String
        Get
            Return _nome
        End Get
        Set(ByVal Value As String)
            _nome = Value
        End Set
    End Property

    Public Property SobreNome() As String
        Get
            Return _sobrenome
        End Get
        Set(ByVal Value As String)
            _sobrenome = Value
        End Set
    End Property
End Class

Obs: Essa classe será usada nos demais exemplos quando necessário.

A seguinte declaração de namespace no interior do elemento Window permite que o código XAML refere-se a classe Pessoa e outras classes definidas no programa.

xmlns:local="clr-namespace:DataBinding_Cenarios"

Agora podemos definir na declaração XAML o objeto Pessoa :

<Window.Resources>
     <local:Pessoa x:Key="porAuthor" Nome="Jose Carlos" SobreNome="Macoratti"/>
</Window.Resources>

A seguir o código XAML a seguir realiza a vinculação com as propriedades do Objeto :

<Label Content="{Binding Source={StaticResource porAuthor}, Path=Nome}"/>
<Label Content="{Binding Source={StaticResource porAuthor}, Path=SobreNome}"/>

O código completo pode ser visto abaixo:

<Window x:Class="MainWindow"
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
    xmlns:local="clr-namespace:DataBinding_Cenarios"
    Title="MainWindow" Height="350" Width="525">
    <Window.Resources>
        <local:Pessoa x:Key="porAuthor" Nome="Jose Carlos" SobreNome="Macoratti"/>
    </Window.Resources>
    <Grid>
        <StackPanel>
          <Label Content="{Binding Source={StaticResource porAuthor}, Path=Nome}"/>
          <Label Content="{Binding Source={StaticResource porAuthor}, Path=SobreNome}"/>
        </StackPanel>
    </Grid>
</Window>

Você pode exibir o nome completo sobre-escrevendo o método ToString() na classe Pessoa.

 Public Overrides Function ToString() As String
        Return Nome + " " + SobreNome
    End Function

Agora se o código XAML refere-se ao objeto sem um parâmetro Path, a vinculação retorna o valor padrão do objeto, o qual é a string retornada pelo método ToString.

<Label Content="{Binding Source={StaticResource porAuthor}}" />

Abaixo vemos o código completo e o resultado:


<Window x:Class="MainWindow"
Name="MacWindow"
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
    xmlns:local="clr-namespace:DataBinding_Cenarios"
    Title="MainWindow" Height="350" Width="525">
    <Window.Resources>
        <local:Pessoa x:Key="porAuthor" Nome="Jose Carlos" SobreNome="Macoratti"/>
    </Window.Resources>
    <Grid>
        <StackPanel>
          <Label Content="{Binding Source={StaticResource porAuthor}, Path=Nome}"/>
          <Label Content="{Binding Source={StaticResource porAuthor}, Path=SobreNome}"/>
          <Label Content="{Binding Source={StaticResource porAuthor}}" />
        </StackPanel>
    </Grid>
</Window>

3- Binding com classes em XAML

Veremos agora um cenário onde o código se vincula a um objeto Pessoa que é criado no code-behind.

Suponha que o programa defina uma classe Pessoa com as propriedades Nome e SobreNome e um método ToString sobrescrito conforme descrito no item anterior.

Agora suponha que você queira criar via code-behind um objeto Pessoa depois realizar a vinculação via código XAML com esse objeto.

Adicione no code-behind o código a seguir para criar o objeto e a propriedade que retorna o objeto semelhante:


Class MainWindow 

     Private m_Pessoa As New Pessoa() With { _
     .Nome = "Jose Carlos", .SobreNome = "Macoratti"}

    Public Property Mac_Pessoa() As Pessoa
        Get
            Return m_Pessoa
        End Get
        Set(ByVal value As Pessoa)
            m_Pessoa = value
        End Set
    End Property


End Class

No código XAML inclua a declaração : xmlns:local="clr-namespace:DataBinding_Cenarios"

Observe que o elemento principal Window também especifica o nome da janela usando um atributo Name ou um x:Name. No nosso caso : Name="MacWindows"

Agora o código XAML pode usar esse nome para se referir ao elemento da janela que está executando o código. Pode então referir-se a propriedade da janela que retorna o objeto Pessoa.

O código a seguir vincula um Label ao objeto retornado pela propriedade Mac_Pessoa da janela Window.

<Window x:Class="MainWindow" Name="MacWindow"
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
    xmlns:local="clr-namespace:DataBinding_Cenarios"
    Title="MainWindow" Height="350" Width="525">
    <Window.Resources>
        <local:Pessoa x:Key="porAuthor" Nome="Jose Carlos" SobreNome="Macoratti"/>
    </Window.Resources>
    <Grid>
        <StackPanel>
            <Label Content="{Binding ElementName=MacWindow, Path=Mac_Pessoa}"/>
            <Label Content="{Binding ElementName=MacWindow, Path=Mac_Pessoa.Nome}"/>
        </StackPanel>
    </Grid> 
</Window>

4- Tratando com coleções de dados

Como os controles de itens como ListBox, ComboBox e TreeView exibem grupos de itens os dados a eles vinculados devem conter grupos de valores.

Veremos a seguir como vincular coleções de dados a esses controles no código XAML e no code-behind.

1- VInculando coleções via XAML

Para vincular uma coleção de dados usando o código XAML defina a seguinte declaração XAML : xmlns:sys=”clr-namespace:System;assembly=mscorlib”

Agora podemos usar um elemento x:Array para criar um array de objetos que usa os tipos de dados do sistema. O código a seguir define um array de string chamado nomes:

<Window.Resources>
<x:Array x:Key="nomes" Type="sys:String">
   <sys:String>Macoratti</sys:String>
   <sys:String>Miriam</sys:String>
   <sys:String>Janice</sys:String>
   <sys:String>Jefferson</sys:String>
   <sys:String>Jessica</sys:String>
</x:Array>
</Window.Resources>

Agora podemos vincular o array a um controle de itens. No exemplo abaixo estamos usando um controle ListBox:

<Window x:Class="MainWindow" Name="MacWindow"
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
    xmlns:local="clr-namespace:DataBinding_Cenarios"
    xmlns:sys="clr-namespace:System;assembly=mscorlib"
    Title="MainWindow" Height="350" Width="525">
    <Window.Resources>
        <x:Array x:Key="nomes" Type="sys:String">
            <sys:String>Macoratti</sys:String>
            <sys:String>Miriam</sys:String>
            <sys:String>Janice</sys:String>
            <sys:String>Jefferson</sys:String>
            <sys:String>Jessica</sys:String>
        </x:Array>

    </Window.Resources>
    <Grid>
        <StackPanel>
           
<ListBox ItemsSource="{StaticResource nomes}"/>
        </StackPanel>
    </Grid>
</Window>

2- VInculando coleções via Code-behind

Veremos agora um cenário onde o código se vincula a uma coleção de objetos Pessoa que é criado no code-behind.

Suponha que o programa defina uma classe Pessoa com as propriedades Nome e SobreNome e um método ToString sobrescrito conforme descrito no item anterior.

Agora suponha que você queira criar via code-behind uma coleção de objetos Pessoa e depois realizar a vinculação via código XAML com um controle ListBox.

No code-behind inclua o código abaixo para criar um array e uma propriedade que retorna o array. Ele define um array de objetos Pessoa e a propriedade Mac_Pessoa que retorna o array.

   Private m_Pessoa As Pessoa() = New Pessoa() {New Pessoa() With { _
                    .Nome = "Jose Carlos", _
                    .SobreNome = "Macoratti" _
                    }, New Pessoa() With { _
                    .Nome = "Jefferson", _
                    .SobreNome = "Andre" _
                    }, New Pessoa() With { _
                    .Nome = "Janice", _
                    .SobreNome = "Rachel" _
                    }, New Pessoa() With { _
                    .Nome = "Miriam", _
                    .SobreNome = "Estela" _
                    }}

    Public Property Mac_Pessoa() As Pessoa()
        Get
            Return m_Pessoa
        End Get
        Set(ByVal value As Pessoa())
            m_Pessoa = value
        End Set
    End Property

No código XAML inclua a declaração : xmlns:local="clr-namespace:DataBinding_Cenarios"

Observe que o elemento principal Window também especifica o nome da janela usando um atributo Name ou um x:Name. No nosso caso : Name="MacWindows"

Agora o código XAML pode usar esse nome para se referir ao elemento da janela que está executando o código. Pode então referir-se a propriedade da janela que retorna o objeto Pessoa.

A seguir temos o código XAML que vincula o ListBox a propriedade Mac_Pessoa que retorna a coleção de objetos Pessoa:

<Window x:Class="MainWindow" Name="MacWindow"
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
    xmlns:local="clr-namespace:DataBinding_Cenarios"
    xmlns:sys="clr-namespace:System;assembly=mscorlib"
    Title="MainWindow" Height="350" Width="525">
    <Grid>
        <StackPanel>
            <ListBox ItemsSource="{Binding ElementName=MacWindow, Path=Mac_Pessoa}"/>
        </StackPanel>
    </Grid>
</Window>

Nota: A vinculação aos demais controles de itens segue o mesmo procedimento. Para ilustrar vemos abaixo a mesma vinculação com um controle ComboBox:

<Window x:Class="MainWindow" Name="MacWindow"
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
    xmlns:local="clr-namespace:DataBinding_Cenarios"
    xmlns:sys="clr-namespace:System;assembly=mscorlib"
    Title="MainWindow" Height="350" Width="525">
    <Grid>
        <StackPanel>
            <ComboBox ItemsSource="{Binding ElementName=MacWindow, Path=Mac_Pessoa}"/>
        </StackPanel>
    </Grid>
</Window>

5 - Usando Templates

Acabamos de mostrar como criar array de itens e realizar a vinculação usando o controle ListBox e ComboBox. Estes controles permitem também que você especifique um DataTemplate que determina como o controle irá exibir cada item. Para fazer isso você tem que realizar 3 tarefas básicas:

1 - Criar um elemento ItemTemplate;
2 - No interior deste elemento coloque o elemento DataTemplate contendo os controles que você quer usar para exibir cada item;
3 - Realize a vinculação com data context do controle desejado;.

No exemplo a seguir vinculamos um ListBox a matriz Mac_Pessoa definida no item anterior onde o DataTemplate faz com que o controle exiba cada item em dois controles TextBlocks separador por um outro controle contendo espaço onde no terceiro controle exibimos o sobrenome em negrito e na cor azul:

<Window x:Class="MainWindow" Name="MacWindow"
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
    xmlns:local="clr-namespace:DataBinding_Cenarios"
    xmlns:sys="clr-namespace:System;assembly=mscorlib"
    Title="MainWindow" Height="350" Width="525">
    <Grid>
        <StackPanel>
            <ListBox ItemsSource="{Binding ElementName=MacWindow, Path=Mac_Pessoa}">
                <ListBox.ItemTemplate>
                    <DataTemplate>
                        <StackPanel Orientation="Horizontal">
                            <TextBlock Text="{Binding Nome}" FontWeight="Normal"/>
                            <TextBlock Text=" "/>
                            <TextBlock Text="{Binding SobreNome}" FontWeight="Bold" ForeGround="Blue"/>
                        </StackPanel>
                    </DataTemplate>
                </ListBox.ItemTemplate>
            </ListBox>
        </StackPanel>
    </Grid>
</Window>

Com isso fizemos uma pequena revisão recordando os pontos básicos do databinding no WPF que pode lhe ajudar no seu dia a dia.

Pegue o projeto completo aqui: DataBinding_Cenarios.zip

Eu sei é apenas WPF, mas eu gosto...

"Sendo pois justificados pela fé, temos paz com Deus, por nosso Senhor Jesus Cristo."(Rom:5-1)

Referências:

José Carlos Macoratti