Опубликован: 08.07.2011 | Уровень: для всех | Доступ: платный
Самостоятельная работа 1:

Разработка приложения на базе WPF

< Лекция 5 || Самостоятельная работа 1: 12345 || Лекция 6 >

Задание 4. Произвести привязку данных к элементам контроля – 2 часа.

Для взаимодействия приложения с базой данных необходимо в коде класса страницы объявить статическое свойство контекста данных сформированной EDM-модели. Это свойство целесообразно объявлять статическим.

Например:

public static TitlePresonalEntities DataEntitiesEmployee { get; set; }

Также необходимо объявить обобщенную коллекцию типа ObservableCollection<Employee> для работы приложения с коллекцией объектов базы данных. Этот тип представляет коллекцию динамических данных, обеспечивающих выдачу уведомления при получении и удалении элементов или при обновлении всего списка. Тип ObservableCollection<Т> находится в пространстве имен System.Collections.ObjectModel, ссылку на которое нужно добавить в объявлении класса приложения.

using System.Collections.ObjectModel;

Экземпляры свойств контекста данных и коллекции необходимо создать в конструкторе класса страницы.

Например:

public PageEmployee()
{
      InitializeComponent();
    DataEntitiesEmployee = new TitlePresonalEntities();
    ListEmployee = new ObservableCollection<Employee>();
}

Формирование данных для приложения, которые должны предоставляться из базы данных, буде проводиться при загрузке страницы приложения. Для этого в XAML-документ Page добавьте свойство Loaded.

Loaded="Page_Loaded"

В код класса страницы приложения включаем обработчик Page_Loaded.

private void Page_Loaded(object sender, RoutedEventArgs e)
{
    ObjectQuery<Employee> employees = DataEntitiesEmployee.Employees;
    var queryEmployee = from employee in employees
                        orderby employee.Surname
                        select employee;
     foreach (Employee emp in queryEmployee)
    {
        ListEmployee.Add(emp);
    }
    DataGridEmployee.ItemsSource = ListEmployee;
}

Поле employees имеет тип ObjectQuery<Employee>. Класс ObjectQuery<Т> представляет запрос, возвращающий коллекцию типизированных сущностей с любым количеством элементов. Запрос сформируем с помощью технологии LINQ.

var queryEmployee = from employee in employees
                        orderby employee.Surname
                        select employee;

Результаты запроса целесообразно отсортировать, например по фамилии сотрудника ( orderby employee.Surname ). Далее формируем коллекцию объектов базы данных и источник данных для сетки DataGrid.

foreach (Employee emp in queryEmployee)
    {
        ListEmployee.Add(emp);
    }
DataGridEmployee.ItemsSource = ListEmployee;

В результате проектирования в приложении сформирована коллекция с данными из таблицы базы данных.

Необходимо настроить сетку DataGrid для корректного отображения данных.

Модифицируйте общее описание DataGrid.

  1. Определите привязку для источника данных.
    ItemsSource="{Binding}"
  2. Отмените автоматическую генерацию столбцов.
    AutoGenerateColumns="False"
  3. Установите привязку к левому краю страницы.
    HorizontalAlignment="Left"
  4. Определите максимальные размеры сетки.
    MaxWidth="1000" MaxHeight="295"
  5. Установите основной и альтернативный цвета заливки сетки.
    RowBackground="#FFE6D3EF" 
    AlternatingRowBackground="#FC96CFD4"
  6. Определите цвет заливки и толщину линии для рамки сетки.
    BorderBrush="#FF1F33EB" 
    BorderThickness="3"
  7. Определите высоту строк сетки.
    RowHeight="25"
  8. Переопределите форму курсора при наведении указателя мыши на таблицу DataGridEmployee.

Модифицированное XAML-описание сетки DataGrid:

Cursor="Hand"
<DataGrid Name="DataGridEmployee"  
ItemsSource="{Binding}"
AutoGenerateColumns="False" 
HorizontalAlignment="Left" 
MaxWidth="1000" MaxHeight="295" 
RowBackground="#FFE6D3EF" 
AlternatingRowBackground="#FC96CFD4" 
BorderBrush="#FF1F33EB" 
BorderThickness="3"  
IsReadOnly="True" 
RowHeight="25" 
Cursor="Hand">

