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
Otevřete sadu Visual Studio.
V úvodním okně zvolte Vytvořit nový projekt.
Zvolte model Windows Forms Aplikace a pak zvolte Další.
Na další obrazovce zadejte název projektu, například GetStartedWinForms, a zvolte Další.
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.
Zvolte Vytvořit.
Instalace balíčků NUGet EF Core
Klikněte pravým tlačítkem na řešení a zvolte Spravovat balíčky NuGet pro řešení...
Zvolte kartu Procházet a vyhledejte Microsoft.EntityFrameworkCore.Sqlite.
Vyberte balíček Microsoft.EntityFrameworkCore.Sqlite.
Zkontrolujte projekt GetStartedWinForms v pravém podokně.
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 .
Klikněte na Nainstalovat.
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.
Klikněte pravým tlačítkem myši na projekt a zvolte Přidat, potom Třídu... a přidejte novou třídu.
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!; }
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 ObservableCollectionListSource
pro 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 .
Změňte název hlavního formuláře z
Form1
naMainForm
.A změňte název na Produkty a kategorie.
Pomocí panelu nástrojů přidejte dva
DataGridView
ovládací prvky uspořádané vedle sebe.Ve vlastnostech prvního
DataGridView
změňte název nadataGridViewCategories
.Ve vlastnostech pro sekundu
DataGridView
změňte název nadataGridViewProducts
.Pomocí panelu nástrojů přidejte
Button
také ovládací prvek.Pojmenujte tlačítko
buttonSave
a dejte mu text "Uložit". Formulář by měl vypadat nějak takto:
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.
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.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....
Zvolte Kategorie , chcete-li vytvořit zdroj dat objektu pro kategorie, a klepněte na tlačítko OK.
Tip
Pokud se zde nezobrazují žádné typy zdrojů dat, ujistěte se, že
Product.cs
byly přidány do projektu a řešení bylo vytvořeno.ProductsContext.cs
Category.cs
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.
Druhá
DataGridView
bude vázána na produkty. Místo vazby na typ nejvyšší úrovněProduct
však bude vázána naProducts
navigaci zCategory
vazby prvníhoDataGridView
. To znamená, že když je kategorie vybrána v prvním zobrazení, produkty pro tuto kategorii se automaticky použijí ve druhém zobrazení.Pomocí akce návrháře Glyph na druhé
DataGridView
, zvolte Zvolit zdroj dat, pak rozbaltecategoryBindingSource
a zvolteProducts
.
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í.
Klikněte pravým tlačítkem myši na první
DataGridView
a zvolte Upravit sloupce....Nastavte
CategoryId
sloupec, který představuje primární klíč, jen pro čtení a klikněte na tlačítko OK.Klikněte pravým tlačítkem myši na druhý
DataGridView
a zvolte Upravit sloupce.... Nastavte sloupec jen proProductId
čtení a odeberteCategoryId
sloupce aCategory
klikněte na tlačítko OK.
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.
MainForm
Otevřete kód tak, že kliknete pravým tlačítkem myši na soubor a zvolíte Zobrazit kód.Přidejte soukromé pole, které bude obsahovat
DbContext
relaci, a přidejte přepsání pro metodyOnLoad
aOnClosing
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 proDbContext
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. (ŘádekEnsureDeleted
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 vProductsContext.OnModelCreating
metodě.- Metoda
Load
rozšíření se používá k načtení všech kategorií z databáze doDbContext
. Tyto entity budou nyní sledovány uživatelemDbContext
, 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ányDbContext
. To se provádí volánímLocal.ToBindingList()
vlastnostiCategories
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 datovouIBindingList
vazbu, 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:
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:
V návrháři hlavního formuláře vyberte
DataGridView
kategorie.V okně Vlastnosti vyberte
DataGridView
události (tlačítko blesku) a poklikejte na událost 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.
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étoCategory
(.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:
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.
V návrháři hlavního formuláře vyberte tlačítko Uložit .
Ve vlastnostech
Button
zvolte události (tlačítko blesku) a poklikejte na událost Kliknutí.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
DbContext
kó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
Po uložení
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 .