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ší.

    Create a new Windows Forms project

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

    Manage NuGet Packages for Solution

  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.

    Install the Microsoft.EntityFrameworkCore.Sqlite package

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.

    Add new class

  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.

    Rename Form1 to MainForm

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

    Title MainForm as

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

    Add 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:

    Form layout

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.

    The Designer Action Glyph

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

    Add new Object Data Source

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

    Choose Category data source type

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

    Choose Category data source

    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.

    Choose Products data source

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

    Edit DataGridView columns

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

    Make CategoryId column read-only

  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.

    Make ProductId column read-only and remove CategoryId and Category columns

Připojení pro 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.

    View Code

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

    Add the SelectionChanged event

    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ží:

  • EntityEntryCategory 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:

Products are loaded

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 vlastnostechButtonzvolte události (tlačítko blesku) a poklikejte na událost Kliknutí.

    Add the Click event for Save

  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á SaveChangesDbContextkó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

The running application before clicking Save

Po uložení

The running application after clicking Save

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