Sdílet prostřednictvím


Začínáme s Windows Forms

Tento podrobný návod ukazuje, jak vytvořit jednoduchou aplikaci Windows Forms (WinForms) s podporou databáze SQLite. Aplikace používá Entity Framework Core (EF Core) k načtení dat z databáze, sledování změn provedených v těchto datech a zachování těchto změn zpět do databáze.

Snímky obrazovky a výpisy kódu v tomto návodu pocházejí ze sady Visual Studio 2022 17.3.0.

Návod

Ukázku pro tento článek najdete na GitHubu.

Požadavky

K dokončení tohoto návodu musíte mít nainstalovanou sadu Visual Studio 2022 17.3 nebo novější s vybraným .NET desktopovým pracovním vytížením. Další informace o instalaci nejnovější verze sady Visual Studio naleznete v tématu Instalace sady Visual Studio.

Vytvoření aplikace

  1. Otevřete Visual Studio.

  2. V úvodním okně zvolte Vytvořit nový projekt.

  3. Zvolte Windows Forms aplikaci a poté vyberte Další.

    Vytvořte nový projekt Windows Forms

  4. Na další obrazovce zadejte název projektu, například GetStartedWinForms, a zvolte Další.

  5. Na další obrazovce zvolte verzi .NET, která se má použít. Tento názorný postup byl vytvořen s .NET 7, ale měl by také fungovat s novějšími verzemi.

  6. Zvolte Vytvořit.

Nainstalujte balíčky NuGet EF Core

  1. Klikněte pravým tlačítkem na řešení a zvolte Spravovat balíčky NuGet pro řešení...

    Správa balíčků NuGet pro řešení

  2. Zvolte kartu Procházet a vyhledejte Microsoft.EntityFrameworkCore.Sqlite.

  3. Vyberte balíček Microsoft.EntityFrameworkCore.Sqlite.

  4. Zkontrolujte projekt GetStartedWinForms v pravém podokně.

  5. Zvolte nejnovější verzi. Pokud chcete použít předběžnou verzi, ujistěte se, že je zaškrtnuté políčko Zahrnout předběžné verze .

  6. Klikněte na Nainstalovat.

    Instalace balíčku Microsoft.EntityFrameworkCore.Sqlite

Poznámka:

Microsoft.EntityFrameworkCore.Sqlite je balíček "poskytovatel databáze" pro použití EF Core s databází SQLite. Podobné balíčky jsou k dispozici pro jiné databázové systémy. Při instalaci balíčku zprostředkovatele databáze se automaticky zobrazí všechny závislosti potřebné k použití EF Core s tímto databázovým systémem. To zahrnuje základní balíček Microsoft.EntityFrameworkCore .

Definování modelu

V tomto názorném postupu implementujeme model pomocí kódu First. To znamená, že EF Core vytvoří databázové tabulky a schéma založené na definovaných třídách jazyka C#. Informace o tom, jak místo toho použít existující databázi, najdete v tématu Správa schémat databáze.

  1. Klikněte pravým tlačítkem myši na projekt a zvolte Přidat, potom Třídu... a přidejte novou třídu.

    Přidání nové třídy

  2. Použijte název souboru Product.cs a nahraďte kód třídy takto:

    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. Opakujte vytvoření Category.cs s následujícím kódem:

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

Vlastnost Products třídy Category a Category vlastnosti třídy Product se nazývají "navigace". Navigace v EF Core definují vztah mezi dvěma typy entit. V tomto případě navigace odkazuje na kategorii, Product.Category do které daný produkt patří. Category.Products Podobně navigace v kolekci obsahuje všechny produkty pro danou kategorii.

Návod

Při použití Windows Forms lze ObservableCollectionListSource, který implementuje IListSource, použít pro navigaci v kolekci. To není nutné, ale vylepšuje prostředí obousměrných datových vazeb.

Definování DbContext

V EF Core se třída odvozená z DbContext používá ke konfiguraci typů entit v modelu a funguje jako session pro interakci s databází. V nejjednodušším případě je třída DbContext.

  • Obsahuje DbSet vlastnosti pro každý typ entity v modelu.
  • Přepíše metodu OnConfiguring, aby nakonfigurovala zprostředkovatele databáze a připojovací řetězec, který se má použít. Další informace najdete v tématu Konfigurace DbContext .

V tomto případě dbContext třída také přepíše metodu OnModelCreating , aby poskytovala ukázková data pro aplikaci.

Přidejte do projektu novou ProductsContext.cs třídu s následujícím kódem:

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

Ujistěte se, že v tuto chvíli sestavíte řešení.

Přidání ovládacích prvků do formuláře

