Nota
O acesso a esta página requer autorização. Pode tentar iniciar sessão ou alterar os diretórios.
O acesso a esta página requer autorização. Pode tentar alterar os diretórios.
Important
Este documento é válido para WPF apenas no .NET Framework
Este documento descreve a vinculação de dados para WPF no .NET Framework. Para novos projetos .NET, recomendamos que você use o EF Core em vez do Entity Framework 6. A documentação para vinculação de dados no EF Core está aqui: Introdução ao WPF.
Este passo a passo mostra como vincular tipos POCO a controles WPF em um formulário "mestre-detalhe". 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\mestre) e Produto (dependente\detalhe). Em seguida, as ferramentas do Visual Studio são usadas para vincular os tipos definidos no modelo aos controles WPF. 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 2013, mas você pode concluir este passo a passo com o Visual Studio 2012 ou Visual Studio 2010.
Use a opção 'Object' para criar fontes de dados WPF
Com a versão anterior do Entity Framework, costumávamos recomendar o uso da opção Banco de Dados ao criar uma nova Fonte de Dados com base em um modelo criado com o EF Designer. Isso ocorreu porque o designer geraria um contexto derivado de ObjectContext e classes de entidade derivadas de EntityObject. Usar a opção Banco de dados ajudaria você a escrever o melhor código para interagir com essa superfície de API.
Os EF Designers para Visual Studio 2012 e Visual Studio 2013 geram um contexto que deriva de DbContext juntamente com classes de entidade POCO simples. Com o Visual Studio 2010, recomendamos a troca para um modelo de geração de código que usa DbContext, conforme descrito posteriormente neste passo a passo.
Ao usar a superfície da API DbContext, você deve usar a opção Object ao criar uma nova fonte de dados, conforme mostrado nesta explicação passo a passo.
Se necessário, você pode reverter para a geração de código baseada em ObjectContext para modelos criados com o EF Designer.
Pre-Requisites
Você precisa ter o Visual Studio 2013, Visual Studio 2012 ou Visual Studio 2010 instalado para concluir este passo a passo.
Se você estiver usando o Visual Studio 2010, também precisará instalar o NuGet. Para obter mais informações, consulte Instalando o NuGet.
Criar o aplicativo
- Abrir o Visual Studio
- Ficheiro -> Novo -> Projeto....
- Selecione Windows no painel esquerdo e WPFApplication no painel direito
- Digite WPFwithEFSample como o nome
- Selecione OK
Instalar o pacote NuGet do Entity Framework
- No Gerenciador de Soluções, clique com o botão direito do mouse no projeto WinFormswithEFSample
- Selecione Gerenciar pacotes NuGet...
- Na caixa de diálogo Gerenciar pacotes NuGet, selecione a guia Online e escolha o pacote EntityFramework
- Clique em Instalar
Note
Além do assembly do EntityFramework, uma referência a System.ComponentModel.DataAnnotations também é adicionada. Se o projeto tiver uma referência a System.Data.Entity, ele será removido quando o pacote EntityFramework for instalado. O assembly System.Data.Entity não é mais usado para aplicativos do Entity Framework 6.
Definir um modelo
Neste passo a passo, você pode optar por implementar um modelo usando o Code First ou o EF Designer. Preencha uma das duas seções a seguir.
Opção 1: Definir um modelo usando o Code First
Esta seção mostra como criar um modelo e seu banco de dados associado usando o Code First. Vá para a próxima seção (Opção 2: Definir um modelo usando o Database First) se preferir usar o Database First para fazer engenharia reversa do seu modelo a partir de um banco de dados usando o designer EF
Ao usar o desenvolvimento Code First, você geralmente começa escrevendo classes do .NET Framework que definem seu modelo conceitual (domínio).
- Adicione uma nova classe ao WPFwithEFSample:
- Clique com o botão direito do rato no nome do projeto
- Selecione Adicionar e, em seguida, Novo Item
- Selecione Classe e insira Produto para o nome da classe
- Substitua a definição de classe de produto pelo seguinte código:
namespace WPFwithEFSample
{
public class Product
{
public int ProductId { get; set; }
public string Name { get; set; }
public int CategoryId { get; set; }
public virtual Category Category { get; set; }
}
}
- Adicione uma classe Category com a seguinte definição:
using System.Collections.ObjectModel;
namespace WPFwithEFSample
{
public class Category
{
public Category()
{
this.Products = new ObservableCollection<Product>();
}
public int CategoryId { get; set; }
public string Name { get; set; }
public virtual ObservableCollection<Product> Products { get; private set; }
}
}
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 classe ProductContext ao projeto com a seguinte definição:
using System.Data.Entity;
namespace WPFwithEFSample
{
public class ProductContext : DbContext
{
public DbSet<Category> Categories { get; set; }
public DbSet<Product> Products { get; set; }
}
}
Compile o projeto.
Opção 2: Definir um modelo usando o Database First
Esta seção mostra como usar o Database First para fazer engenharia reversa do seu modelo a partir de um banco de dados usando o designer do EF. Se você concluiu a seção anterior (Opção 1: Definir um modelo usando o Code First), ignore esta seção e vá direto para a seção Carregamento lento .
Criar um banco de dados existente
Normalmente, quando se direciona um banco de dados existente, ele já estará criado, mas para este tutorial precisamos criar um banco de dados para acessar.
O servidor de banco de dados instalado com o Visual Studio é diferente dependendo da versão do Visual Studio que você instalou:
- Se você estiver usando o Visual Studio 2010, criará um banco de dados SQL Express.
- Se você estiver usando o Visual Studio 2012, criará um banco de dados LocalDB .
Vamos em frente e gerar o banco de dados.
Visualizar -> Explorador de Servidores
Clique com o botão direito do mouse em Conexões de Dados -> Adicionar Conexão...
Se você não se conectou a um banco de dados a partir do Gerenciador de Servidores, antes precisará selecionar o Microsoft SQL Server como a fonte de dados
Conecte-se ao LocalDB ou ao SQL Express, dependendo de qual você instalou, e insira Produtos como o nome do banco de dados
Selecione OK e você será perguntado se deseja criar um novo banco de dados, selecione Sim
O novo banco de dados agora aparecerá no Gerenciador de Servidores, clique com o botão direito do mouse nele e selecione Nova Consulta
Copie o seguinte SQL para a nova consulta, clique com o botão direito do mouse na consulta e selecione Executar
CREATE TABLE [dbo].[Categories] (
[CategoryId] [int] NOT NULL IDENTITY,
[Name] [nvarchar](max),
CONSTRAINT [PK_dbo.Categories] PRIMARY KEY ([CategoryId])
)
CREATE TABLE [dbo].[Products] (
[ProductId] [int] NOT NULL IDENTITY,
[Name] [nvarchar](max),
[CategoryId] [int] NOT NULL,
CONSTRAINT [PK_dbo.Products] PRIMARY KEY ([ProductId])
)
CREATE INDEX [IX_CategoryId] ON [dbo].[Products]([CategoryId])
ALTER TABLE [dbo].[Products] ADD CONSTRAINT [FK_dbo.Products_dbo.Categories_CategoryId] FOREIGN KEY ([CategoryId]) REFERENCES [dbo].[Categories] ([CategoryId]) ON DELETE CASCADE
Modelo de engenharia reversa
Vamos usar o Entity Framework Designer, que está incluído como parte do Visual Studio, para criar nosso modelo.
Project -> Adicionar novo item...
Selecione Dados no menu esquerdo e, em seguida, ADO.NET Modelo de Dados de Entidade
Introduza ProductModel como o nome e clique em OK
Isso inicia o Assistente de Modelo de Dados de Entidade
Selecione Gerar a partir do banco de dados e clique em Avançar
Selecione a conexão com o banco de dados que você criou na primeira seção, digite ProductContext como o nome da cadeia de conexão e clique em Avançar
Clique na caixa de seleção ao lado de 'Tabelas' para importar todas as tabelas e clique em 'Concluir'
Quando o processo de engenharia reversa for concluído, o novo modelo será adicionado ao seu projeto e aberto para você visualizar no Entity Framework Designer. Um arquivo App.config também foi adicionado ao seu projeto com os detalhes de conexão para o banco de dados.
Etapas adicionais no Visual Studio 2010
Se você estiver trabalhando no Visual Studio 2010, precisará atualizar o designer EF para usar a geração de código EF6.
- Clique com o botão direito do mouse em um local vazio do seu modelo no EF Designer e selecione Adicionar item de geração de código...
- Selecione Modelos Online no menu esquerdo e procure por DbContext
- Selecione o EF 6.x DbContext Generator para C#, digite ProductsModel como o nome e clique em Adicionar
Atualizando a geração de código para vinculação de dados
O EF gera código a partir do seu modelo usando modelos T4. Os modelos fornecidos com o Visual Studio ou baixados da galeria do Visual Studio destinam-se ao uso geral. Isso significa que as entidades geradas a partir desses modelos têm propriedades ICollection<T> simples. No entanto, ao fazer a vinculação de dados usando o WPF, é desejável usar ObservableCollection para propriedades de coleta para que o WPF possa acompanhar as alterações feitas nas coleções. Para este fim, vamos modificar os modelos para usar ObservableCollection.
Abra o Gerenciador de Soluções e localize o arquivo ProductModel.edmx
Encontre o arquivo ProductModel.tt que será aninhado no arquivo ProductModel.edmx
Clique duas vezes no arquivo ProductModel.tt para abri-lo no editor do Visual Studio
Encontre e substitua as duas ocorrências de "ICollection" por "ObservableCollection". Estes estão localizados aproximadamente nas linhas 296 e 484.
Encontre e substitua a primeira ocorrência de "HashSet" por "ObservableCollection". Esta ocorrência está localizada aproximadamente na linha 50. Não substitua a segunda ocorrência de HashSet encontrada posteriormente no código.
Localize e substitua a única ocorrência de "System.Collections.Generic" por "System.Collections.ObjectModel". Este está localizado aproximadamente na linha 424.
Salve o arquivo ProductModel.tt. Isso deve fazer com que o código para entidades seja regenerado. Se o código não regenerar automaticamente, clique com o botão direito do rato em ProductModel.tt e escolha "Executar ferramenta personalizada".
Se você abrir o arquivo Category.cs (que está aninhado em ProductModel.tt), verá que a coleção Products tem o tipo ObservableCollection<Product>.
Compile o projeto.
Carregamento preguiçoso
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.
O EF oferece a opção de carregar entidades relacionadas do banco de dados automaticamente na primeira vez que você acessar 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 POCO, o EF obtém carregamento lento ao criar instâncias de tipos de proxy derivadas durante o tempo de execução e, em seguida, ao substituir as propriedades virtuais nas suas classes, adicionando assim um gancho para o 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. Na seção Code First, optamos por tornar as propriedades de navegação virtuais pelo mesmo motivo.
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
No menu principal, selecione Projeto -> Adicionar Nova Fonte de Dados ... (no Visual Studio 2010, você precisa selecionar Dados -> Adicionar Nova Fonte de Dados...)
Na janela Escolha um tipo de fonte de dados, selecione Objeto e clique em Avançar
Na caixa de diálogo Selecionar objetos de dados, desdobre o WPFwithEFSample duas vezes e selecione Categoria
Não há necessidade de selecionar a fonte de dados do Produto, pois chegaremos a ela por meio da propriedade do Produto na fonte de dados Categoria
Clica em Terminar.
A janela Fontes de Dados é aberta ao lado da janela MainWindow.xaml Se a janela Fontes de Dados não estiver aparecendo, selecione Exibir -> Outras Fontes de Dados do Windows ->
Pressione o ícone de pino para que a janela Fontes de dados não se oculte automaticamente. Talvez seja necessário pressionar o botão de atualização se a janela já estiver visível.
Selecione a fonte de dados Categoria e arraste-a para o formulário.
O seguinte aconteceu quando arrastámos esta fonte:
- O recurso categoryViewSource e o controle categoryDataGrid foram adicionados ao XAML
- A propriedade DataContext no elemento Grid pai foi definida como "{StaticResource categoryViewSource }". O recurso categoryViewSource serve como uma fonte de ligação para o elemento Grid outer\parent. Os elementos internos do Grid herdam o valor do DataContext do pai Grid (a propriedade ItemsSource do categoryDataGrid é atribuída a "{Binding}")
<Window.Resources>
<CollectionViewSource x:Key="categoryViewSource"
d:DesignSource="{d:DesignInstance {x:Type local:Category}, CreateList=True}"/>
</Window.Resources>
<Grid DataContext="{StaticResource categoryViewSource}">
<DataGrid x:Name="categoryDataGrid" AutoGenerateColumns="False" EnableRowVirtualization="True"
ItemsSource="{Binding}" Margin="13,13,43,191"
RowDetailsVisibilityMode="VisibleWhenSelected">
<DataGrid.Columns>
<DataGridTextColumn x:Name="categoryIdColumn" Binding="{Binding CategoryId}"
Header="Category Id" Width="SizeToHeader"/>
<DataGridTextColumn x:Name="nameColumn" Binding="{Binding Name}"
Header="Name" Width="SizeToHeader"/>
</DataGrid.Columns>
</DataGrid>
</Grid>
Adicionar uma grelha de detalhes
Agora que temos uma grade para exibir Categorias, vamos adicionar uma grade de detalhes para exibir os Produtos associados.
- Selecione a propriedade Products na fonte de dados Category e arraste-a para o formulário.
- O recurso categoryProductsViewSource e a grelha productDataGrid são adicionados ao XAML.
- O caminho de vinculação para este recurso é definido como Produtos
- A estrutura de vinculação de dados do WPF garante que apenas os Produtos relacionados à Categoria selecionada apareçam no productDataGrid
- Na Caixa de Ferramentas, arraste Button para o formulário. Defina a propriedade Name como buttonSave e a propriedade Content como Save.
O formulário deve ser semelhante a este:
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 , isso seleciona a janela principal
Na janela Propriedades, escolha Eventos no canto superior direito e, em seguida, clique duas vezes na caixa de texto à direita do rótulo Carregado
Adicione também o evento Click para o botão Salvar clicando duas vezes no botão Salvar no designer.
Isso leva você ao "code-behind" do formulário, agora vamos editar o código para usar o ProductContext para realizar operações de acesso a dados. Atualize o código para o MainWindow como mostrado abaixo.
O código declara uma instância de execução prolongada de ProductContext. O objeto ProductContext é usado para consultar e salvar dados no banco de dados. O Dispose() na instância ProductContext é então chamado a partir do método OnClosing substituído. Os comentários de código fornecem detalhes sobre o que o código faz.
using System.Data.Entity;
using System.Linq;
using System.Windows;
namespace WPFwithEFSample
{
public partial class MainWindow : Window
{
private ProductContext _context = new ProductContext();
public MainWindow()
{
InitializeComponent();
}
private void Window_Loaded(object sender, RoutedEventArgs e)
{
System.Windows.Data.CollectionViewSource categoryViewSource =
((System.Windows.Data.CollectionViewSource)(this.FindResource("categoryViewSource")));
// Load is an extension method on IQueryable,
// defined in the System.Data.Entity namespace.
// This method enumerates the results of the query,
// similar to ToList but without creating a list.
// When used with Linq to Entities this method
// creates entity objects and adds them to the context.
_context.Categories.Load();
// After the data is loaded call the DbSet<T>.Local property
// to use the DbSet<T> as a binding source.
categoryViewSource.Source = _context.Categories.Local;
}
private void buttonSave_Click(object sender, RoutedEventArgs e)
{
// When you delete an object from the related entities collection
// (in this case Products), the Entity Framework doesn’t mark
// these child entities as deleted.
// Instead, it removes the relationship between the parent and the child
// by setting the parent reference to null.
// So we manually have to delete the products
// that have a Category reference set to null.
// The following code uses LINQ to Objects
// against the Local collection of Products.
// The ToList call is required because otherwise the collection will be modified
// by the Remove call while it is being enumerated.
// In most other situations you can use LINQ to Objects directly
// against the Local property without using ToList first.
foreach (var product in _context.Products.Local.ToList())
{
if (product.Category == null)
{
_context.Products.Remove(product);
}
}
_context.SaveChanges();
// Refresh the grids so the database generated values show up.
this.categoryDataGrid.Items.Refresh();
this.productsDataGrid.Items.Refresh();
}
protected override void OnClosing(System.ComponentModel.CancelEventArgs e)
{
base.OnClosing(e);
this._context.Dispose();
}
}
}
Testar o aplicativo WPF
Compile e execute o aplicativo. Se você usou Code First, verá que um banco de dados WPFwithEFSample.ProductContext foi criado para você.
Insira um nome de categoria na grade superior e nomes de produtos na grade inferior Não insira nada em colunas de ID, porque a chave primária é gerada pelo banco de dados
Pressione o botão Salvar para salvar os dados no banco de dados
Após a chamada para SaveChanges() do DbContext, as IDs são preenchidas com os valores gerados pelo banco de dados. Como chamamos Refresh() após SaveChanges(), os controles DataGrid também são atualizados com os novos valores.
Recursos adicionais
Para saber mais sobre a vinculação de dados a coleções usando o WPF, consulte este tópico na documentação do WPF.