Sdílet prostřednictvím


Začínáme s Windows Forms

Tento podrobný návod ukazuje, jak vytvořit jednoduchou aplikaci model Windows Forms (WinForms) podporovanou databází 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.

Tip

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 vybranou úlohou .NET Desktop. Další informace o instalaci nejnovější verze sady Visual Studio naleznete v tématu Instalace sady Visual Studio.

Vytvoření aplikace

  1. Otevřete sadu Visual Studio.

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

  3. Zvolte model Windows Forms Aplikace a pak zvolte Další.

    Vytvoření nového projektu model 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.

Instalace balíčků 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.

Tip

Při použití model Windows Forms lze ObservableCollectionListSourcepro navigaci v kolekci použít příkaz , který implementujeIListSource. 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 ní používá ke konfiguraci typů entit v modelu a funguje jako relace pro interakci s databází. V nejjednodušším případě DbContext třída:

  • Obsahuje DbSet vlastnosti pro každý typ entity v modelu.
  • Přepíše metodu OnConfiguring tak, aby nakonfigurovali zprostředkovatele databáze a připojovací řetězec 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 řešení v tuto chvíli sestavíte .

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.

    Hlavníformulář nadpisu 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 sekundu DataGridViewzměň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. Otevře se seznam akcí, ze kterého je možné získat přístup 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 zdroje dat Kategorie.

    Tip

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

  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 zdroj dat Kategorie

    Druhá DataGridView bude vázána na produkty. Místo vazby na typ nejvyšší úrovně Product však bude vázána na 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í akce návrháře Glyph na druhé DataGridView, zvolte Zvolit zdroj dat, pak rozbalte categoryBindingSource a zvolte Products.

    Výběr zdroje dat Products

Konfigurace zobrazeného zobrazení

Ve výchozím nastavení se ve sloupci vytvoří DataGridView 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é.

Tip

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 jen pro ProductId čtení a odeberte CategoryId sloupce a Category klikněte na tlačítko 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 obsahovat DbContext relaci, a přidejte přepsání pro metody OnLoad a OnClosing metody. 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 pro DbContext 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 se nedá okomentovat a snadno odstranit a znovu vytvořit databázi při spuštění aplikace.) Místo toho můžete 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 do kategorií, které jsou sledovány DbContext. To se provádí voláním Local.ToBindingList() vlastnosti Categories DbSet . 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 datovou IBindingListvazbu, která je srozumitelná model Windows Forms datovou vazbou.

Metoda OnClosing je volána při zavření formuláře. V tuto chvíli je uvolněna, DbContext což zajišťuje uvolnění všech prostředků databáze a dbContext pole je nastaveno na hodnotu null, aby ho nelze znovu použít.

Naplnění zobrazení Produkty

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

Fist run of the application

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 kategorie.

  2. V okně Vlastnosti vyberte DataGridViewudálosti (tlačítko blesku) a poklikejte na událost SelectionChanged .

    Přidání události SelectionChanged

    Tím se v kódu hlavního formuláře vytvoří zástupný kód události, která se aktivuje při každé 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í relace (nenulová), DbContext získáme instanci vázanou Category na aktuálně vybraný řádek DataViewGrid. (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 Category Získání instance (dbContext.Entry(category))
  • Dáme EF Core vědět, že chceme pracovat s Products navigací kolekce této Category (.Collection(e => e.Products))
  • A nakonec ef Core říkáme, že chceme načíst kolekci produktů z databáze (.Load();)

Tip

Když Load se volá, EF Core bude přistupovat k databázi, aby načetl produkty, 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 Buttonzvolte události (tlačítko blesku) a poklikejte na událost Kliknutí.

    Přidání události 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 DbContextkód , který ukládá všechny změny provedené 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í DataGridView se ovládací prvky 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.

Tip

Novou kategorii nebo produkt lze přidat do DataViewControl prázdného řádku v dolní čá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