Aplikace zobrazí seznam kategorií a seznam produktů. Když je v prvním seznamu vybraná kategorie, změní se druhý seznam tak, aby zobrazoval produkty pro danou kategorii. Tyto seznamy lze upravit tak, aby přidávaly, odebíraly nebo upravovaly produkty a kategorie a tyto změny lze uložit do databáze SQLite kliknutím na tlačítko Uložit .

  1. Změňte název hlavního formuláře z Form1 na MainForm.

    Přejmenování formuláře 1 na MainForm

  2. A změňte název na Produkty a kategorie.

    Pojmenujte hlavní formulář jako

  3. Pomocí panelu nástrojů přidejte dva DataGridView ovládací prvky uspořádané vedle sebe.

    Přidání DataGridView

  4. Ve vlastnostech prvního DataGridViewzměňte název na dataGridViewCategories.

  5. Ve Vlastnostech pro druhý DataGridView změňte Název na dataGridViewProducts.

  6. Pomocí panelu nástrojů přidejte Button také ovládací prvek.

  7. Pojmenujte tlačítko buttonSave a dejte mu text "Uložit". Formulář by měl vypadat nějak takto:

    Rozložení formuláře

Datová vazba

Dalším krokem je připojení Product a Category typů z modelu k ovládacím prvkům DataGridView . Tím se sváže data načtená ef Core s ovládacími prvky, aby entity sledované EF Core byly synchronizované s těmi, které se zobrazují v ovládacích prvcích.

  1. Klikněte na Glyph akce návrháře na první DataGridView. Toto je malé tlačítko v pravém horním rohu ovládacího prvku.

    Glyph akce návrháře

  2. Tímto se otevře Seznam akcí, odkud lze přistoupit k rozevíracímu seznamu Pro výběr zdroje dat. Ještě jsme nevytvořili zdroj dat, takže přejděte do dolní části a zvolte Přidat nový zdroj dat objektu....

    Přidání nového zdroje dat objektu

  3. Zvolte Kategorie , chcete-li vytvořit zdroj dat objektu pro kategorie, a klepněte na tlačítko OK.

    Zvolte typ datového zdroje Kategorie.

    Návod

    Pokud se zde nezobrazují žádné typy zdrojů dat, ujistěte se, že Product.cs, Category.cs a ProductsContext.cs byly přidány do projektu a že řešení bylo sestaveno.

  4. Teď rozevírací seznam Zvolit zdroj dat obsahuje právě vytvořený zdroj dat objektu. Rozbalte další zdroje dat, pak zdroje dat projektu a zvolte Kategorie.

    Zvolit kategorii zdroje dat

    Druhá DataGridView bude vázána na produkty. Místo toho, aby byla vázána na Product jako typ nejvyšší úrovně, bude připojena k Products navigaci z Category vazby prvního DataGridView. To znamená, že když je kategorie vybrána v prvním zobrazení, produkty pro tuto kategorii se automaticky použijí ve druhém zobrazení.

  5. Pomocí ikony akce návrháře Glyph na druhé DataGridView, zvolte Zvolit Zdroj dat, pak rozbalte categoryBindingSource a zvolte Products.

    Vyberte zdroj dat Produktů

Konfigurace zobrazení obsahu

Ve výchozím nastavení je v DataGridView vytvořen sloupec pro každou vlastnost vázaných typů. Také hodnoty pro každou z těchto vlastností může uživatel upravit. Některé hodnoty, například hodnoty primárního klíče, jsou ale koncepčně jen pro čtení a proto by se neměly upravovat. Některé vlastnosti, například vlastnost cizího CategoryId klíče a Category navigace, nejsou užitečné pro uživatele, a proto by měly být skryté.

Návod

Vlastnosti primárního klíče je běžné skrýt v reálné aplikaci. Jsou zde viditelné, aby bylo možné snadno zjistit, co EF Core dělá na pozadí.

  1. Klikněte pravým tlačítkem myši na první DataGridView a zvolte Upravit sloupce....

    Úprava sloupců DataGridView

  2. Nastavte CategoryId sloupec, který představuje primární klíč, jen pro čtení a klikněte na tlačítko OK.

    Nastavit sloupec CategoryId jen pro čtení

  3. Klikněte pravým tlačítkem myši na druhý DataGridView a zvolte Upravit sloupce.... Nastavte sloupec ProductId jako jen pro čtení a odeberte sloupce CategoryId a Category, pak klikněte na OK.

    Nastavení sloupce ProductId jen pro čtení a odebrání sloupců CategoryId a Category

Připojení k EF Core

Aplikace teď potřebuje k připojení EF Core k ovládacím prvkům vázaným na data malé množství kódu.

  1. MainForm Otevřete kód tak, že kliknete pravým tlačítkem myši na soubor a zvolíte Zobrazit kód.

    Zobrazit kód

  2. Přidejte soukromé pole, které bude držet DbContext pro relaci, a přidejte přepsání pro metody OnLoad a OnClosing. Kód by měl vypadat takto:

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

