Примечание.
Для доступа к этой странице требуется авторизация. Вы можете попробовать войти или изменить каталоги.
Для доступа к этой странице требуется авторизация. Вы можете попробовать изменить каталоги.
Important
Этот документ действителен только для WPF в .NET Framework
В этом документе описывается привязка данных для WPF в .NET Framework. Для новых проектов .NET рекомендуется использовать EF Core вместо Entity Framework 6. Документация по привязке данных в EF Core приведена здесь: начало работы с WPF.
В этом пошаговом руководстве показано, как привязать типы POCO к элементам управления WPF в форме master-detail. Приложение использует API Entity Framework для заполнения объектов данными из базы данных, отслеживания изменений и сохранения данных в базе данных.
Модель определяет два типа, которые участвуют в отношениях "один ко многим": Категория (основной\главный) и Продукт (зависимый\подчиненный). Затем средства Visual Studio используются для привязки типов, определенных в модели, к элементам управления WPF. Платформа привязки данных WPF обеспечивает навигацию между связанными объектами: выбор строк в главном представлении приводит к обновлению представления сведений с соответствующими дочерними данными.
Снимки экрана и описания кода в этом пошаговом руководстве взяты из Visual Studio 2013, но вы можете выполнить это пошаговое руководство с помощью Visual Studio 2012 или Visual Studio 2010.
Использование параметра Object для создания источников данных WPF
В предыдущей версии Entity Framework мы использовали параметр "База данных " при создании нового источника данных на основе модели, созданной с помощью конструктора EF. Это связано с тем, что конструктор создаст контекст, производный от ObjectContext и классов сущностей, производных от EntityObject. Использование параметра "База данных" поможет вам создать лучший код для взаимодействия с этой поверхностью API.
Конструкторы EF для Visual Studio 2012 и Visual Studio 2013 создают контекст, производный от DbContext вместе с простыми классами сущностей POCO. В Visual Studio 2010 рекомендуется переключение на шаблон создания кода, использующий DbContext, как описано далее в этом пошаговом руководстве.
При использовании поверхности API DbContext следует использовать параметр Object при создании нового источника данных, как показано в этом пошаговом руководстве.
При необходимости можно вернуться к созданию кода на основе ObjectContext для моделей, созданных с помощью конструктора EF.
Pre-Requisites
Для выполнения этого пошагового руководства необходимо установить Visual Studio 2013, Visual Studio 2012 или Visual Studio 2010.
Если вы используете Visual Studio 2010, также необходимо установить NuGet. Дополнительные сведения см. в разделе "Установка NuGet".
Создание приложения
- Запустите Visual Studio
- Файл —> Новый —> Project....
- Выберите Windows на левой панели и WPFApplication в правой области
- Введите WPFwithEFSample в качестве имени
- Нажмите кнопку "ОК"
Установка пакета NuGet Entity Framework
- В обозревателе решений щелкните правой кнопкой мыши проект WinFormswithEFSample
- Выберите "Управление пакетами NuGet...
- В диалоговом окне "Управление пакетами NuGet" выберите вкладку "Интернет" и выберите пакет EntityFramework
- Щелкните Установить.
Note
Помимо сборки EntityFramework также добавляется ссылка на System.ComponentModel.DataAnnotations. Если у проекта есть ссылка на System.Data.Entity, она будет удалена при установке пакета EntityFramework. Сборка System.Data.Entity больше не используется для приложений Entity Framework 6.
Определение модели
В этом пошаговом руководстве вы можете реализовать модель с помощью code First или EF Designer. Выполните один из двух следующих разделов.
Вариант 1. Определение модели с помощью кода в первую очередь
В этом разделе показано, как создать модель и связанную с ней базу данных с помощью Code First. Перейдите к следующему разделу (Вариант 2: Определение модели с использованием подхода Database First), если вы предпочитаете использовать подход Database First для обратной разработки модели из базы данных с помощью конструктора EF.
При использовании разработки Code First вы обычно начинаете с написания классов .NET Framework, определяющих концептуальную модель (домен).
- Добавьте новый класс в WPFwithEFSample:
- Щелкните правой кнопкой мыши имя проекта
- Выберите "Добавить", а затем "Создать элемент"
- Выберите класс и введите Product для имени класса
- Замените определение класса Product следующим кодом:
namespace WPFwithEFSample
{
public class Product
{
public int ProductId { get; set; }
public string Name { get; set; }
public int CategoryId { get; set; }
public virtual Category Category { get; set; }
}
}
- Добавьте класс Category со следующим определением:
using System.Collections.ObjectModel;
namespace WPFwithEFSample
{
public class Category
{
public Category()
{
this.Products = new ObservableCollection<Product>();
}
public int CategoryId { get; set; }
public string Name { get; set; }
public virtual ObservableCollection<Product> Products { get; private set; }
}
}
Свойство Products для класса Category и свойства Category в классе Product — это свойства навигации. В Entity Framework свойства навигации предоставляют способ навигации между двумя типами сущностей.
Помимо определения сущностей, необходимо определить класс, производный от DbContext и предоставляющий свойства DbSet<TEntity> . Свойства DbSet<TEntity> позволяют контексту знать, какие типы необходимо включить в модель.
Экземпляр производного типа DbContext управляет объектами сущности во время выполнения, включая заполнение объектов данными из базы данных, отслеживание изменений и сохранение данных в базе данных.
- Добавьте новый класс ProductContext в проект со следующим определением:
using System.Data.Entity;
namespace WPFwithEFSample
{
public class ProductContext : DbContext
{
public DbSet<Category> Categories { get; set; }
public DbSet<Product> Products { get; set; }
}
}
Скомпилируйте проект.
Вариант 2. Определение модели с помощью базы данных First
В этом разделе показано, как использовать Database First для обратного проектирования модели из базы данных с помощью дизайнера EF. Если вы завершили предыдущий раздел (вариант 1. Определение модели с помощью кода first), пропустите этот раздел и перейдите прямо к разделу " Отложенная загрузка ".
Создание существующей базы данных
Обычно при выборе существующей базы данных она уже будет создана, но для этого пошагового руководства необходимо создать базу данных для доступа.
Сервер базы данных, установленный с Visual Studio, отличается в зависимости от установленной версии Visual Studio:
- Если вы используете Visual Studio 2010, вы создадите базу данных SQL Express.
- Если вы используете Visual Studio 2012, вы создадите базу данных LocalDB .
Давайте пойдем вперед и создадим базу данных.
Вид —> Эксплорер сервера
Щелкните правой кнопкой мыши на Подключения к данным —> Добавление подключения...
Если вы никогда раньше не подключались к базе данных из обозревателя серверов, вам потребуется выбрать Microsoft SQL Server в качестве источника данных.
Подключитесь к LocalDB или SQL Express, в зависимости от того, какой вы установили, и введите "Продукты " в качестве имени базы данных.
Нажмите кнопку "ОК ", и вам будет предложено создать новую базу данных, нажмите кнопку "Да"
Новая база данных появится в обозревателе серверов, щелкните его правой кнопкой мыши и выберите новый запрос
Скопируйте следующий SQL в новый запрос, а затем щелкните правой кнопкой мыши запрос и выберите "Выполнить".
CREATE TABLE [dbo].[Categories] (
[CategoryId] [int] NOT NULL IDENTITY,
[Name] [nvarchar](max),
CONSTRAINT [PK_dbo.Categories] PRIMARY KEY ([CategoryId])
)
CREATE TABLE [dbo].[Products] (
[ProductId] [int] NOT NULL IDENTITY,
[Name] [nvarchar](max),
[CategoryId] [int] NOT NULL,
CONSTRAINT [PK_dbo.Products] PRIMARY KEY ([ProductId])
)
CREATE INDEX [IX_CategoryId] ON [dbo].[Products]([CategoryId])
ALTER TABLE [dbo].[Products] ADD CONSTRAINT [FK_dbo.Products_dbo.Categories_CategoryId] FOREIGN KEY ([CategoryId]) REFERENCES [dbo].[Categories] ([CategoryId]) ON DELETE CASCADE
Модель обратного проектирования
Мы будем использовать конструктор Entity Framework, который входит в состав Visual Studio, чтобы создать нашу модель.
Проект —> добавление нового элемента...
Выберите данные из меню слева, а затем ADO.NET модель данных сущности
Введите ProductModel в качестве имени и нажмите кнопку "ОК"
Запустится мастер модели данных сущностей
Выберите "Создать из базы данных" и нажмите кнопку "Далее"
Выберите подключение к базе данных, созданной в первом разделе, введите ProductContext в качестве имени строки подключения и нажмите кнопку "Далее".
Установите флажок рядом с "Таблицы", чтобы импортировать все таблицы и нажмите кнопку "Готово"
После завершения процедуры обратного проектирования новая модель добавляется в проект и открывается для просмотра в среде Entity Framework Designer. В проект также добавлен файл App.config с сведениями о подключении для базы данных.
Дополнительные шаги в Visual Studio 2010
Если вы работаете в Visual Studio 2010, необходимо обновить конструктор EF для использования создания кода EF6.
- Щелкните правой кнопкой мыши пустое место модели в конструкторе EF и выберите пункт "Добавить элемент создания кода".
- Выберите онлайн-шаблоны в меню слева и найдите DbContext
- Выберите генератор DBContext ef 6.x для C#, введите ProductsModel в качестве имени и нажмите кнопку "Добавить".
Обновление создания кода для привязки данных
EF создает код из модели с помощью шаблонов T4. Шаблоны, поставляемые с Visual Studio или скачанные из коллекции Visual Studio, предназначены для общего использования. Это означает, что сущности, созданные из этих шаблонов, имеют простые свойства ICollection<T> . Однако при привязке данных с помощью WPF желательно использовать ObservableCollection для свойств коллекции, чтобы WPF мог отслеживать изменения, внесенные в коллекции. Для этого мы изменим шаблоны для использования ObservableCollection.
Откройте обозреватель решений и найдите файл ProductModel.edmx
Найдите файл ProductModel.tt , который будет вложен в файл ProductModel.edmx
Дважды щелкните файл ProductModel.tt, чтобы открыть его в редакторе Visual Studio
Найдите и замените два вхождения "ICollection" на "ObservableCollection". Они расположены примерно в строках 296 и 484.
Найдите и замените первое вхождение "HashSet" на "ObservableCollection". Это вхождение находится примерно в строке 50. Не заменяйте второе вхождение HashSet в дальнейшем в коде.
Найдите и замените единственное вхождение System.Collections.Generic на System.Collections.ObjectModel. Это находится примерно в строке 424.
Сохраните файл ProductModel.tt. Это должно привести к повторному создании кода сущностей. Если код не создается автоматически, щелкните правой кнопкой мыши ProductModel.tt и выберите "Запустить настраиваемое средство".
Если вы откроете файл Category.cs (вложенный в ProductModel.tt), то вы увидите, что коллекция Products имеет тип ObservableCollection<Product>.
Скомпилируйте проект.
Отложенная загрузка
Свойство Products для класса Category и свойства Category в классе Product — это свойства навигации. В Entity Framework свойства навигации предоставляют способ навигации между двумя типами сущностей.
EF позволяет автоматически загружать связанные сущности из базы данных при первом доступе к свойству навигации. При таком типе загрузки (называемой отложенной загрузкой), помните, что при первом доступе к каждому свойству навигации отдельный запрос будет выполняться в базе данных, если содержимое еще не находится в контексте.
При использовании типов сущностей POCO, EF добивается ленивой загрузки за счет создания экземпляров производных прокси-типов во время выполнения и последующего переопределения виртуальных свойств в ваших классах для добавления механизма загрузки. Чтобы получить отложенную загрузку связанных объектов, необходимо объявить методы получения свойств навигации как общедоступные и виртуальные (переопределяемые в Visual Basic), а класс не должен быть запечатан (NotOverridable в Visual Basic). При использовании Database First навигационные свойства автоматически становятся виртуальными для включения отложенной загрузки. В разделе Code First мы решили сделать свойства навигации виртуальными по той же причине.
Привязка объекта к элементам управления
Добавьте классы, определенные в модели в качестве источников данных для этого приложения WPF.
Дважды щелкните MainWindow.xaml в обозревателе решений, чтобы открыть основную форму
В главном меню выберите "Проект "> Добавить новый источник данных ... (в Visual Studio 2010 необходимо выбрать данные —> добавить новый источник данных...)
В поле "Выбор типа источника данных" выберите "Объект" и нажмите кнопку "Далее"
В диалоговом окне "Выбор объектов данных" разверните wpFwithEFSample два раза и выберите категорию
Нет необходимости выбрать источник данных Product, так как мы вернемся к нему через свойство Product в источнике данных категории
Нажмите кнопку "Готово".
Окно "Источники данных" открывается рядом с окном MainWindow.xaml. Если окно "Источники данных" не отображается, выберите "Вид"> - Другие окна -> Источники данных
Нажмите значок закрепления, чтобы окно источников данных не скрывалось автоматически. Возможно, потребуется нажать кнопку обновления, если окно уже видно.
Выберите источник данных категории и перетащите его в форму.
При перетаскивании этого источника произошло следующее:
- Ресурс categoryViewSource и элемент управления categoryDataGrid были добавлены в XAML
- Для свойства DataContext в родительском элементе Grid задано значение "{StaticResource categoryViewSource }". Ресурс categoryViewSource служит источником привязки для внешнего элемента Сетки\parent Grid. Затем внутренние элементы Grid наследуют значение DataContext из родительской сетки (свойство ItemsSource объекта CategoryDataGrid имеет значение "{Binding}")
<Window.Resources>
<CollectionViewSource x:Key="categoryViewSource"
d:DesignSource="{d:DesignInstance {x:Type local:Category}, CreateList=True}"/>
</Window.Resources>
<Grid DataContext="{StaticResource categoryViewSource}">
<DataGrid x:Name="categoryDataGrid" AutoGenerateColumns="False" EnableRowVirtualization="True"
ItemsSource="{Binding}" Margin="13,13,43,191"
RowDetailsVisibilityMode="VisibleWhenSelected">
<DataGrid.Columns>
<DataGridTextColumn x:Name="categoryIdColumn" Binding="{Binding CategoryId}"
Header="Category Id" Width="SizeToHeader"/>
<DataGridTextColumn x:Name="nameColumn" Binding="{Binding Name}"
Header="Name" Width="SizeToHeader"/>
</DataGrid.Columns>
</DataGrid>
</Grid>
Добавление сетки сведений
Теперь, когда у нас есть сетка для отображения категорий, давайте добавим сетку сведений для отображения связанных продуктов.
- Выберите свойство Products из источника данных категории и перетащите его в форму.
- Ресурс categoryProductsViewSource и сетка productDataGrid добавляются в XAML
- Путь привязки для этого ресурса установлен на Продукты.
- Платформа привязки данных WPF гарантирует, что в productDataGrid отображаются только продукты, связанные с выбранной категорией.
- Из панели элементов перетащите кнопку в форму. Задайте свойству Name значение buttonSave и свойству Content значение Save.
Форма должна выглядеть примерно так:
Добавление кода, обрабатывающего взаимодействие с данными
Пришло время добавить некоторые обработчики событий в главное окно.
В окне XAML щелкните на элементе <Window, чтобы выбрать главное окно.
В окне "Свойства" выберите события в правом верхнем углу, а затем дважды щелкните текстовое поле справа от загруженной метки
Кроме того, добавьте событие Click для кнопки "Сохранить", дважды нажав кнопку "Сохранить" в конструкторе.
Это приведет вас к коду, связанному с формой, теперь мы изменим код, чтобы использовать ProductContext для доступа к данным. Обновите код для MainWindow, как показано ниже.
Код объявляет долговременный экземпляр ProductContext. Объект ProductContext используется для запроса и сохранения данных в базе данных. Затем Метод Dispose()экземпляра ProductContext вызывается из переопределенного метода OnClosing . Примечания кода содержат сведения о том, что делает код.
using System.Data.Entity;
using System.Linq;
using System.Windows;
namespace WPFwithEFSample
{
public partial class MainWindow : Window
{
private ProductContext _context = new ProductContext();
public MainWindow()
{
InitializeComponent();
}
private void Window_Loaded(object sender, RoutedEventArgs e)
{
System.Windows.Data.CollectionViewSource categoryViewSource =
((System.Windows.Data.CollectionViewSource)(this.FindResource("categoryViewSource")));
// Load is an extension method on IQueryable,
// defined in the System.Data.Entity namespace.
// This method enumerates the results of the query,
// similar to ToList but without creating a list.
// When used with Linq to Entities this method
// creates entity objects and adds them to the context.
_context.Categories.Load();
// After the data is loaded call the DbSet<T>.Local property
// to use the DbSet<T> as a binding source.
categoryViewSource.Source = _context.Categories.Local;
}
private void buttonSave_Click(object sender, RoutedEventArgs e)
{
// When you delete an object from the related entities collection
// (in this case Products), the Entity Framework doesn’t mark
// these child entities as deleted.
// Instead, it removes the relationship between the parent and the child
// by setting the parent reference to null.
// So we manually have to delete the products
// that have a Category reference set to null.
// The following code uses LINQ to Objects
// against the Local collection of Products.
// The ToList call is required because otherwise the collection will be modified
// by the Remove call while it is being enumerated.
// In most other situations you can use LINQ to Objects directly
// against the Local property without using ToList first.
foreach (var product in _context.Products.Local.ToList())
{
if (product.Category == null)
{
_context.Products.Remove(product);
}
}
_context.SaveChanges();
// Refresh the grids so the database generated values show up.
this.categoryDataGrid.Items.Refresh();
this.productsDataGrid.Items.Refresh();
}
protected override void OnClosing(System.ComponentModel.CancelEventArgs e)
{
base.OnClosing(e);
this._context.Dispose();
}
}
}
Тестирование приложения WPF
Скомпилируйте и запустите приложение. Если вы использовали подход Code First, то увидите, что вам будет создана база данных WPFwithEFSample.ProductContext.
Введите имя категории в верхней сетке и названия продуктов в нижней сетке Не введите ничего в столбцах идентификатора, так как первичный ключ создается базой данных.
Нажмите кнопку "Сохранить ", чтобы сохранить данные в базе данных
После вызова DbContext SaveChanges()идентификаторы заполняются созданными значениями базы данных. Поскольку мы вызвали Refresh() после SaveChanges(), элементы управления DataGrid также обновились с новыми значениями.
Дополнительные ресурсы
Дополнительные сведения о привязке данных к коллекциям с помощью WPF см. в этой статье в документации ПО WPF.