Introdução
Quando falamos em WPF, pensamos logo em interfaces ricas de
usuário. Isto faz com que, muitas vezes, o WPF não seja considerado como uma
plataforma para desenvolvimento de aplicações LOB (Line-of-Business –
aplicações de negócios). A frase comum é “o WPF é bom para a interface de
usuário, mas para criar uma aplicação de negócios o melhor é usar Windows
Forms”. Este artigo irá mostrar que, além da interface de usuário, o WPF
presta-se muito bem ao desenvolvimento de aplicações de negócios.
Iremos criar um banco de dados Sql Server com duas tabelas,
Clientes e Contatos. Cada cliente poderá ter diversos contatos. Para fazer o
mapeamento objeto-relacional com o banco de dados, usaremos o LINQ to SQL. Também
criaremos um UserControl para editar os dados dos contatos do cliente, que
ficarão em um TabControl.
Criando o banco de dados
Iremos começar nossa aplicação criando o banco de dados e as
tabelas, no Visual Studio. Na aba Server Explorer, em Data Connections,
adicione um novo banco de dados e dê o nome de Clientes. Neste banco,
adicione uma tabela chamada Clientes, como mostra a Figura 1.

Figura 1 – Tabela de Clientes
Em seguida, crie uma nova tabela e chame-a de Contatos,
como mostra a Figura 2.

Figura 2 – Tabela de Contatos
No item Database Diagrams do banco de dados no Server
Explorer, adicione um novo diagrama, relacionando as duas tabelas, como mostra
a Figura 3.

Figura 3 – Relacionamento entre clientes e contatos
Salve o banco de dados. Agora já podemos criar nossa
aplicação.
Criando a aplicação WPF
No Visual Studio, crie uma nova aplicação WPF. Em seguida,
adicione um novo item ao projeto, escolhendo LINQ to SQL. De o nome de ClientesModel
ao modelo.
Arraste as duas tabelas que criamos ao modelo. Você deverá
ter uma tela semelhante à da Figura 4.

