Delen via


Aan de slag met Windows Forms

In dit stapsgewijze overzicht ziet u hoe u een eenvoudige Windows Forms-toepassing (WinForms) bouwt die wordt ondersteund door een SQLite-database. De toepassing maakt gebruik van Entity Framework Core (EF Core) om gegevens uit de database te laden, wijzigingen in die gegevens bij te houden en deze wijzigingen weer in de database te behouden.

De schermafbeeldingen en codevermeldingen in dit overzicht worden genomen vanuit Visual Studio 2022 17.3.0.

Vereiste voorwaarden

U moet Visual Studio 2022 17.3 of hoger hebben geïnstalleerd met de .NET-desktopworkload geselecteerd om deze handleiding te voltooien. Zie Visual Studio installeren voor meer informatie over het installeren van de nieuwste versie van Visual Studio.

De toepassing maken

  1. Visual Studio openen

  2. Kies nieuw project maken in het startvenster.

  3. Kies Windows Forms-app en klik vervolgens op Volgende.

    Een nieuw Windows Forms-project maken

  4. Geef in het volgende scherm een naam op voor het project, bijvoorbeeld GetStartedWinForms, en kies Volgende.

  5. Kies in het volgende scherm de .NET-versie die u wilt gebruiken. Deze handleiding is gemaakt met .NET 7, maar zal ook werken met latere versies.

  6. Kies Maken.

De EF Core NuGet-pakketten installeren

  1. Klik met de rechtermuisknop op de oplossing en kies NuGet-pakketten beheren voor oplossing...

    NuGet-pakketten voor oplossing beheren

  2. Kies het tabblad Bladeren en zoek naar Microsoft.EntityFrameworkCore.Sqlite.

  3. Selecteer het pakket Microsoft.EntityFrameworkCore.Sqlite .

  4. Controleer het project GetStartedWinForms in het rechterdeelvenster.

  5. Kies de nieuwste versie. Als u een voorlopige versie wilt gebruiken, moet u ervoor zorgen dat het selectievakje Voorlopige versie opnemen is ingeschakeld.

  6. Klik op Installeren

    Het Microsoft.EntityFrameworkCore.Sqlite-pakket installeren

Opmerking

Microsoft.EntityFrameworkCore.Sqlite is het pakket 'databaseprovider' voor het gebruik van EF Core met een SQLite-database. Vergelijkbare pakketten zijn beschikbaar voor andere databasesystemen. Als u een databaseproviderpakket installeert, worden automatisch alle afhankelijkheden opgenomen die nodig zijn voor het gebruik van EF Core met dat databasesysteem. Dit omvat het basispakket Microsoft.EntityFrameworkCore .

Een model definiëren

In dit scenario implementeren we een model met behulp van Code First. Dit betekent dat EF Core de databasetabellen en het schema maakt op basis van de C#-klassen die u definieert. Zie Databaseschema's beheren om te zien hoe u in plaats daarvan een bestaande database gebruikt.

  1. Klik met de rechtermuisknop op project en kies Toevoegen en vervolgens Klasse... om een nieuwe klasse toe te voegen.

    Nieuwe klasse toevoegen

  2. Gebruik de bestandsnaam Product.cs en vervang de code voor de klasse door:

    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!;
    }
    
  3. Herhaal om Category.cs te maken met de volgende code:

    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();
    }
    

De Products eigenschap van de Category klasse en de Category eigenschap in de Product klasse worden 'navigatie' genoemd. In EF Core definiëren navigatie een relatie tussen twee entiteitstypen. In dit geval verwijst de Product.Category navigatie naar de categorie waartoe een bepaald product behoort. Op dezelfde manier bevat de Category.Products verzamelingsnavigatie alle producten voor een bepaalde categorie.

Aanbeveling

Wanneer u Windows Forms gebruikt, kan de ObservableCollectionListSource, die IListSource implementeert, worden gebruikt voor verzamelingsnavigaties. Dit is niet nodig, maar verbetert wel de tweerichtingsbindingservaring voor gegevens.

DbContext definiëren

In EF Core wordt een klasse die is afgeleid van DbContext , gebruikt om entiteitstypen in een model te configureren en te fungeren als een sessie voor interactie met de database. In het eenvoudigste geval, een DbContext klasse:

  • Bevat DbSet eigenschappen voor elk entiteitstype in het model.
  • Overschrijft de OnConfiguring-methode om de databaseprovider en verbindingstekst die moet worden gebruikt te configureren. Zie Een DbContext configureren voor meer informatie.