Привязка текстового поля

Для каждого текстового столбца задайте свойство привязки Binding. В расширении разметки привязки данного свойства определите свойство класса источника даннных, к которому привязывается колонка. Далее укажите режим двусторонней привязки ( Mode=TwoWay ), который определяет синхронное обновление как источника, так и приемника привязки. Способ обновления источника привязки задается свойством UpdateSourceTrigger, которое принимает значение PropertyChanged, что соответствует немедленному обновлению источника привязки каждый раз при изменении свойства цели привязки.

Например:

<DataGridTextColumn Header="Фамилия" 
	Binding="{Binding Surname, Mode=TwoWay, 
	UpdateSourceTrigger=PropertyChanged}"/>

Привязка выпадающего списка

Привязка данных к колонке типа DataGridComboBoxColumn требует определенной подготовительной работы. Если в таблице модели данных хранится не текстовое значение поля, а внешний ключ для другой таблицы, где находится данные, то в EDM-модели можно получить значение атрибута из связанных таблиц. Для этого используется атрибут связи в таблице EDM-модели.

Например, для обеспечения возможности работы с коллекцией таблицы Title в приложении добавте в проект папку Model и в ней создайте класс ListTitle.

public  class ListTitle: ObservableCollection<Title>
{
    public ListTitle()
    {
        ObjectQuery<Title> titles = PageEmployee.DataEntitiesEmployee.Titles;
        var queryTitle = from title in titles select title;
         foreach (Title titl in queryTitle)
        {
            this.Add(titl);
        }
    }
}

Класс ListTitle наследуется от класса обобщенной коллекции ObservableCollection<T> и его назначение создавать коллекцию объектов Title. Поле titles является запросом типа ObjectQuery<Title>. Данному полю присваивается свойство Titles контекста данных DataEntitiesEmployee, который определен в классе приложения.

Запрос LINQ получает данные из базы данных в поле queryTitle и затем в цикле foreach формируется коллекция класса ListTitle.

Для использования класса ListTitle в XAML-документе класса приложения необходимо объявить данный класс как ресурс. При этом нужно подключить пространство имен WpfApplProject.Model.

xmlns:core ="clr-namespace:WpfApplProject.Model"

Затем определите ресурс страницы с ключом listTitle.

<Page.Resources>
   <core:ListTitle x:Key="listTitle" />
</Page.Resources>

Далее модифицируйте XAML-описание для типа DataGridComboBoxColumn.

<DataGridComboBoxColumn Header="Должность" 
        ItemsSource="{Binding Source={StaticResource listTitle}}"
        DisplayMemberPath="Title1"
        SelectedValueBinding="{Binding Path=TitleID, Mode=TwoWay, 
        UpdateSourceTrigger=PropertyChanged}"
        SelectedValuePath="ID" />

Источник выпадающего списка задается как статический ресурс { StaticResource listTitle }. Выводимое в ячейки колонки поле должно соответствовать полю Title1 таблицы Title EDM-модели ( DisplayMemberPath="Title1" ). Выбираемый в списке параметр ( SelectedValueBinding ) должен быть привязан к полю TitleID таблицы Employee.

SelectedValueBinding="{Binding Path=TitleID, Mode=TwoWay, 
        UpdateSourceTrigger=PropertyChanged}"

Выбор в свойства SelectedValueBinding производится по пути определенному свойством SelectedValuePath (SelectedValuePath="ID").

Привязка даты

При привязке даты ставится задача для невыделенной ячейки представлять дату в виде цифр дня, месяца и года, разделенных точками, а для выделенной ячейки – использовать класс для выбора даты.

Решение данной задачи осуществлено с помощью разработки двух шаблонов данных DataTemplate.

В первом шаблоне, для которого задан ключ DateTemplate, используется элемент управления TextBlock, свойство Text которого привязывается к атрибуту BirstDate таблицы Employee. Данные в привязке форматируются свойством StringFormat и строковые данные выравниваются по центру. Этот шаблон используется для визуализации данных в режиме их просмотра.

