Memulai Formulir Windows
Panduan langkah demi langkah ini menunjukkan cara membangun aplikasi Formulir Windows sederhana (WinForms) yang didukung oleh database SQLite. Aplikasi ini menggunakan Entity Framework Core (EF Core) untuk memuat data dari database, melacak perubahan yang dilakukan pada data tersebut, dan mempertahankan perubahan tersebut kembali ke database.
Cuplikan layar dan daftar kode dalam panduan ini diambil dari Visual Studio 2022 17.3.0.
Tip
Anda dapat melihat contoh artikel ini di GitHub.
Prasyarat
Anda harus menginstal Visual Studio 2022 17.3 atau yang lebih baru dengan beban kerja desktop .NET yang dipilih untuk menyelesaikan panduan ini. Untuk informasi selengkapnya tentang menginstal Visual Studio versi terbaru, lihat Menginstal Visual Studio.
Membuat Aplikasi
Membuka Visual Studio
Di jendela mulai, pilih Buat proyek baru.
Pilih aplikasi Formulir Windows lalu pilih Berikutnya.
Di layar berikutnya, beri nama proyek, misalnya, GetStartedWinForms, dan pilih Berikutnya.
Di layar berikutnya, pilih versi .NET yang akan digunakan. Panduan ini dibuat dengan .NET 7, tetapi juga harus berfungsi dengan versi yang lebih baru.
Pilih Buat.
Menginstal paket EF Core NuGet
Klik kanan pada solusi dan pilih Kelola Paket NuGet untuk Solusi...
Pilih tab Telusuri dan cari "Microsoft.EntityFrameworkCore.Sqlite".
Pilih paket Microsoft.EntityFrameworkCore.Sqlite .
Periksa proyek GetStartedWinForms di panel kanan.
Pilih versi terbaru. Untuk menggunakan versi pra-rilis, pastikan kotak Sertakan prarilis dicentang.
Klik Instal
Catatan
Microsoft.EntityFrameworkCore.Sqlite adalah paket "penyedia database" untuk menggunakan EF Core dengan database SQLite. Paket serupa tersedia untuk sistem database lainnya. Menginstal paket penyedia database secara otomatis membawa semua dependensi yang diperlukan untuk menggunakan EF Core dengan sistem database tersebut. Ini termasuk paket dasar Microsoft.EntityFrameworkCore .
Tentukan Model
Dalam panduan ini kita akan menerapkan model menggunakan "Kode Pertama". Ini berarti bahwa EF Core akan membuat tabel database dan skema berdasarkan kelas C# yang Anda tentukan. Lihat Mengelola Skema Database untuk melihat cara menggunakan database yang sudah ada sebagai gantinya.
Klik kanan pada proyek dan pilih Tambahkan, lalu Kelas... untuk menambahkan kelas baru.
Gunakan nama
Product.cs
file dan ganti kode untuk kelas dengan: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!; }
Ulangi untuk membuat
Category.cs
dengan kode berikut: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(); }
Properti Products
pada Category
kelas dan Category
properti di Product
kelas disebut "navigasi". Di EF Core, navigasi menentukan hubungan antara dua jenis entitas. Dalam hal ini, Product.Category
navigasi mereferensikan kategori tempat produk tertentu berada. Demikian juga, Category.Products
navigasi koleksi berisi semua produk untuk kategori tertentu.
Tip
Saat menggunakan Formulir Windows, ObservableCollectionListSource
, yang mengimplementasikan IListSource
, dapat digunakan untuk navigasi koleksi. Ini tidak diperlukan, tetapi meningkatkan pengalaman pengikatan data dua arah.
Tentukan DbContext
Di EF Core, kelas yang berasal dari DbContext
digunakan untuk mengonfigurasi jenis entitas dalam model dan bertindak sebagai sesi untuk berinteraksi dengan database. Dalam kasus paling sederhana, kelas DbContext
:
- Berisi
DbSet
properti untuk setiap jenis entitas dalam model. - Mengambil alih
OnConfiguring
metode untuk mengonfigurasi penyedia database dan string koneksi untuk digunakan. Lihat Mengonfigurasi DbContext untuk informasi selengkapnya.
Dalam hal ini, kelas DbContext juga mengambil alih OnModelCreating
metode untuk menyediakan beberapa data sampel untuk aplikasi.
Tambahkan kelas baru ProductsContext.cs
ke proyek dengan kode berikut:
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" });
}
}
Pastikan untuk membangun solusi pada saat ini.
Menambahkan kontrol ke formulir
Aplikasi ini akan menampilkan daftar kategori dan daftar produk. Saat kategori dipilih dalam daftar pertama, maka daftar kedua akan berubah untuk menampilkan produk untuk kategori tersebut. Daftar ini dapat dimodifikasi untuk menambahkan, menghapus, atau mengedit produk dan kategori, dan perubahan ini dapat disimpan ke database SQLite dengan mengklik tombol Simpan .
Ubah nama formulir utama dari
Form1
menjadiMainForm
.Dan ubah judul menjadi "Produk dan Kategori".
Menggunakan Kotak Alat, tambahkan dua
DataGridView
kontrol, disusun di samping satu sama lain.Di Properti untuk yang pertama
DataGridView
, ubah Nama menjadidataGridViewCategories
.Di Properti untuk yang kedua
DataGridView
, ubah Nama menjadidataGridViewProducts
.Juga menggunakan Kotak Alat, tambahkan
Button
kontrol.Beri nama tombol
buttonSave
dan berikan teks "Simpan". Formulir akan terlihat seperti ini:
Pengikatan data
Langkah selanjutnya adalah menyambungkan Product
jenis dan Category
dari model ke DataGridView
kontrol. Ini akan mengikat data yang dimuat oleh EF Core ke kontrol, sehingga entitas yang dilacak oleh EF Core tetap sinkron dengan yang ditampilkan dalam kontrol.
Klik Glyph Tindakan Perancang pada yang pertama
DataGridView
. Ini adalah tombol kecil di sudut kanan atas kontrol.Ini membuka Daftar Tindakan, tempat drop-down untuk Memilih Sumber Data dapat diakses. Kami belum membuat sumber data, jadi buka bagian bawah dan pilih Tambahkan Sumber Data Objek baru....
Pilih Kategori untuk membuat sumber data objek untuk kategori, dan klik OK.
Tip
Jika tidak ada jenis sumber data yang muncul di sini, pastikan bahwa , dan telah ditambahkan ke proyek dan solusi telah dibuat.
ProductsContext.cs
Category.cs
Product.cs
Sekarang drop-down Pilih Sumber Data berisi sumber data objek yang baru saja kita buat. Perluas Sumber Data Lain, lalu Sumber Data Proyek, dan pilih Kategori.
Yang kedua
DataGridView
akan terikat pada produk. Namun, daripada mengikat ke jenis tingkatProduct
atas, sebaliknya akan terikat keProducts
navigasi dariCategory
pengikatan yang pertamaDataGridView
. Ini berarti bahwa ketika kategori dipilih dalam tampilan pertama, produk untuk kategori tersebut akan secara otomatis digunakan dalam tampilan kedua.Menggunakan Glyph Tindakan Perancang pada yang kedua
DataGridView
, pilih Pilih Sumber Data, lalu perluascategoryBindingSource
dan pilihProducts
.
Mengonfigurasi apa yang ditampilkan
Secara default, kolom dibuat di DataGridView
untuk setiap properti dari jenis terikat. Selain itu, nilai untuk setiap properti ini dapat diedit oleh pengguna. Namun, beberapa nilai, seperti nilai kunci utama, secara konseptual baca-saja, sehingga tidak boleh diedit. Selain itu CategoryId
, beberapa properti, seperti properti kunci asing dan Category
navigasi tidak berguna bagi pengguna, sehingga harus disembunyikan.
Tip
Adalah umum untuk menyembunyikan properti kunci utama dalam aplikasi nyata. Mereka dibiarkan terlihat di sini untuk memudahkan untuk melihat apa yang dilakukan EF Core di belakang layar.
Klik kanan pada kolom pertama
DataGridView
dan pilih Edit Kolom....CategoryId
Buat kolom, yang mewakili kunci utama, baca-saja, dan klik OK.Klik kanan pada detik
DataGridView
dan pilih Edit Kolom....ProductId
Buat kolom baca-saja, dan hapusCategoryId
kolom danCategory
, lalu klik OK.
Menyambungkan ke EF Core
Aplikasi sekarang membutuhkan sejumlah kecil kode untuk menghubungkan EF Core ke kontrol terikat data.
MainForm
Buka kode dengan mengklik kanan pada file dan memilih Tampilkan Kode.Tambahkan bidang privat untuk menahan
DbContext
sesi, dan tambahkan penimpaanOnLoad
untuk metode danOnClosing
. Kode akan terlihat seperti ini:
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;
}
}
}
Metode OnLoad
ini dipanggil ketika formulir dimuat. Saat ini
- Instans
ProductsContext
dibuat yang akan digunakan untuk memuat dan melacak perubahan pada produk dan kategori yang ditampilkan oleh aplikasi. EnsureCreated
dipanggil untukDbContext
membuat database SQLite jika belum ada. Ini adalah cara cepat untuk membuat database saat membuat prototipe atau menguji aplikasi. Namun, jika model berubah, maka database perlu dihapus sehingga dapat dibuat lagi.EnsureDeleted
(Baris dapat tidak dikomentari untuk menghapus dan membuat ulang database dengan mudah saat aplikasi dijalankan.) Anda mungkin ingin menggunakan Migrasi Inti EF untuk memodifikasi dan memperbarui skema database tanpa kehilangan data apa pun.EnsureCreated
juga akan mengisi database baru dengan data yang ditentukan dalamProductsContext.OnModelCreating
metode .- Metode
Load
ekstensi digunakan untuk memuat semua kategori dari database keDbContext
dalam . Entitas ini sekarang akan dilacak olehDbContext
, yang akan mendeteksi perubahan apa pun yang dibuat ketika kategori diedit oleh pengguna. - Properti
categoryBindingSource.DataSource
diinisialisasi ke kategori yang sedang dilacak olehDbContext
. Ini dilakukan dengan memanggilLocal.ToBindingList()
propertiCategories
DbSet
.Local
menyediakan akses ke tampilan lokal kategori yang dilacak, dengan peristiwa yang dikaitkan untuk memastikan data lokal tetap sinkron dengan data yang ditampilkan, dan sebaliknya.ToBindingList()
mengekspos data ini sebagaiIBindingList
, yang dipahami oleh pengikatan data Formulir Windows.
Metode OnClosing
ini dipanggil ketika formulir ditutup. Saat ini, DbContext
dibuang, yang memastikan sumber daya database apa pun akan dibebaskan, dan dbContext
bidang diatur ke null sehingga tidak dapat digunakan lagi.
Mengisi tampilan Produk
Jika aplikasi dimulai pada saat ini, maka aplikasi akan terlihat seperti ini:
Perhatikan bahwa kategori telah dimuat dari database, tetapi tabel produk tetap kosong. Selain itu, tombol Simpan tidak berfungsi.
Untuk mengisi tabel produk, EF Core perlu memuat produk dari database untuk kategori yang dipilih. Untuk mencapai ini:
Di perancang untuk formulir utama, pilih
DataGridView
untuk kategori.Di Properti untuk
DataGridView
, pilih peristiwa (tombol kilat), dan klik dua kali peristiwa SelectionChanged.Ini akan membuat stub dalam kode formulir utama untuk peristiwa yang akan diaktifkan setiap kali pemilihan kategori berubah.
Isi kode untuk peristiwa:
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();
}
}
}
Dalam kode ini, jika ada sesi aktif (non-null), DbContext
maka kita mendapatkan Category
instans yang terikat ke baris yang saat ini dipilih dari DataViewGrid
. (Ini mungkin null
jika baris akhir dalam tampilan dipilih, yang digunakan untuk membuat kategori baru.) Jika ada kategori yang dipilih, maka DbContext
diinstruksikan untuk memuat produk yang terkait dengan kategori tersebut. Hal ini dilakukan dengan:
EntityEntry
Mendapatkan untukCategory
instans (dbContext.Entry(category)
)- Memberi tahu EF Core bahwa kami ingin beroperasi pada navigasi pengumpulan () tersebut
Products
Category
(.Collection(e => e.Products)
) - Dan akhirnya memberi tahu EF Core bahwa kami ingin memuat koleksi produk tersebut dari database (
.Load();
)
Tip
Ketika Load
dipanggil, EF Core hanya akan mengakses database untuk memuat produk jika belum dimuat.
Jika aplikasi sekarang dijalankan lagi, maka aplikasi harus memuat produk yang sesuai setiap kali kategori dipilih:
Menyimpan perubahan
Terakhir, tombol Simpan dapat disambungkan ke EF Core sehingga setiap perubahan yang dilakukan pada produk dan kategori disimpan ke database.
Di perancang untuk formulir utama, pilih tombol Simpan .
Di Properti untuk
Button
, pilih peristiwa (tombol kilat), dan klik dua kali peristiwa Klik.Isi kode untuk peristiwa:
private void buttonSave_Click(object sender, EventArgs e)
{
this.dbContext!.SaveChanges();
this.dataGridViewCategories.Refresh();
this.dataGridViewProducts.Refresh();
}
Kode ini memanggil SaveChanges
pada DbContext
, yang menyimpan perubahan apa pun yang dilakukan pada database SQLite. Jika tidak ada perubahan yang dilakukan, maka ini adalah no-op, dan tidak ada panggilan database yang dilakukan. Setelah disimpan, DataGridView
kontrol akan disegarkan. Ini karena EF Core membaca nilai kunci utama yang dihasilkan untuk produk dan kategori baru apa pun dari database. Memanggil Refresh
pembaruan tampilan dengan nilai yang dihasilkan ini.
Aplikasi akhir
Berikut adalah kode lengkap untuk formulir utama:
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();
}
}
}
Aplikasi sekarang dapat dijalankan, dan produk dan kategori dapat ditambahkan, dihapus, dan diedit. Perhatikan bahwa jika tombol Simpan diklik sebelum menutup aplikasi, maka setiap perubahan yang dilakukan akan disimpan dalam database dan dimuat ulang saat aplikasi dimulai kembali. Jika Simpan tidak diklik, perubahan apa pun akan hilang saat aplikasi dimulai kembali.
Tip
Kategori atau produk baru dapat ditambahkan ke DataViewControl
menggunakan baris kosong di bagian bawah kontrol. Baris dapat dihapus dengan memilihnya dan menekan tombol Del .
Sebelum menyimpan
Setelah menyimpan
Perhatikan bahwa nilai kunci utama untuk kategori dan produk yang ditambahkan diisi saat Simpan diklik.