WPF - Usando a vinculação de dados, conceitos e prática


Há algum tempo atrás publiquei um artigo introdutório sobre o WPF - Windows Presentation Foundation - e depois me calei a respeito. De lá para cá a ferramenta amadureceu e eu fiquei de publicar mais sobre a vinculação de dados com WPF.

Uma aplicação WPF é composta por um conjunto de páginas ou janelas XAML, e o seu respectivo código.

O WPF é uma nova tecnologia com foco na apresentação gráfica permitindo a criação de interfaces visuais mais sofisticadas usando recursos de última geração, uma alternativa ao Windows Forms. (Eu disse alternativa não substituição embora creio que o Windows Forms já deu o que tinha que dar) .

Nota: Para criar interfaces ricas de alta qualidade usando WPF você vai precisar conhecer muito bem XAML e também ser um bom design gráfico. Além disso vai precisar conhecer outras ferramentas como o SilverLight e o Expression Blend da Microsoft para poder estender os recursos oferecidos pelo Visual Studio. Nas apresentações sobre WPF são mostrados exemplos de cubos e paletas de cores mas ninguém usa isso no dia a dia. Na prática se trabalha com aplicações com acesso a dados o que requer também o conhecimento do databinding.

Recursos e objetivos do WPF:( http://msdn.microsoft.com/pt-br/library/cc564903.aspx)

De forma geral um programa WPF é composto por duas partes básicas :

O arquivo XAML , como um bom arquivo XML apresenta as seguintes características:

- Tem um controle raiz que contém todos os outros;
- Esta ligado a uma classe no código via propriedade x:Class do elemento raiz;
- Os elementos são definidos usando tags <> e </>;
- Os elementos podem conter atributos;

Então na prática podemos criar 3 tipos de aplicações com WPF :

Neste artigo eu vou mostrar como criar uma aplicação WPF usando a vinculação de dados onde irei mostrar como acessar e efetuar as tarefas básicas de manutenção de dados (CRUD); as operações para incluir, excluir, alterar, consultar e navegar nos dados. Eu não vou portanto criar um design gráfico elaborado para interface , o que seria um dos motivos para usar o WPF, vou mostrar os conceitos para que você possa efetuar a vinculação de dados usando WPF.

Uma pequena introdução sobre a vinculação de dados (DataBinding)

O DataBinding no WPF fornece uma forma simples e consistente para as aplicações de apresentar e interagir com dados. OS elementos podem ser vinculados aos dados a partir de uma grande variedade de fontes de dados na forma de objetos CLR e XML. A WPF fornece controles (ContentControls) como Button e controles de itens (ItemsControls) como ListBox e ListView que possuem funcionalidades que permitem uma flexível customização de estilo de uma coleção de itens ou de um simples item, além de permitir a ordenação, o filtro e agrupamento de visões.

O DataBinding do WPF possui muitas vantagens sobre o modelo tradicional pois inclui uma grande variedade de propriedades com suporte a vinculação de dados, representação gráfica e uma separação lógica da lógica de negócio da interface.

A vinculação de dados é o processo que estabelece uma conexão entre o aplicativo e lógica do negócio. Se a associação possui configurações corretas e os dados fornecem notificações adequadas, então, quando os dados mudam de valor, os elementos que são associados aos dados refletem as mudanças automaticamente.  

A vinculação de  dados também pode significar que se uma representação externa de dados em um elemento é alterada, então os dados relacionados podem ser automaticamente atualizados para refletir a mudança. Por exemplo, se o usuário edita o valor em um elemento TexBox, o valor do dado relacionado na fonte de dados é automaticamente atualizado para refletir a alteração.

Um uso típico de vinculação de dados é colocar o servidor ou a configuração local dos dados em formulários ou outros controles. No WPF esse conceito é expandido para incluir a vinculação de uma ampla variedade de propriedades para uma variedade de fontes de dados. No WPF, as propriedades de dependência dos elementos podem ser vinculados a objetos (incluindo objetos ou associados com os serviços da Web e propriedades da Web) e aos dados.

Independente de qual elemento você está associando e a natureza de sua fonte de dados, cada associação segue o modelo ilustrado na seguinte figura:

Como ilustrado pela figura acima, a vinculação de dados é essencialmente a ponte entre a ligação de destino e a sua ligação de origem. A figura demonstra os seguintes conceitos da vinculação de dados:

Em geral, cada ligação possui quatro componentes:

  1. Um objeto de vinculação de destino (binding target);
  2. Uma propriedade de destino (target property);
  3. Uma vinculação de origem (binding source);
  4. Um caminho para o valor de vinculação de origem usar;

Por exemplo, se você deseja vincular o conteúdo de um TextBox para a propriedade Name um objeto Funcionário, o objeto de destino é o TextBox  a propriedade de destino é a propriedade Text, o valor a ser usado é Name (Nome) e o objeto de origem é objeto Funcionário.

A propriedade de destino deve ser uma propriedade de dependência. A maioria das propriedades UIElement (a classe base da WPF para construção e representação de elementos) são propriedades de dependência e a maioria das propriedades de dependência , exceto aquelas somente leitura, dão suporte a vinculação de dados por padrão.

Embora não especificado na figura, deve ser observado que o objeto de vinculação de origem não é restrito a ser um objeto CLR. A WPF suporta a associação de dados  no formulário de objetos CLR e XML .

Afinal, na WPF o que é destino e que é fonte ?
 

Na WPF , você usa o data binding para estabelecer a conexão entre as propriedades de dois objetos. Neste relacionamento , um objeto é referenciado como sendo a fonte e o outro como sendo o destino. Em um cenário típico, seu objeto fonte é o objeto que contém os dados e o seu objeto fonte é o controle que pretende exibir os dados.

A direção do fluxo de dados

O fluxo de dados de uma vinculação pode ir a partir de Ligação de destino para a Ligação de origem;Por exemplo, o valor de origem é alterado quando um usuário edita o valor de um TexBox  e/ou a partir de Ligação de origem para o Ligação de destino;Por exemplo, o seu  TextBox obtém e exibe o conteúdo atualizado com as alterações na ligação de origem se a origem de ligação fornece as notificações adequadas.

Você pode querer que sua aplicação permita aos usuários modificar os dados e propagá-los de volta para o objeto fonte; ou você não quer permitir que usuários atualizem a fonte de dados. Você pode controlar isso definindo a propriedade Mode() do seu  objeto vinculado. A seguinte figura ilustra os diferentes tipos de fluxo de dados:

O WPF oferece as seguintes possibilidades de fluxo de dados  que são definidas com o Binding.Mode:

O modo de vinculação TwoWay ou OneWayToSource fica monitorando as alterações na propriedade destino e então as propaga para a origem. Assim você pode editar o texto em um TextBox para alterar o valor a origem correspondente.

Criando uma aplicação WPF com vinculação de dados

Agora vamos a prática, criar uma aplicação WPF desktop que execute em janela Windows Forms com vinculação de dados e recursos de alterar, incluir, excluir , e selecionar e navegar pelos dados.

Podemos usar o Visual Basic 2008 Express Edition ou o Visual Studio 2008 (ambos com service pack1) para criar uma aplicação WPF. Vou usar o Visual Studio 2008 (Você pode baixar a versão trial do VS 2008 aqui ).

Abra o VS 2008 e no menu File escolha New Project e em Project Types selecione Visual Basic -> Windows e na janela Templates marque WPF Application e informe o nome wpf_Dados e clique em OK;

Vamos agora definir uma fonte de dados selecionando o menu Data-> Add New Data Source;

Na janela do assistente selecione DataBase e clique em Next>;

Na próxima janela selecione uma conexão com o banco de dados Northwind.mdf e clique em Next>;

Nota: Se você não possuir a conexão criada clique no botão New Connection... e defina a conexão para o Northwind.mdf

Vamos aceitar e salvar a string de conexão e clicar no botão Next>;

Vamos selecionar a tabela Categories e clicar no botão Finish;

Desta forma já temos o dataset NortwindDataSet criado em nossa aplicação.

Vamos agora definir o leiaute da aplicação WPF.

Na janela Solution Explorer selecione Window1.xaml e tenha o cuidado de habilitar a partir do menu View _> ToolBox e View -> Other Windows -> Document Outline;

De forma que você visualize o seguinte leiaute da aplicação. Note que a janela de propriedades é um pouco diferente da usada em uma aplicação Windows Forms;

Observe que temos duas visões: a do designer e a do arquivo XAML e que por padrão o formulário WPF apresenta a classe Window1 e o componente Grid que define o leiaute.

O que o WPF oferece?

Essencialmente temos os seguintes controles de leiautes disponibilizados:  Canvas,   Stackpanel, Wrappanel, Dockpanel, Grid

Canvas  - Este é um dos controles de layout mais fáceis de se utilizar. Igual aos formulários Windows Forms ou VCL, nele podemos posicionar os controle utilizando as coordenadas X,Y.

StackPanel  - É fácil de ser utilizado, nele os controles são empilhados verticalmente ou horizontalmente. Isso pode ser feito pela propriedade Orientation.

Wrappanel - Distribui os controles que estão contidos nele.

Dockpanel - É muito útil e pode servir como leiaute base para qualquer janela WPF. Através dele podemos construir os mais variados leiautes, posicionando por exemplo um menu ao topo, uma área livre à direita e até mesmo um outro controle de layout à esquerda. É possível aninhar controles de layout. Cada controle que é inserido em um dockpanel pode ser alinhado através da propriedade DockPanel.Dock.

O Dockpanel também oferece uma propriedade muito interessante, a LastChildFill, que faz com que o último controle inserido nele preencha todo o espaço restante. Utilizei essa propriedade para fazer o Scrollbox preencher todo o espaço disponível da janela

Grid - O Grid é semelhante a uma tabela, com ele definimos colunas e linhas, e dentro delas inserimos os controles. (Veremos sua utilização no exemplo)

Para definir o leiaute vamos criar uma linha (Row) e duas colunas(Column). Para fazer isso você apenas posiciona o mouse no Grid onde deseja criar a divisão e clica ajustando o tamanho;

Note que na ToolBox temos os controles WPF que poderemos arrastar e soltar no designer. Note que temos uma quantidade de menor de controles do que nas aplicações Windows Forms;

Vamos então arrastar e soltar oito Button no Designer conforme o leiaute abaixo e definir a sua propriedade Name e Content (A propriedade Content é idêntica a propriedade Text dos controles Windows Forms) conforme a seguir:

  1. Name = btnNovo   ;  Content = Novo
  2. Name = btnExcluir ;  Content = Excluir
  3. Name = btnCancelar   ;  Content = Cancelar
  4. Name = btnSalvar  ;  Content = Salvar
  5. Name = btnPrimeiro   ;  Content = |<
  6. Name = btnAnterior  ;  Content = <
  7. Name = btnProximo   ;  Content = >
  8. Name = btnUltimo   ;  Content = >|

Observe que quando você seleciona um controle surgem setas e indicações de tamanho e altura do mesmo:

As setas servem para alterar o tamanho e também para ancorar o controle. Para isso arraste o mouse sobre a posição da seta e quando o ícone de uma mão aparecer clique na seta para ancorar o controle.

Note também que a representação esta sendo gerada no arquivo XAML.

Vamos incluir dois controles StackPanel em cada uma das colunas criadas no Grid e expandir o controle até o tamanho máximo das colunas;

Em seguida vamos incluir a partir da ToolBox, 3 controles Label no primeiro StackPanel, e 3 controles TextBox no segundo StackPanel e definir a sua propriedade Width como Auto de forma a obter o seguinte resultado:

Agora vamos definir a propriedade Content do controle Label para indicar o nome de cada um dos campos que vamos exibir ou seja: Código, Nome, Descrição.

Em cada um dos controles TextBox vamos efetua as seguintes definições:

Note que você pode acompanhar na janela Document Outline a hierarquia de controles no WPF;

Agora temos que efetuar a vinculação de cada um dos controles TextBox com os dados. Para isso vamos efetuar a definição no arquivo XAML usando notação especial para o binding;

Observe que definimos os nomes dos campos com os quais desejamos vincular os controles e no caso do TextBox txtCategoryID temos a definição do Mode como OneWay pois o campo é somente-leitura.

Text="{Binding Path=nomedocampo, Mode=modofluxodados }"

Agora vamos definir o código para carregar os dados no code-behind. Clique com o botão direito sobre o designer e selecione View Code;

A seguir defina o seguinte código :

Nota: O evento para carga do arquivo XAML é o evento Loaded.

O código relativo a definição do TableAdapter e TableAdapterManager nós já conhecemos.

Os TableAdapters fornecem a comunicação entre sua aplicação e uma fonte de dados, ou seja, um TableAdapter efetua a conexão com uma fonte de dados, executa instruções SQL ou procedimentos armazenados e retorna um novo conjunto de dados (DataTable) preenchido com os dados retornados ou preenche um DataTable já existente com os dados retornados. Os TableAdapters também são usados para enviar dados atualizados de sua aplicação de volta a fonte de dados.

Você pode pensar em um TableAdapter como sendo um DataAdapter com um objeto connection embutido e com a característica de conter múltiplas consultas. Cada consulta incluída a um TableAdapter é exposta como um método público que é chamado como qualquer outro método ou função em um objeto.

Além das funcionalidades padrão de um DataAdapter , os TableAdapters fornecem métodos tipados adicionais que encapsulam consultas que compartilham um esquema comum com o DataTable tipado associado; ou seja, você pode ter tantas consultas quando você desejar em um TableAdapter contanto que elas retornam dados que se conformam com o mesmo esquema.

O TableAdapterManager é um novo componente presente no VS 2008 que atua sobre datasets tipados e TableAdapters e permite a atualização de dados em tabelas relacionadas(Atualização hierárquica) de uma forma mais fácil. Na versão anterior era necessário a inclusão de código para realizar esta tarefa corretamente.

A atualização hierárquica é o processo de atualização dos dados modificados em tabelas relacionadas em um dataset tipado pela utilização do novo componente TableAdapterManager.

O TableAdapterManager usa o relacionamento da chave primária entre as tabelas para determinar corretamente a ordem na qual deve enviar os Inserts, Updates e Deletes do DataSet para o banco de dados sem violar as restrições da chave-primária (integridade referencial) no banco de dados.

Agora note a propriedade DataContext  a qual atribuímos a nossa fonte de dados.

DataContext é uma propriedade existente em qualquer FrameWorkElement e que permite que os controles herdem, de seu parent, a fonte de dados.

Um DataContext pode ser ligado diretamente a um objeto CLR ou a um DataSource Provider. No nosso exemplo temos uma Window e esse Window possui alguns TextBox inseridos nele. Ao se definir que o o DataContext do Window é por exemplo a classe Categories, não é mais necessário indicar o DataContext dos demais TextBox. Eles, por estarem dentro da hierarquia do Window, entendem que devem herdar o DataContext de Window, caso outro não seja especificado.

Existem muitas formas de especificar a sua fonte de vinculação de dados, ou seja, definir qual a origem dos seus dados.

A forma mais simples é instanciar o objeto origem como um recurso XAML e então definir a propriedade Source da vinculação para o recurso.

Usar a propriedade Source  é a forma mais simples. Porém, se múltiplas propriedades estão vinculadas a mesma origem, considera utilizar a propriedade DataContext..

 

A propriedade DataContext fornece uma forma conveniente de estabelecer um escopo de dados quando você tem muitos controles e você quer vincular todos a mesma origem.

Dessa forma já podemos executar a aplicação que veremos o databinding em ação exibindo os dados da tabela Categorias no  TextBox;

Agora temos que permitir a navegação pelos dados. (Lembra do BindingNavigator ? )

No WPF podemos fazer a mesma coisa usando a classe CollectionView.

A classe CollectionView representa uma visão para agrupamento, ordenação, filtragem e navegação de uma coleção de dados.

Para criar uma visão para uma coleção que implementa IEnumeralbe, crie um objeto CollectionViewSource, inclua a sua coleção a propriedade Source e obtenha a visão da coleção através da propriedade View.

Você pode pensar na CollectionView como uma camada na parte superior de uma vinculação de origem que permite que você navegue e exiba a coleção com base na classificação, filtro e agrupamento de consultas, tudo sem precisar manipular a coleção de origem subjacentes. Se a coleção de origem implementa a interface INotifyCollectionChanged, as alterações que disparam o evento CollectionChanged são propagadas para os visões.
 

Nas aplicações WPF todas as coleções tem uma visão de coleção padrão associada. Em vez trabalhar diretamente com a coleção, o mecanismo de ligação sempre acessa a coleção através de exibição associada. Para obter o modo de exibição padrão use o método CollectionViewSource.GetDefaultView.

Vamos então implementar esta funcionalidade em nosso código:

Agora só temos que implementar nos eventos dos controles Button para navegação usando os métodos para navegação oferecidos pela CollectionView.

Clicando duas vezes no botão de navegação podemos implementar o código para navegação com a ajuda do intellissense:

A seguir o código para cada um dos botões:

Private Sub btnPrimeiro_Click(ByVal sender As System.Object, ByVal e As System.Windows.RoutedEventArgs) Handles btnPrimeiro.Click

        Me.view.MoveCurrentToFirst()

End Sub
 

Private Sub btnAnterior_Click(ByVal sender As System.Object, ByVal e As System.Windows.RoutedEventArgs) Handles btnAnterior.Click

     If view.CurrentPosition > 0 Then

           Me.view.MoveCurrentToPrevious()

      End If

End Sub


Private
Sub btnProximo_Click(ByVal sender As System.Object, ByVal e As System.Windows.RoutedEventArgs) Handles
btnProximo.Click

      If Me.view.CurrentPosition < Me.view.Count - 1 Then

              Me.view.MoveCurrentToNext()

     End If

End Sub


Private
Sub btnUltimo_Click(ByVal sender As System.Object, ByVal e As System.Windows.RoutedEventArgs) Handles
btnUltimo.Click

     Me.view.MoveCurrentToLast()

End Sub

 

Note que a lógica é a mesma usada para navegação com o BindingNavigator pois temos métodos parecidos, vejamos os principais:

Agora já podemos navegar usando os botões.

Na continuação deste artigo vou mostrar a implementação do código para as operações de Incluir, alterar e excluir dados.

Acompanhe a continuação em : WPF - Usando a vinculação de dados, conceitos e prática II

Eu sei é apenas VB .NET mas eu gosto...

Referencias:

José Carlos Macoratti