<DataTemplate x:Key="DateTemplate" >
    <TextBlock Text="{Binding BirstDate, 
               StringFormat={}{0:dd\.}{0:MM\.}{0:yyyy}}" 
               VerticalAlignment="Center" HorizontalAlignment="Center" />
</DataTemplate>

Второй шаблон с ключом EditingDateTemplate использует класс DatePicker. Этот шаблон используется в режиме редактирования данных.

<DataTemplate x:Key="EditingDateTemplate">
    <DatePicker SelectedDate="{Binding BirstDate, Mode=TwoWay,
     UpdateSourceTrigger=PropertyChanged}"  />
</DataTemplate>

Разработанные шаблоны необходимо добавить к ресурсам в XAML-документ страницы PageEmployee.

Для настройки колонки "Дата рождения" модифицируем её XAML-описание.

<DataGridTemplateColumn Header="Дата рождения" 
      CellTemplate="{StaticResource DateTemplate}" 
      CellEditingTemplate="{StaticResource EditingDateTemplate}" />

Невыделенную ячейку колонки ( CellTemplate ) свяжите с ресурсом DateTemplate, а редактируемую ячейку ( CellEditingTemplate ) – с ресурсом EditingDateTemplate.

Ячейка столбца для отображения даты типа DataGridTemplateColumn имеет три представления ( рис. 6.17). Если ячейка не выделена, то представление обычное текстовое ( рис. 6.17а). При выделении ячейки прорисовывается свернутое содержание класса DatePicker ( рис. 6.17б). При щелчке на ячейке появляется календарь ( рис. 6.17в).

Отображение даты в сетке DataGrid

Рис. 6.17. Отображение даты в сетке DataGrid

Задание 5. Разработать методы манипулирования данными – 4 часа.

Для редактирования данных в приложении модифицируйте метод EditCommandBinding_Executed