Figura 4 – Modelo LINQ to SQL
Note que o modelo faz o relacionamento entre as duas classes
a partir da chave estrangeira do banco de dados. Quando usarmos a classe Cliente,
veremos que um dos seus membros é a coleção Contatos, que contém seus
contatos.
Em Window1.xaml, adicione o seguinte markup
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="*"
/>
<ColumnDefinition Width="2*"
/>
</Grid.ColumnDefinitions>
<DockPanel>
<StackPanel
HorizontalAlignment="Right" Orientation="Horizontal"
DockPanel.Dock="Bottom">
<Button x:Name="button1"
Content="Novo" Margin="5"/>
<Button x:Name="button2"
Content="Exclui" Margin="5"/>
<Button x:Name="button3"
Content="Salva" Margin="5"/>
</StackPanel>
<ListBox Name="listBox1"
Margin="5,5,0,5" />
</DockPanel>
</Grid>
Estamos incluindo duas colunas na grid. Na primeira,
colocamos um DockPanel com uma ListBox e um StackPanel. Na ListBox iremos
mostrar os nomes dos clientes cadastrados, para mostrar seus detalhes na
segunda coluna da grid. No StackPanel colocamos os botões para incluir ou
excluir um cliente e salvar as alterações.
Em Window1.xaml.cs, coloque o seguinte código
ClientesModelDataContext dc = new ClientesModelDataContext();
ObservableCollection<Cliente> cliLista = new
ObservableCollection<Cliente>();
public Window1()
{
InitializeComponent();
foreach (var c in dc.Clientes)
{
cliLista.Add(c);
}
listBox1.ItemsSource = cliLista;
}
Estamos declarando o contexto de dados como um membro
privado, assim ficará acessível a toda a classe. Em seguida, declaramos uma
ObservableCollection<Cliente>. Este é um tipo de coleção próprio para o
WPF. Ela implementa a interface INotifyCollectionChanged, que é
necessária para que as alterações nos dados da coleção sejam propagados para a
interface. Assim, quando alterarmos qualquer informação, a interface de usuário
será notificada e apresentada ao usuário. Como não temos como converter
diretamente o resultado de dc.Clientes (que é um IEnumerable) para a
ObservableCollection, fazemos um loop varrendo os clientes e adicionando-os à
coleção. Desta maneira, podemos apresentar os dados na interface de usuário e
alterá-los, sem precisar criar código especial para alimentar a interface.
Finalmente, atribuímos a coleção à propriedade ItemsSource da listbox.
Em seguida, podemos criar o markup para o detalhe do cliente.
Em Window1.xaml, coloque o seguinte código:
<Grid
Grid.Column="1" DataContext="{Binding
ElementName=listBox1,Path=SelectedItem}">
<Grid.RowDefinitions>
<RowDefinition Height="Auto"
/>
<RowDefinition Height="Auto"
/>
<RowDefinition Height="Auto"
/>
<RowDefinition Height="Auto"
/>
<RowDefinition Height="Auto"
/>
<RowDefinition Height="Auto"
/>
<RowDefinition />
<RowDefinition Height="Auto"
/>
</Grid.RowDefinitions>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="100"
/>
<ColumnDefinition />
</Grid.ColumnDefinitions>
<TextBlock Text="Nome"
Grid.Row="0" Margin="5"
VerticalAlignment="Center"/>
<TextBlock Text="Endereço"
Grid.Row="1" Margin="5"
VerticalAlignment="Center"/>
<TextBlock Text="Cidade"
Grid.Row="2" Margin="5"
VerticalAlignment="Center"/>
<TextBlock Text="Estado"
Grid.Row="3" Margin="5"
VerticalAlignment="Center"/>
<TextBlock Text="Telefone"
Grid.Row="4" Margin="5"
VerticalAlignment="Center"/>
<TextBlock Text="Obs"
Grid.Row="5" Margin="5"
VerticalAlignment="Center"/>
<TextBox
Grid.Row="0" Grid.Column="1" Margin="5" Text="{Binding Nome,Mode=TwoWay,UpdateSourceTrigger=PropertyChanged}"/>
<TextBox
Grid.Row="1" Grid.Column="1" Margin="5" Text="{Binding
Endereco,Mode=TwoWay,UpdateSourceTrigger=PropertyChanged}"/>
<TextBox
Grid.Row="2" Grid.Column="1" Margin="5" Text="{Binding Cidade,Mode=TwoWay,UpdateSourceTrigger=PropertyChanged}"/>
<TextBox
Grid.Row="3" Grid.Column="1" Margin="5" Text="{Binding Estado,Mode=TwoWay,UpdateSourceTrigger=PropertyChanged}"/>
<TextBox
Grid.Row="4" Grid.Column="1" Margin="5" Text="{Binding
Telefone,Mode=TwoWay,UpdateSourceTrigger=PropertyChanged}"/>
<TextBox
Grid.Row="5" Grid.Column="1" Margin="5" Text="{Binding Obs,Mode=TwoWay,UpdateSourceTrigger=PropertyChanged}"/>
<TabControl x:Name="tabControl1"
Grid.Row="6" Grid.ColumnSpan="2" Margin="5"
/>
<StackPanel
Orientation="Horizontal" Grid.Row="7"
Grid.ColumnSpan="2"
HorizontalAlignment="Right">
<Button Margin="5"
Content="Novo contato" />
<Button Margin="5"
Content="Exclui contato" />
</StackPanel>
</Grid>
Você deve obter uma tela semelhante à Figura 5.