In dit geval overschrijft de DbContext-klasse ook de OnModelCreating methode om enkele voorbeeldgegevens voor de toepassing op te geven.

Voeg een nieuwe ProductsContext.cs klasse toe aan het project met de volgende code:

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" });
    }
}

Zorg ervoor dat u de oplossing op dit moment bouwt.

Besturingselementen toevoegen aan het formulier

In de toepassing wordt een lijst met categorieën en een lijst met producten weergegeven. Wanneer een categorie in de eerste lijst is geselecteerd, wordt de tweede lijst gewijzigd om producten voor die categorie weer te geven. Deze lijsten kunnen worden gewijzigd om producten en categorieën toe te voegen, te verwijderen of te bewerken. Deze wijzigingen kunnen worden opgeslagen in de SQLite-database door op een knop Opslaan te klikken.

  1. Wijzig de naam van het hoofdformulier in Form1MainForm.

    De naam van Form1 wijzigen in MainForm

  2. En wijzig de titel in 'Producten en categorieën'.

    Titel MainForm als

  3. Voeg met behulp van de Werkset twee DataGridView besturingselementen toe, gerangschikt naast elkaar.

    DataGridView toevoegen

  4. In de eigenschappen voor de eerste DataGridView, wijzig de naam in dataGridViewCategories.

  5. Wijzig in de eigenschappen de DataGridView van de tweede naar dataGridViewProducts.

  6. Gebruik ook de Toolbox om een Button controle toe te voegen.

  7. Geef de knop buttonSave een naam en geef deze de tekst 'Opslaan'. Het formulier ziet er ongeveer als volgt uit:

    Formulierindeling

Gegevensbinding

De volgende stap bestaat uit het verbinden van de Product en Category typen van het model met de DataGridView besturingselementen. Hiermee worden de gegevens die door EF Core worden geladen, gekoppeld aan de besturingselementen, zodat de entiteiten die door EF Core worden bijgehouden, gesynchroniseerd blijven met de entiteiten die in de besturingselementen worden weergegeven.

  1. Klik op de Designer-actieglyph op de eerste DataGridView. Dit is de kleine knop in de rechterbovenhoek van het besturingselement.

    De actie-glyph van de ontwerpfunctie

  2. Hiermee opent u de actielijst, waaruit de vervolgkeuzelijst voor de gekozen gegevensbron kan worden geopend. We hebben nog geen gegevensbron gemaakt, dus ga naar de onderkant en kies Nieuwe objectgegevensbron toevoegen....

    Nieuwe objectgegevensbron toevoegen

  3. Kies Categorie om een objectgegevensbron voor categorieën te maken en klik op OK.

    Gegevensbrontype voor categorieën kiezen

    Aanbeveling

    Als hier geen gegevensbrontypen worden weergegeven, moet u ervoor zorgen dat Product.csen Category.csProductsContext.cs zijn toegevoegd aan het project en dat de oplossing is gebouwd.

  4. Nu bevat de vervolgkeuzelijst Gegevensbron kiezen de objectgegevensbron die we zojuist hebben gemaakt. Vouw Andere gegevensbronnen uit, vervolgens Projectgegevensbronnen en kies Categorie.

    Categoriegegevensbron kiezen

    De tweede DataGridView is gebonden aan producten. Maar in plaats van een binding met het type op het hoogste niveau Product , wordt het in plaats daarvan gebonden aan de Products navigatie vanuit de Category binding van de eerste DataGridView. Dit betekent dat wanneer een categorie in de eerste weergave is geselecteerd, de producten voor die categorie automatisch worden gebruikt in de tweede weergave.

  5. Kies met behulp van het Designer Actiepictogram op de tweede DataGridView, Gegevensbron kiezen, vouw vervolgens het categoryBindingSource uit en kies Products.

    Gegevensbron Producten kiezen

Configureren wat wordt weergegeven