Metoda OnLoad je volána při načtení formuláře. V tuto chvíli

  • Vytvoří se instance ProductsContext , která se použije k načtení a sledování změn produktů a kategorií zobrazených aplikací.
  • EnsureCreated je volána na DbContext k vytvoření databáze SQLite, pokud ještě neexistuje. Jedná se o rychlý způsob, jak vytvořit databázi při vytváření prototypů nebo testování aplikací. Pokud se ale model změní, bude potřeba databázi odstranit, aby ji bylo možné vytvořit znovu. (Řádek EnsureDeleted lze odkomentovat pro snadné odstranění a opětovné vytvoření databáze při spuštění aplikace.) Mohli byste místo toho chtít použít Migrace EF Core k úpravě a aktualizaci schématu databáze bez ztráty dat.
  • EnsureCreated naplní také novou databázi daty definovanými v ProductsContext.OnModelCreating metodě.
  • Metoda Load rozšíření se používá k načtení všech kategorií z databáze do DbContext. Tyto entity budou nyní sledovány uživatelem DbContext, což rozpozná všechny změny provedené při úpravách kategorií uživatelem.
  • Vlastnost categoryBindingSource.DataSource je inicializována na kategorie, které sleduje DbContext. To se provádí voláním vlastnosti CategoriesDbSet na Local.ToBindingList(). Local poskytuje přístup k místnímu zobrazení sledovaných kategorií, přičemž události jsou připojeny, aby se zajistila synchronizace místních dat se zobrazenými daty a naopak. ToBindingList()zveřejňuje tato data jako IBindingList, která je kompatibilní s datovou vazbou Windows Forms.

Metoda OnClosing je volána při zavření formuláře. V tomto okamžiku je DbContext uvolněno, což zajišťuje, že všechny prostředky databáze budou uvolněny, a pole dbContext je nastaveno na hodnotu null, aby nemohlo být znovu použito.

Doplnění zobrazení produktů

Pokud je aplikace v tomto okamžiku spuštěná, měla by vypadat přibližně takto:

První spuštění aplikace

Všimněte si, že kategorie byly načteny z databáze, ale tabulka produktů zůstává prázdná. Tlačítko Uložit také nefunguje.

Pokud chcete naplnit tabulku produktů, ef Core musí načíst produkty z databáze pro vybranou kategorii. Chcete-li toho dosáhnout:

  1. V návrháři hlavního formuláře vyberte DataGridView kategorií.

  2. V části Vlastnosti pro DataGridView vyberte události (tlačítko blesku) a dvojitým kliknutím otevřete událost SelectionChanged.

    Přidání události SelectionChanged

    Tím se v kódu hlavního formuláře vytvoří zástupný prvek pro aktivaci události, kdykoli dojde ke změně výběru kategorie.

  3. Vyplňte kód události:

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

Pokud je v tomto kódu aktivní (nenulová) relace DbContext, pak získáme instanci vázanou k aktuálně vybranému řádku v DataViewGridCategory. (To může být null , pokud je vybrán poslední řádek v zobrazení, který slouží k vytvoření nových kategorií.) Pokud je vybraná kategorie, DbContext zobrazí se pokyn k načtení produktů přidružených k této kategorii. K tomu slouží:

  • EntityEntry Získání Category instance (dbContext.Entry(category))
  • Dáme EF Core vědět, že chceme pracovat na kolekční navigaci tohoto Category (.Collection(e => e.Products))
  • A nakonec ef Core říkáme, že chceme načíst kolekci produktů z databáze (.Load();)

Návod

Když je volána funkce Load, EF Core přistoupí k databázi, aby načetl produkty, pouze pokud ještě nebyly načteny.

Pokud je aplikace spuštěná znovu, měla by se načíst příslušné produkty vždy, když je vybrána kategorie:

Produkty jsou načteny

Ukládání změn

Nakonec můžete tlačítko Uložit připojit k EF Core, aby se všechny změny provedené v produktech a kategoriích uložily do databáze.

  1. V návrháři hlavního formuláře vyberte tlačítko Uložit .

  2. Ve vlastnostech pro Button, zvolte události (tlačítko s bleskem) a dvojitě klikněte na událost Click.

    Přidejte událost kliknutí pro uložení

  3. Vyplňte kód události:

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

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

Tento kód volá SaveChanges na DbContext, což provede uložení všech změn v databázi SQLite. Pokud nebyly provedeny žádné změny, jedná se o operace typu no-op a nedojde k žádnému volání databáze. Po uložení se ovládací prvky DataGridView aktualizují. Důvodem je to, že EF Core čte hodnoty primárního klíče pro všechny nové produkty a kategorie z databáze. Volání Refresh aktualizuje zobrazení těmito vygenerovanými hodnotami.

Konečná aplikace

Tady je úplný kód hlavního formuláře:

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

Aplikace se teď dá spustit a produkty a kategorie se dají přidávat, odstraňovat a upravovat. Všimněte si, že pokud tlačítko Uložit kliknete před zavřením aplikace, všechny provedené změny budou uloženy v databázi a znovu načteny při opětovném spuštění aplikace. Pokud na tlačítko Uložit není kliknuto, při opětovném spuštění aplikace dojde ke ztrátě změn.

Návod

Do DataViewControl lze novou kategorii nebo produkt přidat pomocí prázdného řádku ve spodní části ovládacího prvku. Řádek můžete odstranit tak, že ho vyberete a stisknete klávesu Del .

Před uložením

Spuštěná aplikace před kliknutím na Uložit

Po uložení

Spuštěná aplikace po kliknutí na Uložit

Všimněte si, že hodnoty primárního klíče pro přidanou kategorii a produkty se vyplní po kliknutí na tlačítko Uložit .

Další informace