Figura 5 – Janela após a inclusão do markup de detalhe
Este código adiciona os TextBlocks com os títulos e os
TextBoxes para edição dos dados. A propriedade Text dos TextBoxes é
ligada aos dados usando Data Binding. Por exemplo, na primeira TextBox, estamos
ligando sua propriedade Text a Nome. O modo de ligação é TwoWay,
de maneira que quando alterarmos a interface de usuário editando o texto, o
dado da classe também seja alterado. Além disso, configuramos a propriedade UpdateSourceTrigger
para PropertyChanged de maneira que os dados sejam alterados à medida
que digitamos.
Abaixo dos dados do detalhe, temos um TabControl que irá
conter os contatos do cliente. Temos ainda 2 botões, para incluir ou excluir um
contato.
Agora, você deve estar se perguntando: “ok, ligamos os dados
usando data binding, mas de onde estes dados estão vindo?”. A resposta está na
propriedade DataContext. Configuramos a propriedade DataContext
da grid para fazer um binding com o item selecionado da listbox. Assim, quando
selecionarmos um item da listbox, os dados da parte de detalhe serão
preenchidos automaticamente.
Dê um clique duplo no botão Novo e insira o seguinte
código:
private void
button1_Click(object sender, RoutedEventArgs e)
{
Cliente cli = new
Cliente() { Nome = "Não
identificado" };
dc.Clientes.InsertOnSubmit(cli);
cliLista.Add(cli);
}
Este código inclui um novo cliente. Para inserirmos este
cliente no banco de dados, devemos usar a função InsertOnSubmit. Com
ela, quando usarmos o método SubmitChanges do DataContext, os dados
deste cliente serão persistidos para o banco. Ao mesmo tempo, devemos adicionar
este cliente à nossa ObservableCollection, para que ele apareça na interface.
Isto é tudo o que precisamos fazer para que o dado do cliente apareça nas
caixas de texto e seja alterado quando editamos.
Dê um clique duplo no botão Exclui e adicione o
seguinte código:
private void
button2_Click(object sender, RoutedEventArgs e)
{
if
(listBox1.SelectedItem != null)
{
Cliente
cli = (Cliente)listBox1.SelectedItem;
cli.Contatos.Clear();
dc.Clientes.DeleteOnSubmit((Cliente)listBox1.SelectedItem);
cliLista.Remove((Cliente)listBox1.SelectedItem);
}
}
Este código é bastante simples: verificamos se há um item
selecionado na ListBox. Se houver, excluímos todos os seus contatos e, em
seguida,o excluímos do DataContext usando a função DeleteOnSubmit e
retiramos o elemento da coleção. Dê um clique duplo no botão Salva e coloque:
private
void button3_Click(object
sender, RoutedEventArgs e)
{
dc.SubmitChanges();
}
Neste caso, precisamos apenas chamar a função SubmitChanges
para gravar fisicamente as alterações. Rode o programa e verifique que, ao
clicar no botão Novo, adicionamos um novo cliente. Selecionando-o na
lista, seus dados são mostrados no detalhe. Podemos alterar seus dados e salvar
usando o botão Salva. Temos apenas uma pequena coisa a alterar. Os
clientes incluídos não são mostrados na lista. Cada cliente é mostrado como <Namespace>.Cliente,
onde <Namespace> é o nome do Namespace de seu projeto. Para
mostrar o nome do cliente, devemos configurar a propriedade DisplayMemberPath
da ListBox para mostrar o nome, assim:
<ListBox Name="listBox1" Margin="5,5,0,5" DisplayMemberPath="Nome"/>
Com esta alteração, o nome do cliente é mostrado na lista.
Agora devemos adicionar os contatos na tela.
Criando um UserControl
Apresentaremos os contatos do cliente no TabControl que está
abaixo das caixas de edição. Cada contato ficará em um TabItem e poderá ser
editado. Como a estrutura de cada contato é igual, é interessante que criemos
um UserControl para editar este contato. Assim, bastará adicionar o UserControl
ao TabItem e não teremos mais nada a fazer.
Adicione um novo UserControl ao projeto e chame-o de ContatoControl.
Tire o Width e o Height do UserControl e adicione o seguinte markup na janela:
<Grid>
<Grid.RowDefinitions>
<RowDefinition Height="Auto"
/>
<RowDefinition Height="Auto"
/>
<RowDefinition Height="Auto"
/>
</Grid.RowDefinitions>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="100"
/>
<ColumnDefinition />
</Grid.ColumnDefinitions>
<TextBlock Text="Nome"
Grid.Row="0" Margin="5"
VerticalAlignment="Center"/>
<TextBlock Text="Email"
Grid.Row="1" Margin="5"
VerticalAlignment="Center"/>
<TextBlock Text="Telefone"
Grid.Row="2" Margin="5"
VerticalAlignment="Center"/>
<TextBox
Grid.Row="0" Grid.Column="1" Margin="5" Text="{Binding Nome,Mode=TwoWay,UpdateSourceTrigger=PropertyChanged}"/>
<TextBox
Grid.Row="1" Grid.Column="1" Margin="5" Text="{Binding Email,Mode=TwoWay,UpdateSourceTrigger=PropertyChanged}"/>
<TextBox
Grid.Row="2" Grid.Column="1" Margin="5" Text="{Binding Telefone,Mode=TwoWay,UpdateSourceTrigger=PropertyChanged}"/>
</Grid>
Neste UserControl, colocamos as caixas de edição para os
três campos do contato, usando data binding. Você deve ter algo semelhante à
Figura 6.

Figura 6 – UserControl do contato
Precisamos agora mostrar os contatos do cliente selecionado.
Volte a Window1.xaml, selecione a listbox e, na janela de propriedades,
selecione os eventos e dê um clique duplo no evento SelectionChanged. Nocódigo
do manipulador para este evento, coloque:
private
void listBox1_SelectionChanged(object sender, SelectionChangedEventArgs
e)
{
tabControl1.Items.Clear();
if
(listBox1.SelectedItem != null)
{
Cliente cli = (Cliente)listBox1.SelectedItem;
foreach (var c in
cli.Contatos)
{
AdicionaTabContato(c);
}
}
}
Inicialmente, limpamos o TabControl e verificamos se há um
elemento selecionado na lista. Se houver, este é do tipo Cliente. Então,
varremos os contatos do cliente, adicionando os TabItems. A função AdicionaTabContato
é
private
void AdicionaTabContato(Contato c)
{
TabItem item = new
TabItem();
ContatoControl ctrl = new ContatoControl();
ctrl.DataContext = c;
item.Content = ctrl;
item.Header = c.Id.ToString();
tabControl1.Items.Add(item);
tabControl1.SelectedItem = item;
}
Criamos um novo TabItem e um UserControl do tipo que
criamos. Em seguida, associamos o contexto de dados ao contato atual. Assim,
todo o data binding é feito de maneira automática, sem que precisemos associar
nada. Associamos o UserControl à propriedade Content do TabItem, de modo
que o TabItem mostre o UserControl. Adicionamos o TabItem ao PageControl e
selecionamos ele. Com o manipulador do método SelectionChanged e a
função AdicionaContato, mostramos no TabControl os contatos do cliente
selecionado.
Para incluir um novo contato, devemos dar um clique duplo no
botão de incluir contatos e adicionar os seguinte código:
private void
Button_Click(object sender, RoutedEventArgs e)
{
if
(listBox1.SelectedItem != null)
{
Cliente
cli = (Cliente)listBox1.SelectedItem;
Contato con = new Contato() { Cliente = cli.Id, Nome="Não identificado" };
cli.Contatos.Add(con);
AdicionaTabContato(con);
}
}
Verificamos se há um item selecionado na ListBox. Se houver,
criamos um novo contato e adicionamos ele à coleção de contatos do cliente. Em
seguida, chamamos a função AdicionaTabContato para adicionar um novo
TabItem para este contato.
Para excluir um contato, devemos dar um clique duplo no
botão de exclusão e adicionar o seguinte código:
private void
Button_Click_1(object sender, RoutedEventArgs e)
{
if
(listBox1.SelectedItem != null &&
tabControl1.SelectedItem != null)
{
Cliente
cli = (Cliente)listBox1.SelectedItem;
Contato
con = (Contato)((ContatoControl)((TabItem)tabControl1.SelectedItem).Content).DataContext;
cli.Contatos.Remove(con);
tabControl1.Items.Remove(tabControl1.SelectedItem);
if
(tabControl1.Items.Count > 0)
tabControl1.SelectedIndex = 0;
}
}
Neste caso, precisamos verificar se há um item na ListBox
selecionado e, se houver, se há um TabItem de contato selecionado. Se houver,
obtemos o contato a partir do DataContext do UserControl que está no TabItem
selecionado e removemos ele da coleção de contatos do cliente. Em seguida,
removemos o TabItem selecionado do TabControl.
Com isso, nossa aplicação está pronta. Podemos incluir,
alterar ou excluir clientes e, dentro de cada cliente, incluir, alterar ou
excluir contatos. A aplicação pronta está mostrada na Figura 7.

Figura 7 – Aplicação finalizada
Conclusões
Como pudemos ver, o desenvolvimento de aplicações LOB com
WPF é bastante simples e isto é facilitado com o uso do Data Binding. Não
precisamos gerar nenhum código para alimentar nossa interface de usuário, que
está diretamente ligada aos dados. A criação de UserControls também facilita
bastante o uso do WPF, pois com um controle encapsulamos toda a funcionalidade
requerida pelo contato. Poderíamos colocar ali código de validação ou mesmo
mais informações e isto não afeta em nada nosso programa principal.
Agora, podemos nos dedicar a deixar esta aplicação mais
bonita, outra das especialidades do WPF, mas isto é assunto para outro artigo.