Standaard wordt er een kolom gemaakt in de DataGridView voor elke eigenschap van de afhankelijke typen. De waarden voor elk van deze eigenschappen kunnen ook door de gebruiker worden bewerkt. Sommige waarden, zoals de primaire-sleutelwaarden, zijn echter conceptueel alleen-lezen en moeten dus niet worden bewerkt. Bovendien zijn sommige eigenschappen, zoals de CategoryId buitenlandse sleutel-eigenschap en de Category navigatie, niet nuttig voor de gebruiker en moeten dus worden verborgen.

Aanbeveling

Het is gebruikelijk om primaire sleuteleigenschappen in een echte toepassing te verbergen. Ze blijven hier zichtbaar om te zien wat EF Core achter de schermen doet.

  1. Klik met de rechtermuisknop op de eerste DataGridView en kies Kolommen bewerken....

    DataGridView-kolommen bewerken

  2. Maak de kolom CategoryId, die de primaire sleutel vertegenwoordigt, alleen-lezen en klik op OK.

    Categorie-id-kolom alleen-lezen maken

  3. Klik met de rechtermuisknop op de tweede DataGridView en kies Kolommen bewerken.... Maak de ProductId kolom alleen-lezen en verwijder de CategoryId en Category kolommen en klik op OK.

    Kolom Product-id alleen-lezen maken en Categorie-id en Categoriekolommen verwijderen

Verbinding maken met EF Core

De toepassing heeft nu een kleine hoeveelheid code nodig om EF Core te verbinden met de gegevensgebonden besturingselementen.

  1. Open de MainForm code door met de rechtermuisknop op het bestand te klikken en Code weergeven te kiezen.

    Code weergeven

  2. Voeg een privéveld toe om de DbContext voor de sessie te bewaren en voeg overschrijvingen toe voor de OnLoad en OnClosing methoden. De code moet er als volgt uitzien:

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;
        }
    }
}

De OnLoad methode wordt aangeroepen wanneer het formulier wordt geladen. Op dit moment

  • Een instantie van de ProductsContext wordt gemaakt om producten en categorieën te laden en wijzigingen bij te houden die door de toepassing worden weergegeven.
  • EnsureCreated wordt aangeroepen DbContext om de SQLite-database te maken als deze nog niet bestaat. Dit is een snelle manier om een database te maken bij het maken van prototypen of het testen van toepassingen. Als het model echter wordt gewijzigd, moet de database worden verwijderd, zodat deze opnieuw kan worden gemaakt. (De EnsureDeleted regel kan ongecommentarieerd zijn om de database eenvoudig te verwijderen en opnieuw te maken wanneer de toepassing wordt uitgevoerd.) U kunt in plaats daarvan EF Core-migraties gebruiken om het databaseschema te wijzigen en bij te werken zonder gegevens te verliezen.
  • EnsureCreated vult ook de nieuwe database met de gegevens die in de ProductsContext.OnModelCreating methode zijn gedefinieerd.
  • De Load extensiemethode wordt gebruikt om alle categorieën uit de database in de DbContextdatabase te laden. Deze entiteiten worden nu bijgehouden door de DbContextentiteit , waarmee wijzigingen worden gedetecteerd die zijn aangebracht wanneer de categorieën door de gebruiker worden bewerkt.
  • De categoryBindingSource.DataSource eigenschap wordt geïnitialiseerd voor de categorieën die worden bijgehouden door de DbContext. Dit wordt gedaan door de Local.ToBindingList()Categories eigenschap aan te roepenDbSet. Local biedt toegang tot een lokale weergave van de bijgehouden categorieën, waarbij gebeurtenissen zijn gekoppeld om ervoor te zorgen dat de lokale gegevens gesynchroniseerd blijven met de weergegeven gegevens en omgekeerd. ToBindingList() maakt deze gegevens beschikbaar als een IBindingList, die wordt begrepen door windows Forms-gegevensbinding.

De OnClosing methode wordt aangeroepen wanneer het formulier wordt gesloten. Op dit moment wordt de DbContext database verwijderd, waardoor alle databasebronnen worden vrijgemaakt en het dbContext veld is ingesteld op null, zodat het niet opnieuw kan worden gebruikt.

Productenweergave vullen

Als de toepassing op dit moment wordt gestart, ziet deze er ongeveer als volgt uit:

Eerste uitvoering van de toepassing

U ziet dat de categorieën uit de database zijn geladen, maar de tabel producten leeg blijft. De knop Opslaan werkt ook niet.