private void EditCommandBinding_Executed(object sender, 
ExecutedRoutedEventArgs e)
{
    DataGridEmployee.IsReadOnly = false;
    DataGridEmployee.BeginEdit();
    isDirty = false;

Свойству IsReadOnly сетки DataGridEmployee присвойте значение false, что обеспечит возможность редактирования строк и ячеек сетки. Далее используйте метод BeginEdit для начала редактирования ячейки, на которую наведен указатель мыши.

Результаты редактирования необходимо сохранить в базе данных. Для этого необходимо вызвать метод SaveCommandBinding_Executed.

private void SaveCommandBinding_Executed(object sender, 
ExecutedRoutedEventArgs e)
{
    dataEntitiesEmployee.SaveChanges();
    isDirty = true;
    DataGridEmployee.IsReadOnly = true;
}

В методе SaveCommandBinding_Executed вызывается метод SaveChanges класса dataEntitiesEmployee. Метод SaveChanges сохраняет все обновления в источнике данных.

Для создания новых данных модифицируйте метод NewCommandBinding_Executed.

Например:

private void NewCommandBinding_Executed(object sender, 
ExecutedRoutedEventArgs e)
{
    Employee employee = Employee.CreateEmployee(-1, "не задано", "не задано", 
       "не задано", 0);
    employee.Telephone = "не задано";
    employee.Email = "не задано";
    try
    {
        DataEntitiesEmployee.Employees.AddObject(employee);
        ListEmployee.Add(employee);
        isDirty = false;
    }
    catch (DataServiceRequestException ex)
    {
        throw new ApplicationException(
	"Ошибка добавления данных" + ex.ToString());
    }
}

В методе NewCommandBinding_Executed объект employee класса Employee создается с помощью метода CreateEmployee. В блоке try ... catch созданный объект employee добавляется в контекст данных методом AddObject сущности Employees EDM-модели DataEntitiesEmployee, а также в коллекцию ListEmployee.

При инициализации команды создания данных в сетке формируется первоначальная строка с данными "по умолчанию". В сформированную строку необходимо ввести реальные данные сохранить их, вызвав команду "Сохранить".

Для удаления данных по сотруднику используем метод DeleteCommandBinding_Executed.

private void DeleteCommandBinding_Executed(object sender, 
	ExecutedRoutedEventArgs e)
{
    Employee emp = DataGridEmployee.SelectedItem as Employee;
    if (emp != null)
    {
        MessageBoxResult result = MessageBox.Show("Удалить данные ", 
"Предупреждение", MessageBoxButton.OKCancel);
        if (result == MessageBoxResult.OK)
        {
            DataEntitiesEmployee.DeleteObject(emp);
            DataGridEmployee.SelectedIndex =
             DataGridEmployee.SelectedIndex == 0 ? 1 : DataGridEmployee.SelectedIndex - 1;
            ListEmployee.Remove(emp);
            DataEntitiesEmployee.SaveChanges();
        }
    }
    else
    {
        MessageBox.Show("Выберите строку для удаления");
    }
}

В методе определяется объект emp класса Employee, который выделен в сетке DataGridEmployee.

Если объект emp найден, то с помощью диалогового окна класса MessageBoxResult выводится сообщение с данными, которые предполагается удалить.

При подтверждении операции удаления объект emp удаляется из контекста методом DeleteObject.

DataEntitiesEmployee.DeleteObject(emp);

Объект emp удаляется из коллекции ListEmployee.

ListEmployee.Remove(emp);

Изменения вносятся в базу данных.

DataEntitiesEmployee.SaveChanges();

Метод UndoCommandBinding_Executed реализует отмену редактирования последних элементов сетки DataGridEmployee и переводит её в режим просмотра.

Для реализации данной функциональности необходимо провести изменения в коде приложения. Для метода Page_Loaded основной код выделите в метод GetEmployees.

private void GetEmployees()
{
    ObjectQuery<Employee> employees = DataEntitiesEmployee.Employees;
    var queryEmployee = from employee in employees
                        orderby employee.Surname
                        select employee;
    foreach (Employee emp in queryEmployee)
    {
        ListEmployee.Add(emp);
    }
    DataGridEmployee.ItemsSource = ListEmployee;
}

С учетом созданного метода GetEmployees метод Page_Loaded будет иметь следующий вид.

private void Page_Loaded(object sender, RoutedEventArgs e)
{
    GetEmployees();
    DataGridEmployee.SelectedIndex = 0;
}

Создайте метод RewriteEmployee, который будет обновлять контекст данных и коллекцию ListEmployee.

private void RewriteEmployee()
{
    DataEntitiesEmployee = new TitlePresonalEntities();
    ListEmployee.Clear();
    GetEmployees();
}

С учетом проведенных модификаций кода метод UndoCommandBinding_Executed будет иметь следующий вид.

private void UndoCommandBinding_Executed(object sender, 
ExecutedRoutedEventArgs e)
{
    RewriteEmployee();
    DataGridEmployee.IsReadOnly = true;
    isDirty = true;
}

При запуске команды Отмена будет отменено редактирование в текущей ячейке сетки, а если редактировались несколько ячеек, то при повторном запуске команды будет обновлена вся строка.

< Лекция 5 || Самостоятельная работа 1: 12345 || Лекция 6 >
Александр Петров
Александр Петров

При загрузке данных из БД возникает исключение InvalidOperationException с сообщением: Элемент коллекции должен быть пустым перед использованием ItemsSource. Знаю, что для заполнения DataGrid можно использовать коллекции Items или ItemsSource, но одновременно их использовать нельзя: если задано значение для свойства ItemsSource и в коде C# добавляется элемент в Items, возникает исключение. 
Вопрос, как отследить и отключить добавление элемента в Items?

Максим Спиридонов
Максим Спиридонов

В пятой лекции на второй странице в компиляторе выскакивает ошибка в строчке :

ObjectQuery<Employee> employees = DataEntitiesEmployee.Employees;

Ошибка CS0029

Не удается неявно преобразовать тип "System.Data.Entity.DbSet<WpfApplProject.Employee>" в "System.Data.Entity.Core.Objects.ObjectQuery<WpfApplProject.Employee>".

в using прописал все как положено, здесь похоже именно с преобразованием типов проблемы

Igor Chelyadinski
Igor Chelyadinski
Беларусь, Минск, №54, 2013
Валентина Алешина
Валентина Алешина
Россия