Приступая к работе с Windows Forms
В этом пошаговом руководстве показано, как создать простое приложение Windows Forms (WinForms), поддерживаемое базой данных SQLite. Приложение использует Entity Framework Core (EF Core) для загрузки данных из базы данных, отслеживания изменений, внесенных в эти данные, и сохранения этих изменений обратно в базу данных.
Снимки экрана и описания кода в этом пошаговом руководстве взяты из Visual Studio 2022 17.3.0.
Совет
Вы можете скачать используемый в этой статье пример из репозитория GitHub.
Необходимые компоненты
Для выполнения этого пошагового руководства необходимо установить Visual Studio 2022 17.3 или более поздней версии с рабочей нагрузкой рабочего стола .NET. Дополнительные сведения об установке новейшей версии Visual Studio см. в статье Установка Visual Studio.
Создание приложения
Запустите Visual Studio
На начальном экране выберите Создать проект.
Выберите приложение Windows Forms и нажмите кнопку "Далее".
На следующем экране укажите имя проекта, например GetStartedWinForms и нажмите кнопку "Далее".
На следующем экране выберите используемую версию .NET. Это пошаговое руководство было создано с помощью .NET 7, но оно также должно работать с более поздними версиями.
Выберите Создать.
Установка пакетов NuGet EF Core
Щелкните правой кнопкой мыши решение и выберите Управление пакетами NuGet для решения...
Перейдите на вкладку "Обзор" и найдите "Microsoft.EntityFrameworkCore.Sqlite".
Выберите пакет Microsoft.EntityFrameworkCore.Sqlite.
Проверьте проект GetStartedWinForms в правой области.
Выберите последнюю версию. Чтобы использовать предварительную версию, установите флажок "Включить предварительную версию".
Щелкните Установить.
Примечание.
Microsoft.EntityFrameworkCore.Sqlite — это пакет поставщика базы данных для использования EF Core с базой данных SQLite. Аналогичные пакеты доступны для других систем баз данных. Установка пакета поставщика базы данных автоматически приводит все зависимости, необходимые для использования EF Core с этой системой базы данных. Сюда входит базовый пакет Microsoft.EntityFrameworkCore .
Определение модели
В этом пошаговом руководстве мы реализуем модель с помощью Code First. Таким образом, EF Core создаст таблицы и схему базы данных на основе определенных вами классов C#. См. статью "Управление схемами баз данных" , чтобы узнать, как использовать существующую базу данных.
Щелкните проект правой кнопкой мыши и нажмите кнопку "Добавить", а затем "Класс" , чтобы добавить новый класс.
Используйте имя
Product.cs
файла и замените код для класса следующим образом: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!; }
Повторите создание
Category.cs
с помощью следующего кода: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(); }
Свойство Products
Category
класса и Category
свойства класса Product
называются навигациями. В EF Core навигации определяют связь между двумя типами сущностей. В этом случае навигация Product.Category
ссылается на категорию, к которой принадлежит данный продукт. Аналогичным образом навигация Category.Products
по коллекции содержит все продукты для данной категории.
Совет
При использовании Windows Forms , ObservableCollectionListSource
который реализует IListSource
, можно использовать для навигации по коллекции. Это не обязательно, но улучшает двусторонняя привязка данных.
Определение DbContext
В EF Core класс, производный от DbContext
этого, используется для настройки типов сущностей в модели и выступает в качестве сеанса для взаимодействия с базой данных. В самом простом случае DbContext
класс:
- Содержит
DbSet
свойства для каждого типа сущности в модели. - Переопределяет
OnConfiguring
метод для настройки поставщика базы данных и строка подключения для использования. Дополнительные сведения см. в разделе "Настройка DbContext ".
В этом случае класс DbContext также переопределяет OnModelCreating
метод для предоставления некоторых примеров данных для приложения.
Добавьте новый ProductsContext.cs
класс в проект со следующим кодом:
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" });
}
}
Обязательно создайте решение на этом этапе.
Добавление элементов управления на форму
Приложение отобразит список категорий и список продуктов. Если в первом списке выбрана категория, то второй список изменится, чтобы отобразить продукты для этой категории. Эти списки можно изменить, чтобы добавить, удалить или изменить продукты и категории, и эти изменения можно сохранить в базе данных SQLite, нажав кнопку "Сохранить ".
Измените имя основной формы на
Form1
MainForm
.И измените заголовок на "Продукты и категории".
С помощью панели элементов добавьте два
DataGridView
элемента управления, расположенные рядом друг с другом.В свойствах для первого
DataGridView
измените имяdataGridViewCategories
на .В свойствах второго
DataGridView
измените имяdataGridViewProducts
на .Кроме того, с помощью панели элементов добавьте
Button
элемент управления.Назовите кнопку
buttonSave
и присвойте ей текст "Сохранить". Форма должна выглядеть следующим образом:
Привязка данных
Следующим шагом является подключение Product
и Category
типы из модели к DataGridView
элементам управления. Это привязывает данные, загруженные EF Core, к элементам управления, таким образом, что сущности, отслеживаемые EF Core, синхронизируются с данными, отображаемыми в элементах управления.
Щелкните глиф действия конструктора на первом
DataGridView
. Это крошечная кнопка в правом верхнем углу элемента управления.Откроется список действий, из которого можно получить доступ к раскрывающемся списку для выбранного источника данных. Мы еще не создали источник данных, поэтому перейдите к нижнему краю и нажмите кнопку "Добавить новый источник данных объекта...".
Выберите категорию , чтобы создать источник данных объекта для категорий и нажмите кнопку "ОК".
Совет
Если здесь нет типов источников данных, убедитесь, что
Product.cs
он добавлен в проект иProductsContext.cs
решение создано.Category.cs
Теперь раскрывающийся список "Выбор источника данных" содержит только что созданный источник данных объекта. Разверните другие источники данных, а затем выберите "Источники данных проекта" и выберите категорию.
Второй
DataGridView
будет привязан к продуктам. Однако вместо привязки к типу верхнего уровняProduct
она будет привязана кProducts
навигации изCategory
привязки первойDataGridView
. Это означает, что при выборе категории в первом представлении продукты для этой категории будут автоматически использоваться во втором представлении.Используя глиф действия конструктора во втором
DataGridView
, выберите "Выбрать источник данных", а затем развернитеcategoryBindingSource
и выберитеProducts
.
Настройка отображаемых элементов
По умолчанию столбец создается в DataGridView
каждом свойстве привязанных типов. Кроме того, значения для каждого из этих свойств можно изменить пользователем. Однако некоторые значения, такие как значения первичного ключа, концептуально доступны только для чтения, и поэтому не следует изменять. Кроме того, некоторые свойства, такие как CategoryId
свойство внешнего ключа и Category
навигация, не полезны для пользователя, и поэтому их следует скрыть.
Совет
Обычно скрытие свойств первичного ключа в реальном приложении. Они остаются видимыми здесь, чтобы легко увидеть, что EF Core делает за кулисами.
Щелкните правой кнопкой мыши первый
DataGridView
и выберите пункт "Изменить столбцы...".CategoryId
Сделайте столбец, представляющий первичный ключ, только для чтения и нажмите кнопку "ОК".Щелкните правой кнопкой мыши второй
DataGridView
и выберите пункт "Изменить столбцы...".ProductId
Сделайте столбец доступным только для чтения и удалитеCategory
CategoryId
столбцы, а затем нажмите кнопку "ОК".
Подключение к EF Core
Теперь приложению требуется небольшой объем кода для подключения EF Core к элементам управления, привязанным к данным.
MainForm
Откройте код, щелкнув файл правой кнопкой мыши и выбрав "Просмотреть код".Добавьте частное поле для хранения
DbContext
сеанса и добавьте переопределения дляOnLoad
методов иOnClosing
методов. Код должен выглядеть следующим образом:
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;
}
}
}
Метод OnLoad
вызывается при загрузке формы. В настоящее время
- Создается экземпляр
ProductsContext
, который будет использоваться для загрузки и отслеживания изменений в продуктах и категориях, отображаемых приложением. EnsureCreated
вызывается дляDbContext
создания базы данных SQLite, если она еще не существует. Это быстрый способ создания базы данных при создании прототипов или тестировании приложений. Однако если модель изменяется, необходимо удалить базу данных, чтобы ее можно было создать еще раз. (СтрокаEnsureDeleted
может быть незакомментирована, чтобы легко удалить и повторно создать базу данных при запуске приложения.) Вместо этого можно использовать миграции EF Core для изменения и обновления схемы базы данных без потери данных.EnsureCreated
также заполняет новую базу данных данными, определенными в методеProductsContext.OnModelCreating
.- Метод
Load
расширения используется для загрузки всех категорий из базы данных вDbContext
базу данных. Теперь эти сущности будут отслеживатьсяDbContext
пользователем, которые будут обнаруживать любые изменения, внесенные при изменении категорий пользователем. - Свойство
categoryBindingSource.DataSource
инициализировано в категории, отслеживаемые даннымиDbContext
. Это делается путем вызоваLocal.ToBindingList()
Categories
DbSet
свойства.Local
предоставляет доступ к локальному представлению отслеживаемых категорий с событиями, которые подключены к локальным данным, чтобы обеспечить синхронизацию локальных данных с отображаемыми данными и наоборот.ToBindingList()
предоставляет эти данные в видеIBindingList
привязки данных Windows Forms.
Метод OnClosing
вызывается при закрытии формы. В настоящее время удаляется, DbContext
что гарантирует, что все ресурсы базы данных будут освобождены, и dbContext
поле имеет значение NULL, чтобы его нельзя было использовать снова.
Заполнение представления "Продукты"
Если приложение запущено на этом этапе, оно должно выглядеть примерно так:
Обратите внимание, что категории были загружены из базы данных, но таблица продуктов остается пустой. Кроме того, кнопка "Сохранить " не работает.
Чтобы заполнить таблицу продуктов, EF Core необходимо загрузить продукты из базы данных для выбранной категории. Чтобы достичь этого, выполните указанные ниже действия.
В конструкторе основной формы выберите
DataGridView
категории.В разделе "Свойства"
DataGridView
выберите события (кнопка молнии) и дважды щелкните событие SelectionChanged.Это создаст заглушку в основном коде формы для события, которое будет запущено всякий раз, когда изменяется выбор категории.
Введите код для события:
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();
}
}
}
В этом коде, если существует активный (непустой) DbContext
сеанс, мы получаем Category
экземпляр, привязанный к выбранной строке текущей DataViewGrid
строки. (Это может быть null
, если выбрана окончательная строка в представлении, которая используется для создания новых категорий.) Если выбрана категория, необходимо загрузить продукты, DbContext
связанные с этой категорией. Способы выполнения этих целей:
- Получение экземпляра
EntityEntry
Category
(dbContext.Entry(category)
) - Предоставление EF Core знать, что мы хотим работать с
Products
навигацией по коллекции этогоCategory
(.Collection(e => e.Products)
) - И, наконец, сообщите EF Core, что мы хотим загрузить коллекцию продуктов из базы данных (
.Load();
)
Совет
При Load
вызове EF Core будет получать доступ только к базе данных для загрузки продуктов, если они еще не загружены.
Если приложение запущено снова, оно должно загружать соответствующие продукты всякий раз, когда выбрана категория:
Сохранение изменений
Наконец, кнопка "Сохранить" может быть подключена к EF Core, чтобы все изменения, внесенные в продукты и категории, сохранялись в базе данных.
В конструкторе основной формы нажмите кнопку "Сохранить ".
В разделе "Свойства"
Button
выберите события (кнопка молнии) и дважды щелкните событие Click.Введите код для события:
private void buttonSave_Click(object sender, EventArgs e)
{
this.dbContext!.SaveChanges();
this.dataGridViewCategories.Refresh();
this.dataGridViewProducts.Refresh();
}
Этот код вызывает SaveChanges
DbContext
объект, который сохраняет все изменения, внесенные в базу данных SQLite. Если изменения не были внесены, это no-op, и вызов базы данных не выполняется. После сохранения DataGridView
элементы управления обновляются. Это связано с тем, что EF Core считывает созданные значения первичного ключа для любых новых продуктов и категорий из базы данных. Вызов Refresh
обновляет отображение с этими созданными значениями.
Окончательное приложение
Ниже приведен полный код основной формы:
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();
}
}
}
Теперь приложение можно запускать, а продукты и категории можно добавлять, удалять и изменять. Обратите внимание, что если кнопка "Сохранить " нажимается перед закрытием приложения, все внесенные изменения будут храниться в базе данных и повторно загружаться при повторном запуске приложения. Если элемент "Сохранить " не щелкается, все изменения теряются при повторном запуске приложения.
Совет
Новую категорию или продукт можно добавить DataViewControl
в пустую строку в нижней части элемента управления. Строку можно удалить, выбрав ее и нажав клавишу Del .
Перед сохранением
После сохранения
Обратите внимание, что значения первичного ключа для добавленной категории и продуктов заполняются при нажатии кнопки "Сохранить ".