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 criar um aplicativo simples do Windows Forms (WinForms) apoiado por um banco de dados SQLite. O aplicativo usa o Entity Framework Core (EF Core) para carregar dados do banco de dados, controlar as alterações feitas nesses dados e manter essas alterações de volta ao banco de dados.
As capturas de tela e listagens de código neste passo a passo são retiradas do Visual Studio 2022 17.3.0.
Sugestão
Você pode ver o exemplo de deste artigo no GitHub.
Pré-requisitos
Você precisa ter o Visual Studio 2022 17.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.
Escolha Aplicação Windows Forms e, em seguida, escolha Avançar.
Na próxima tela, dê um nome ao projeto, por exemplo, GetStartedWinForms, e escolha Next.
Na próxima tela, escolha a versão .NET a ser usada. Este passo a passo foi criado com o .NET 7, mas também deve funcionar com versões posteriores.
Selecione Criar.
Instalar os pacotes NuGet do EF Core
Clique com o botão direito do mouse na solução e escolha Gerenciar pacotes NuGet para solução...
Escolha a guia Procurar e procure por "Microsoft.EntityFrameworkCore.Sqlite".
Selecione o pacote Microsoft.EntityFrameworkCore.Sqlite .
Verifique o projeto GetStartedWinForms no painel direito.
Escolha a versão mais recente. Para usar uma versão de pré-lançamento, verifique se a caixa Incluir pré-lançamento está marcada.
Clique em Instalar
Observação
O Microsoft.EntityFrameworkCore.Sqlite é o pacote "provedor de banco de dados" para usar o EF Core com um banco de dados SQLite. Pacotes semelhantes estão disponíveis para outros sistemas de banco de dados. A instalação de um pacote de provedor de banco de dados traz automaticamente todas as dependências necessárias para usar o EF Core com esse sistema de banco de dados. Isso inclui o pacote base Microsoft.EntityFrameworkCore .
Definir um modelo
Neste passo a passo, implementaremos um modelo usando "Code First". Isso significa que o EF Core criará as tabelas e o esquema do banco de dados com base nas classes C# que você definir. Consulte Gerenciando esquemas de banco de dados para ver como usar um banco de dados existente.
Clique com o botão direito do mouse no projeto e escolha Adicionar e, em seguida, Classe... para adicionar uma nova classe.
Use o nome do arquivo
Product.cse substitua o código da classe por:using System.ComponentModel; namespace GetStartedWinForms; public class Product { public int ProductId { get; set; } public string? Name { get; set; } public int CategoryId { get; set; } public virtual Category Category { get; set; } = null!; }Repita para criar
Category.cscom o seguinte código:using Microsoft.EntityFrameworkCore.ChangeTracking; namespace GetStartedWinForms; public class Category { public int CategoryId { get; set; } public string? Name { get; set; } public virtual ObservableCollectionListSource<Product> Products { get; } = new(); }
A Products propriedade na Category classe e a Category propriedade na Product classe são chamadas de "navegações". No EF Core, as navegações definem uma relação entre dois tipos de entidade. Neste caso, a Product.Category navegação faz referência à categoria a que pertence um determinado produto. Da mesma forma, a navegação da coleção Category.Products inclui todos os produtos de uma determinada categoria.
Sugestão
Ao usar Windows Forms, o ObservableCollectionListSource, que implementa IListSource, pode ser utilizado para as navegações de coleção. Isso não é necessário, mas melhora a experiência de vinculação de dados bidirecional.
Definir o DbContext
No EF Core, uma classe derivada de DbContext é usada para configurar tipos de entidade num modelo e para agir como uma sessão para interagir com o banco de dados. No caso mais simples, uma DbContext classe:
- Contém
DbSetpropriedades para cada tipo de entidade no modelo. - Substitui o método
OnConfiguringpara configurar o fornecedor de banco de dados e a cadeia de ligação a utilizar. Consulte Configurando um DbContext para obter mais informações.
Nesse caso, a classe DbContext também substitui o OnModelCreating método para fornecer alguns dados de exemplo para o aplicativo.
Adicione uma nova ProductsContext.cs classe ao projeto com o seguinte código:
using Microsoft.EntityFrameworkCore;
namespace GetStartedWinForms;
public class ProductsContext : 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");
protected override void OnModelCreating(ModelBuilder modelBuilder)
{
modelBuilder.Entity<Category>().HasData(
new Category { CategoryId = 1, Name = "Cheese" },
new Category { CategoryId = 2, Name = "Meat" },
new Category { CategoryId = 3, Name = "Fish" },
new Category { CategoryId = 4, Name = "Bread" });
modelBuilder.Entity<Product>().HasData(
new Product { ProductId = 1, CategoryId = 1, Name = "Cheddar" },
new Product { ProductId = 2, CategoryId = 1, Name = "Brie" },
new Product { ProductId = 3, CategoryId = 1, Name = "Stilton" },
new Product { ProductId = 4, CategoryId = 1, Name = "Cheshire" },
new Product { ProductId = 5, CategoryId = 1, Name = "Swiss" },
new Product { ProductId = 6, CategoryId = 1, Name = "Gruyere" },
new Product { ProductId = 7, CategoryId = 1, Name = "Colby" },
new Product { ProductId = 8, CategoryId = 1, Name = "Mozzela" },
new Product { ProductId = 9, CategoryId = 1, Name = "Ricotta" },
new Product { ProductId = 10, CategoryId = 1, Name = "Parmesan" },
new Product { ProductId = 11, CategoryId = 2, Name = "Ham" },
new Product { ProductId = 12, CategoryId = 2, Name = "Beef" },
new Product { ProductId = 13, CategoryId = 2, Name = "Chicken" },
new Product { ProductId = 14, CategoryId = 2, Name = "Turkey" },
new Product { ProductId = 15, CategoryId = 2, Name = "Prosciutto" },
new Product { ProductId = 16, CategoryId = 2, Name = "Bacon" },
new Product { ProductId = 17, CategoryId = 2, Name = "Mutton" },
new Product { ProductId = 18, CategoryId = 2, Name = "Pastrami" },
new Product { ProductId = 19, CategoryId = 2, Name = "Hazlet" },
new Product { ProductId = 20, CategoryId = 2, Name = "Salami" },
new Product { ProductId = 21, CategoryId = 3, Name = "Salmon" },
new Product { ProductId = 22, CategoryId = 3, Name = "Tuna" },
new Product { ProductId = 23, CategoryId = 3, Name = "Mackerel" },
new Product { ProductId = 24, CategoryId = 4, Name = "Rye" },
new Product { ProductId = 25, CategoryId = 4, Name = "Wheat" },
new Product { ProductId = 26, CategoryId = 4, Name = "Brioche" },
new Product { ProductId = 27, CategoryId = 4, Name = "Naan" },
new Product { ProductId = 28, CategoryId = 4, Name = "Focaccia" },
new Product { ProductId = 29, CategoryId = 4, Name = "Malted" },
new Product { ProductId = 30, CategoryId = 4, Name = "Sourdough" },
new Product { ProductId = 31, CategoryId = 4, Name = "Corn" },
new Product { ProductId = 32, CategoryId = 4, Name = "White" },
new Product { ProductId = 33, CategoryId = 4, Name = "Soda" });
}
}
Certifique-se de criar a solução neste momento.
Adicionando controles ao formulário
O aplicativo mostrará uma lista de categorias e uma lista de produtos. Quando uma categoria é selecionada na primeira lista, a segunda lista é alterada para mostrar produtos para essa categoria. Essas listas podem ser modificadas para adicionar, remover ou editar produtos e categorias, e essas alterações podem ser salvas no banco de dados SQLite clicando em um botão Salvar .
Altere o nome do formulário principal de
Form1paraMainForm.
E altere o título para "Produtos e Categorias".
Usando a Caixa de Ferramentas, adicione dois
DataGridViewcontroles, dispostos um ao lado do outro.
Nas Propriedades do primeiro
DataGridView, altere o Nome paradataGridViewCategories.Nas Propriedades do segundo
DataGridView, altere o Nome paradataGridViewProducts.Também usando a Caixa de Ferramentas, adicione um
Buttoncontrole.Nomeie o botão
buttonSavee dê-lhe o texto "Salvar". O formulário deve ter a seguinte aparência:
Vinculação de dados
A próxima etapa é conectar os Product tipos e Category do modelo aos DataGridView controles. Isso vinculará os dados carregados pelo EF Core aos controles, de modo que as entidades rastreadas pelo EF Core sejam mantidas em sincronia com as exibidas nos controles.
Clique no Glifo de Ação do Designer no primeiro
DataGridView. Este é o pequeno botão no canto superior direito do controle.
Isso abre a Lista de Ações, onde se pode aceder à lista suspensa Escolher Fonte de Dados. Ainda não criamos uma fonte de dados, então vá para a parte inferior e escolha Adicionar nova fonte de dados de objeto....
Escolha Categoria para criar uma fonte de dados de objeto para categorias e clique em OK.
Sugestão
Se nenhum tipo de fonte de dados aparecer aqui, certifique-se de que
Product.cs,Category.cseProductsContext.csforam adicionados ao projeto e a solução foi criada.Agora, a lista suspensa Escolher fonte de dados contém a fonte de dados do objeto que acabamos de criar. Expanda Outras Fontes de Dados, Fontes de Dados do Projeto e escolha Categoria.
O segundo
DataGridViewserá vinculado aos produtos. No entanto, em vez de ligar-se ao tipoProductde nível superior, ele será ligado à navegaçãoProductsa partir da ligaçãoCategorydo primeiroDataGridView. Isto significa que, quando uma categoria é selecionada na primeira vista, os produtos dessa categoria serão automaticamente utilizados na segunda vista.Usando o Glifo de Ação do Designer no segundo
DataGridView, escolha Escolher Fonte de Dados e, em seguida, expanda ocategoryBindingSourcee escolhaProducts.
Configurando o que é exibido
Por padrão, uma coluna é criada no DataGridView para cada propriedade dos tipos vinculados. Além disso, os valores para cada uma dessas propriedades podem ser editados pelo usuário. No entanto, alguns valores, como os valores de chave primária, são considerados só de leitura e, portanto, não devem ser editados. Além disso, algumas propriedades, como a propriedade de CategoryId chave estrangeira e a Category navegação, não são úteis para o usuário e, portanto, devem ser ocultadas.
Sugestão
É comum ocultar propriedades de chave primária em um aplicativo real. Eles são deixados visíveis aqui para facilitar a visão do que o EF Core está fazendo nos bastidores.
Clique com o botão direito do mouse na primeira
DataGridViewe escolha Editar colunas....
Torne a
CategoryIdcoluna, que representa a chave primária, somente leitura e clique em OK.
Clique com o botão direito do mouse no segundo
DataGridViewe escolha Editar colunas.... Torne aProductIdcoluna somente leitura e remova asCategoryIdcolunas eCategorye clique em OK.
Conectando-se ao EF Core
O aplicativo agora precisa de uma pequena quantidade de código para conectar o EF Core aos controles ligados a dados.
Abra o
MainFormcódigo clicando com o botão direito do rato no ficheiro e escolhendo Ver Código.
Adicione um campo privado para armazenar o
DbContextda sessão e adicione substituições para os métodosOnLoadeOnClosing. O código deve ter esta aparência:
using Microsoft.EntityFrameworkCore;
using System.ComponentModel;
namespace GetStartedWinForms
{
public partial class MainForm : Form
{
private ProductsContext? dbContext;
public MainForm()
{
InitializeComponent();
}
protected override void OnLoad(EventArgs e)
{
base.OnLoad(e);
this.dbContext = new ProductsContext();
// Uncomment the line below to start fresh with a new database.
// this.dbContext.Database.EnsureDeleted();
this.dbContext.Database.EnsureCreated();
this.dbContext.Categories.Load();
this.categoryBindingSource.DataSource = dbContext.Categories.Local.ToBindingList();
}
protected override void OnClosing(CancelEventArgs e)
{
base.OnClosing(e);
this.dbContext?.Dispose();
this.dbContext = null;
}
}
}
O OnLoad método é chamado quando o formulário é carregado. Neste momento
- É criada uma instância do
ProductsContextque será usada para carregar e controlar alterações em produtos e categorias exibidas pelo aplicativo. -
EnsureCreatedé chamado noDbContextpara criar o banco de dados SQLite se ele ainda não existir. Esta é uma maneira rápida de criar um banco de dados ao prototipar ou testar aplicativos. No entanto, se o modelo for alterado, o banco de dados precisará ser excluído para que possa ser criado novamente. (AEnsureDeletedlinha pode ser descomentada para excluir e recriar facilmente o banco de dados quando o aplicativo é executado.) Em vez disso, você pode usar as migrações do EF Core para modificar e atualizar o esquema do banco de dados sem perder dados. -
EnsureCreatedtambém preencherá o novo banco de dados com os dados definidos noProductsContext.OnModelCreatingmétodo. - O
Loadmétodo de extensão é usado para carregar todas as categorias do banco de dados para oDbContext. Essas entidades agora serão rastreadas peloDbContext, que detetará quaisquer alterações feitas quando as categorias forem editadas pelo usuário. - A
categoryBindingSource.DataSourcepropriedade é inicializada para as categorias que estão sendo rastreadas peloDbContext. Isso é feito chamandoLocal.ToBindingList()na propriedadeCategoriesDbSet.Localfornece acesso a uma visualização local das categorias rastreadas, com eventos conectados para garantir que os dados locais permaneçam sincronizados com os dados exibidos e vice-versa.ToBindingList()expõe esses dados como umIBindingList, que é entendido pela associação de dados do Windows Forms.
O OnClosing método é chamado quando o formulário é fechado. Neste momento, o DbContext é descartado, que garante que quaisquer recursos do banco de dados serão liberados, e o campo dbContext é definido como nulo para que não possa ser usado novamente.
Preencher a vista Produtos
Se o aplicativo for iniciado neste ponto, então ele deve ter a seguinte aparência:
Observe que as categorias foram carregadas do banco de dados, mas a tabela de produtos permanece vazia. Além disso, o botão Salvar não funciona.
Para preencher a tabela de produtos, o EF Core precisa carregar produtos do banco de dados para a categoria selecionada. Para o conseguir:
No designer do formulário principal, selecione o
DataGridViewpara categorias.Nas Propriedades do
DataGridView, escolha os eventos (o botão de relâmpago) e clique duas vezes no evento de SelectionChanged.
Isso criará um stub no código do formulário principal para um evento ser acionado sempre que houver alteração na seleção de categoria.
Preencha o código do evento:
private void dataGridViewCategories_SelectionChanged(object sender, EventArgs e)
{
if (this.dbContext != null)
{
var category = (Category)this.dataGridViewCategories.CurrentRow.DataBoundItem;
if (category != null)
{
this.dbContext.Entry(category).Collection(e => e.Products).Load();
}
}
}
Neste código, se houver uma sessão ativa (não nula) DbContext, obtemos a instância Category vinculada à linha selecionada atualmente do DataViewGrid. (Isso pode ocorrer null se a linha final do modo de exibição estiver selecionada, que é usada para criar novas categorias.) Se houver uma categoria selecionada, então o DbContext é instruído a carregar os produtos associados a essa categoria. Isto é feito por:
- Obtendo um
EntityEntrypara aCategoryinstância (dbContext.Entry(category)) - Informar ao EF Core que queremos operar na navegação da
Productscoleção desseCategory(.Collection(e => e.Products)) - E, finalmente, dizendo ao EF Core que queremos carregar essa coleção de produtos do banco de dados (
.Load();)
Sugestão
Quando Load é chamado, o EF Core só acessará o banco de dados para carregar os produtos se eles ainda não tiverem sido carregados.
Se o aplicativo for executado novamente, ele deverá carregar os produtos apropriados sempre que uma categoria for selecionada:
Guardar alterações
Finalmente, o botão Salvar pode ser conectado ao EF Core para que todas as alterações feitas nos produtos e categorias sejam salvas no banco de dados.
No designer do formulário principal, selecione o botão Salvar .
Nas Propriedades do
Button, escolha os eventos (o botão relâmpago) e clique duas vezes no evento Click .
Preencha o código do evento:
private void buttonSave_Click(object sender, EventArgs e)
{
this.dbContext!.SaveChanges();
this.dataGridViewCategories.Refresh();
this.dataGridViewProducts.Refresh();
}
Esse código chama SaveChanges no DbContext, que salva quaisquer alterações feitas no banco de dados SQLite. Se nenhuma alteração foi feita, então este é um no-op, e nenhuma chamada de banco de dados é feita. Depois de salvar, os DataGridView controles são atualizados. Isso ocorre porque o EF Core lê valores de chave primária gerados para quaisquer novos produtos e categorias do banco de dados. A chamada Refresh atualiza a exibição com esses valores gerados.
Aplicação final
Aqui está o código completo para o formulário principal:
using Microsoft.EntityFrameworkCore;
using System.ComponentModel;
namespace GetStartedWinForms
{
public partial class MainForm : Form
{
private ProductsContext? dbContext;
public MainForm()
{
InitializeComponent();
}
protected override void OnLoad(EventArgs e)
{
base.OnLoad(e);
this.dbContext = new ProductsContext();
// Uncomment the line below to start fresh with a new database.
// this.dbContext.Database.EnsureDeleted();
this.dbContext.Database.EnsureCreated();
this.dbContext.Categories.Load();
this.categoryBindingSource.DataSource = dbContext.Categories.Local.ToBindingList();
}
protected override void OnClosing(CancelEventArgs e)
{
base.OnClosing(e);
this.dbContext?.Dispose();
this.dbContext = null;
}
private void dataGridViewCategories_SelectionChanged(object sender, EventArgs e)
{
if (this.dbContext != null)
{
var category = (Category)this.dataGridViewCategories.CurrentRow.DataBoundItem;
if (category != null)
{
this.dbContext.Entry(category).Collection(e => e.Products).Load();
}
}
}
private void buttonSave_Click(object sender, EventArgs e)
{
this.dbContext!.SaveChanges();
this.dataGridViewCategories.Refresh();
this.dataGridViewProducts.Refresh();
}
}
}
O aplicativo agora pode ser executado e produtos e categorias podem ser adicionados, excluídos e editados. Observe que, se o botão Salvar for clicado antes de fechar o aplicativo, todas as alterações feitas serão armazenadas no banco de dados e recarregadas quando o aplicativo for reiniciado. Se Salvar não for clicado, todas as alterações serão perdidas quando o aplicativo for reiniciado.
Sugestão
Uma nova categoria ou produto pode ser adicionado a um DataViewControl usando a linha vazia na parte inferior do controle. Uma linha pode ser excluída selecionando-a e pressionando a tecla Del .
Antes de guardar
Depois de guardar
Observe que os valores de chave primária para a categoria e os produtos adicionados são preenchidos quando Salvar é clicado.