Als u de tabel producten wilt vullen, moet EF Core producten laden uit de database voor de geselecteerde categorie. U kunt dit als volgt bereiken:

  1. Selecteer in de ontwerpfunctie voor het hoofdformulier de DataGridView optie voor categorieën.

  2. Kies in de Eigenschappen van de DataGridView de gebeurtenissen (de bliksemknop) en dubbelklik op de SelectionChanged gebeurtenis.

    De gebeurtenis SelectionChanged toevoegen

    Hiermee maakt u stub in de hoofdformuliercode om een gebeurtenis te activeren wanneer de categorieselectie wordt gewijzigd.

  3. Vul de code voor de gebeurtenis in:

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();
        }
    }
}

In deze code, als er een actieve (niet-null) DbContext -sessie is, verkrijgen we het Category exemplaar dat is gebonden aan de momenteel geselecteerde rij van de DataViewGrid. (Dit kan zijn null als de laatste rij in de weergave is geselecteerd, die wordt gebruikt om nieuwe categorieën te maken.) Als er een geselecteerde categorie is, wordt de DbContext instructie gegeven om de producten te laden die aan die categorie zijn gekoppeld. Dit wordt gedaan door:

  • Een EntityEntry verkrijgen voor het Category exemplaar (dbContext.Entry(category))
  • EF Core laten weten dat we willen werken met de Products verzamelingnavigatie van die Category (.Collection(e => e.Products))
  • En ten slotte ef Core vertellen dat we die verzameling producten uit de database willen laden (.Load();)

Aanbeveling

Wanneer Load wordt aangeroepen, zal EF Core alleen toegang hebben tot de database om de producten te laden als ze nog niet zijn ingeladen.

Als de toepassing nu opnieuw wordt uitgevoerd, moet deze de juiste producten laden wanneer een categorie wordt geselecteerd:

Producten worden geladen

Wijzigingen opslaan

Ten slotte kan de knop Opslaan worden verbonden met EF Core, zodat eventuele wijzigingen in de producten en categorieën worden opgeslagen in de database.

  1. Selecteer in de ontwerpfunctie voor het hoofdformulier de knop Opslaan .

  2. Kies in de eigenschappen voor de Button, selecteer de events (de bliksemknop) en dubbelklik op het event Click.

    Voeg het klik-evenement toe voor Opslaan

  3. Vul de code voor de gebeurtenis in:

private void buttonSave_Click(object sender, EventArgs e)
{
    this.dbContext!.SaveChanges();

    this.dataGridViewCategories.Refresh();
    this.dataGridViewProducts.Refresh();
}

SaveChanges wordt aangeroepen op de DbContext door deze code, waarmee alle gemaakte wijzigingen in de SQLite-database worden opgeslagen. Als er geen wijzigingen zijn aangebracht, is dit een no-open wordt er geen database-aanroep uitgevoerd. Nadat er is opgeslagen, worden de DataGridView besturingselementen vernieuwd. Dit komt doordat EF Core gegenereerde primaire-sleutelwaarden leest voor nieuwe producten en categorieën uit de database. Het aanroepen van Refresh werkt de weergave bij met deze gegenereerde waarden.

De laatste toepassing

Dit is de volledige code voor het hoofdformulier:

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();
        }
    }
}

De toepassing kan nu worden uitgevoerd en producten en categorieën kunnen worden toegevoegd, verwijderd en bewerkt. Als op de knop Opslaan wordt geklikt voordat u de toepassing sluit, worden alle aangebrachte wijzigingen opgeslagen in de database en opnieuw geladen wanneer de toepassing opnieuw wordt gestart. Als niet op Opslaan wordt geklikt, gaan eventuele wijzigingen verloren wanneer de toepassing opnieuw wordt gestart.

Aanbeveling

Aan een DataViewControl kan een nieuwe categorie of product worden toegevoegd door gebruik te maken van de lege rij onderaan het besturingselement. U kunt een rij verwijderen door deze te selecteren en op de Del-toets te drukken.

Voordat u opslaat

De actieve toepassing voordat u op Opslaan klikt

Na het opslaan

De actieve toepassing nadat u op Opslaan hebt geklikt

U ziet dat de primaire-sleutelwaarden voor de toegevoegde categorie en producten worden ingevuld wanneer op Opslaan wordt geklikt.

Meer informatie