Nota
O acesso a esta página requer autorização. Podes tentar iniciar sessão ou mudar de diretório.
O acesso a esta página requer autorização. Podes tentar mudar de diretório.
Este passo a passo mostra como vincular tipos POCO a controles WPF em um formulário de "detalhes principais". O aplicativo usa as APIs do Entity Framework para preencher objetos com dados do banco de dados, controlar alterações e persistir dados no banco de dados.
O modelo define dois tipos que participam da relação um-para-muitos: Categoria (principal\primário) e Produto (dependente\detalhado). A estrutura de vinculação de dados do WPF permite a navegação entre objetos relacionados: a seleção de linhas na exibição mestre faz com que a exibição de detalhes seja atualizada com os dados filho correspondentes.
As capturas de tela e listagens de código neste passo a passo são retiradas do Visual Studio 2019 16.6.5.
Tip
Você pode ver o exemplo de deste artigo no GitHub.
Pre-Requisites
Você precisa ter o Visual Studio 2019 16.3 ou posterior instalado com a carga de trabalho da área de trabalho .NET selecionada para concluir este passo a passo. Para obter mais informações sobre como instalar a versão mais recente do Visual Studio, consulte Instalar o Visual Studio.
Criar o aplicativo
- Abrir o Visual Studio
- Na janela Iniciar, escolha Criar novo projeto.
- Procure por "WPF", escolha WPF App (.NET) e, em seguida, escolha Next.
- Na próxima tela, dê um nome ao projeto, por exemplo, GetStartedWPF e escolha Criar.
Instalar os pacotes NuGet do Entity Framework
Clique com o botão direito do mouse na solução e escolha Gerenciar pacotes NuGet para solução...
Digite
entityframeworkcore.sqlitena caixa de pesquisa.Selecione o pacote Microsoft.EntityFrameworkCore.Sqlite .
Verifique o projeto no painel direito e clique em Instalar
Repita as etapas para procurar
entityframeworkcore.proxiese instalar Microsoft.EntityFrameworkCore.Proxies.
Note
Quando você instalou o pacote Sqlite, ele automaticamente puxou para baixo o pacote base Microsoft.EntityFrameworkCore relacionado. O pacote Microsoft.EntityFrameworkCore.Proxies fornece suporte para dados de "carregamento lento". Isso significa que quando você tem entidades com entidades filhas, somente os pais são buscados na carga inicial. Os proxies detetam quando uma tentativa de aceder às entidades filhas é feita e carregam-nas automaticamente quando necessário.
Definir um modelo
Neste passo a passo, você implementará um modelo usando "código primeiro". Isso significa que o EF Core criará as tabelas e o esquema do banco de dados com base nas classes C# que você definir.
Adicione uma nova classe. Dê-lhe o nome: Product.cs e preencha-o assim:
Product.cs
namespace GetStartedWPF
{
public class Product
{
public int ProductId { get; set; }
public string Name { get; set; }
public int CategoryId { get; set; }
public virtual Category Category { get; set; }
}
}
Em seguida, adicione uma classe chamada Category.cs e preencha-a com o seguinte código:
Category.cs
using System.Collections.Generic;
using System.Collections.ObjectModel;
namespace GetStartedWPF
{
public class Category
{
public int CategoryId { get; set; }
public string Name { get; set; }
public virtual ICollection<Product>
Products
{ get; private set; } =
new ObservableCollection<Product>();
}
}
As propriedades Products na classe Category e Category na classe Product são propriedades de navegação. No Entity Framework, as propriedades de navegação fornecem uma maneira de navegar em uma relação entre dois tipos de entidade.
Além de definir entidades, você precisa definir uma classe que deriva de DbContext e expõe as propriedades DbSet<TEntity> . As propriedades DbSet<TEntity> permitem que o contexto saiba quais tipos você deseja incluir no modelo.
Uma instância do tipo derivado DbContext gerencia os objetos de entidade durante o tempo de execução, o que inclui o preenchimento de objetos com dados de um banco de dados, o controle de alterações e a persistência de dados no banco de dados.
Adicione uma nova ProductContext.cs classe ao projeto com a seguinte definição:
ProductContext.cs
using Microsoft.EntityFrameworkCore;
namespace GetStartedWPF
{
public class ProductContext : DbContext
{
public DbSet<Product> Products { get; set; }
public DbSet<Category> Categories { get; set; }
protected override void OnConfiguring(
DbContextOptionsBuilder optionsBuilder)
{
optionsBuilder.UseSqlite(
"Data Source=products.db");
optionsBuilder.UseLazyLoadingProxies();
}
}
}
- O
DbSetinforma ao EF Core quais entidades C# devem ser mapeadas para o banco de dados. - Há várias maneiras de configurar o EF Core
DbContext. Você pode ler sobre eles em: Configurando um DbContext. - Este exemplo usa a opção de substituição
OnConfiguringpara especificar um ficheiro de dados Sqlite. - A
UseLazyLoadingProxieschamada instrui o EF Core a implementar o carregamento lento, para que as entidades filhas sejam carregadas automaticamente quando acedidas a partir do pai.
Pressione CTRL+SHIFT+B ou navegue até Build > Build Solution para compilar o projeto.
Tip
Saiba mais sobre as diferentes formas de manter o seu banco de dados e modelos EF Core em sincronia: Gestão de Esquemas de Banco de Dados.
Carregamento preguiçoso
As propriedades Products na classe Category e Category na classe Product são propriedades de navegação. No Entity Framework Core, as propriedades de navegação fornecem uma maneira de navegar em uma relação entre dois tipos de entidade.
O EF Core oferece a opção de carregar entidades relacionadas do banco de dados automaticamente na primeira vez que você acessa a propriedade de navegação. Com esse tipo de carregamento (chamado de carregamento lento), esteja ciente de que, na primeira vez que você acessar cada propriedade de navegação, uma consulta separada será executada no banco de dados se o conteúdo ainda não estiver no contexto.
Ao usar tipos de entidade "Plain Old C# Object" (POCO), o EF Core obtém carregamento lento criando instâncias de tipos de proxy derivados durante o tempo de execução e, em seguida, substituindo propriedades virtuais em suas classes para adicionar o gancho de carregamento. Para obter carregamento lento de objetos relacionados, deve-se declarar as propriedades de navegação getter como públicas e virtuais (Overridable em Visual Basic), e a sua classe não deve ser selada (NotOverridable em Visual Basic). Ao usar o Database First, as propriedades de navegação são automaticamente tornadas virtuais para permitir o carregamento lento.
Vincular objeto a controles
Adicione as classes definidas no modelo como fontes de dados para este aplicativo WPF.
Clique duas vezes em MainWindow.xaml no Gerenciador de Soluções para abrir o formulário principal
Escolha a guia XAML para editar o XAML.
Imediatamente após a tag de abertura
Window, adicione as seguintes fontes para se conectar às entidades do EF Core.<Window x:Class="GetStartedWPF.MainWindow" xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" xmlns:d="http://schemas.microsoft.com/expression/blend/2008" xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" xmlns:local="clr-namespace:GetStartedWPF" mc:Ignorable="d" Title="MainWindow" Height="450" Width="800" Loaded="Window_Loaded"> <Window.Resources> <CollectionViewSource x:Key="categoryViewSource"/> <CollectionViewSource x:Key="categoryProductsViewSource" Source="{Binding Products, Source={StaticResource categoryViewSource}}"/> </Window.Resources>Isso configura a fonte para as categorias "mãe" e a segunda fonte para os produtos "detalhe".
Em seguida, adicione a seguinte marcação ao seu XAML após a tag de abertura
Grid.<DataGrid x:Name="categoryDataGrid" AutoGenerateColumns="False" EnableRowVirtualization="True" ItemsSource="{Binding Source={StaticResource categoryViewSource}}" Margin="13,13,43,229" RowDetailsVisibilityMode="VisibleWhenSelected"> <DataGrid.Columns> <DataGridTextColumn Binding="{Binding CategoryId}" Header="Category Id" Width="SizeToHeader" IsReadOnly="True"/> <DataGridTextColumn Binding="{Binding Name}" Header="Name" Width="*"/> </DataGrid.Columns> </DataGrid>Observe que o
CategoryIdé definido comoReadOnlyporque é atribuído pelo banco de dados e não pode ser alterado.
Adicionar uma grelha de detalhes
Agora que a grade existe para exibir categorias, a grade de detalhes pode ser adicionada para mostrar produtos. Adicione isso dentro do Grid elemento , após o elemento categories DataGrid .
MainWindow.xaml
<DataGrid x:Name="productsDataGrid" AutoGenerateColumns="False"
EnableRowVirtualization="True"
ItemsSource="{Binding Source={StaticResource categoryProductsViewSource}}"
Margin="13,205,43,108" RowDetailsVisibilityMode="VisibleWhenSelected"
RenderTransformOrigin="0.488,0.251">
<DataGrid.Columns>
<DataGridTextColumn Binding="{Binding CategoryId}"
Header="Category Id" Width="SizeToHeader"
IsReadOnly="True"/>
<DataGridTextColumn Binding="{Binding ProductId}" Header="Product Id"
Width="SizeToHeader" IsReadOnly="True"/>
<DataGridTextColumn Binding="{Binding Name}" Header="Name" Width="*"/>
</DataGrid.Columns>
</DataGrid>
Por fim, adicione um botão Save e associe o evento de clique a Button_Click.
<Button Content="Save" HorizontalAlignment="Center" Margin="0,240,0,0"
Click="Button_Click" Height="20" Width="123"/>
A visualização do desenho ou modelo deve ter esta aparência:
Adicionar código que lida com interação de dados
É hora de adicionar alguns manipuladores de eventos à janela principal.
Na janela XAML, clique no <elemento Window> para selecionar a janela principal.
Na janela Propriedades , escolha Eventos no canto superior direito e clique duas vezes na caixa de texto à direita do rótulo Carregado .
Isso leva você ao código subjacente do formulário; agora vamos editar o código para usar o ProductContext para realizar o acesso aos dados. Atualize o código como mostrado abaixo.
O código declara uma instância de longa execução do ProductContext. O ProductContext objeto é usado para consultar e salvar dados no banco de dados. O método Dispose() na instância ProductContext é então chamado a partir do método sobreposto OnClosing. Os comentários de código explicam o que cada etapa faz.
MainWindow.xaml.cs
using Microsoft.EntityFrameworkCore;
using System.ComponentModel;
using System.Windows;
using System.Windows.Data;
namespace GetStartedWPF
{
/// <summary>
/// Interaction logic for MainWindow.xaml
/// </summary>
public partial class MainWindow : Window
{
private readonly ProductContext _context =
new ProductContext();
private CollectionViewSource categoryViewSource;
public MainWindow()
{
InitializeComponent();
categoryViewSource =
(CollectionViewSource)FindResource(nameof(categoryViewSource));
}
private void Window_Loaded(object sender, RoutedEventArgs e)
{
// this is for demo purposes only, to make it easier
// to get up and running
_context.Database.EnsureCreated();
// load the entities into EF Core
_context.Categories.Load();
// bind to the source
categoryViewSource.Source =
_context.Categories.Local.ToObservableCollection();
}
private void Button_Click(object sender, RoutedEventArgs e)
{
// all changes are automatically tracked, including
// deletes!
_context.SaveChanges();
// this forces the grid to refresh to latest values
categoryDataGrid.Items.Refresh();
productsDataGrid.Items.Refresh();
}
protected override void OnClosing(CancelEventArgs e)
{
// clean up database connections
_context.Dispose();
base.OnClosing(e);
}
}
}
Note
O código usa uma chamada para EnsureCreated() criar o banco de dados na primeira execução. Isso é aceitável para demonstrações, mas em aplicativos de produção você deve examinar as migrações para gerenciar seu esquema. O código também é executado de forma síncrona porque usa um banco de dados SQLite local. Para cenários de produção que normalmente envolvem um servidor remoto, considere o uso das versões assíncronas dos métodos Load e SaveChanges.
Testar o aplicativo WPF
Compile e execute o aplicativo pressionando F5 ou escolhendo Depurar > Iniciar Depuração. O banco de dados deve ser criado automaticamente com um arquivo chamado products.db. Introduza um nome de categoria e prima Enter e, em seguida, adicione produtos à grelha inferior. Clique em Salvar e assista à atualização da grade com as ids fornecidas pelo banco de dados. Realce uma linha e pressione Delete para remover a linha. A entidade será excluída quando você clicar em Salvar.
Notificação de alteração de propriedade
Este exemplo se baseia em quatro etapas para sincronizar as entidades com a interface do usuário.
- A chamada
_context.Categories.Load()inicial carrega os dados das categorias. - Os proxies de carregamento lento carregam os dados dos produtos dependentes.
- O controle de alterações integrado do EF Core faz as modificações necessárias nas entidades, incluindo inserções e exclusões, quando
_context.SaveChanges()é chamado. - As chamadas para
DataGridView.Items.Refresh()forçam uma recarga com os IDs recém-gerados.
Isso funciona para nosso exemplo de introdução, mas você pode precisar de código adicional para outros cenários. Os controles WPF renderizam a interface lendo os campos e as propriedades nas suas entidades. Quando você edita um valor na interface do usuário (UI), esse valor é passado para sua entidade. Quando você altera o valor de uma propriedade diretamente em sua entidade, como carregá-la do banco de dados, o WPF não refletirá imediatamente as alterações na interface do usuário. O motor de renderização deve ser notificado das alterações. O projeto fez isso chamando Refresh()manualmente . Uma maneira fácil de automatizar essa notificação é implementando a interface INotifyPropertyChanged . Os componentes do WPF detetarão automaticamente a interface e se registrarão para eventos de alteração. A entidade é responsável por gerar esses eventos.
Tip
Para saber mais sobre como lidar com alterações, leia: Como implementar a notificação de alteração de propriedade.
Próximas Etapas
Saiba mais sobre como configurar um DbContext.