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

Взаимодействие приложения с базой данных

Разбить на страницы
Показывать лекцию целиком

Цель

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

Основные положения EDM модели

Взаимодействие с источником данных WCF-приложения базируется на модели , , . Модель EDM представляет набор основных понятий, которые описывают структуру данных независимо от формы хранения. Описываемые в модели EDM данные могут иметь различную структуру: реляционную, текстовые файлы, файлы XML, электронные таблицы и отчеты. Модель EDM описывает структуры данных на основе сущностей и связей, которые являются независимыми от схем хранения. В результате такого подхода форма хранения данных отделена от приложения и не влияет на его разработку. Это обеспечивается тем, что сущности и связи описывают структуру данных так, как она используется в приложении. Модель EDM является концептуальной моделью, которая описывает представление структуры данных в виде сущностей и связей.

Модель EDM использует три основных понятия для описания структуры данных:

  • тип сущности;
  • тип ассоциации;
  • свойство.
  • Тип сущности используется для описания структуры данных при помощи модели EDM. В концептуальной модели типы сущностей конструируются из свойств и описывают структуру основных концептуальных элементов верхнего уровня, таких как сотрудники и роли. Тип сущности является шаблоном для сущностей. Сущность представляет определенный объект (например, определенного сотрудника или его роль в бизнес-процессе). Каждая сущность должна иметь уникальный ключ внутри набора сущностей. Набор сущностей представляет собой коллекцию экземпляров определенного типа сущности. Наборы сущностей (и наборы ассоциаций) логически сгруппированы в контейнеры сущностей.

    Тип ассоциации применяется для построения описаний связей в модели EDM. В концептуальной модели ассоциация представляет собой связь между двумя типами сущностей. Каждая ассоциация имеет две конечные точки ассоциации, которые определяют типы сущностей, участвующие в ассоциации. Каждая конечная точка ассоциации также определяет кратность конечной точки ассоциации, которая указывает число сущностей, могущих присутствовать на одной конечной точке ассоциации.

    Типы сущностей содержат свойства, которые определяют их структуру и характеристики. Например, тип сущности "Сотрудник" может иметь свойства, такие как идентифицирующий номер, фамилию, имя, отчество, должность, дату рождения и адрес электронной почты.

    Свойства в концептуальной модели аналогичны свойствам, которые определены применительно к классу в приложении или атрибуту реляционной базы данных. Свойство может содержать примитивные данные (такие как строка, целое число, дата, логическое значение) или структурированные данные (такие как сложный тип).

    приведена схема концептуальной модели базы данных TitlePersonal – "Персонал" с двумя типами сущностей ( Employee – Сотрудник и Title –роль/должность) и одной ассоциативной связью 1:* (один ко многим).

    (рис 5.1) Концептуальная модель базы данных

    Таблица Employee содержит данные по сотруднику. Атрибутами таблицы являются:

  • ID – код сотрудника;
  • Surname – фамилия;
  • Name – имя;
  • Patronymic – отчество;
  • BirstDate – дата рождения;
  • Telephone – телефон;
  • Email – адрес электронной почты;
  • TitleIDвнешний ключ для связи с таблицей Title.
  • Таблица Title является справочником должностей, имеющихся на предприятии, и включает следующие атрибуты:

  • ID – код должности;
  • Title – наименование должности.
  • Создание EDM-модели

    Для создания EDM-модели данных добавим в проект новый элемент – модель ADO.NET EDM, присвоив файлу модели имя ).

    (рис 5.2) Добавление в проект модели EDM

    В мастере создания EDM-модели выберем опцию "Создать из базы данных" (рис 5.3). Для создания соединения с базой данных в окне "Выбор подключения к данным" укажем соединение с базой данных (имя_сервера.имя_базы данных, в рассматриваемом примере – алексей-пк.TitlePerson) и зададим имя модели данных - ).

    (рис 5.4) Создание модели EDM из базы данных(рис 5.3) Создание соединения с базой данных

    В окне "Выбор объектов базы данных" отметим необходимые для приложения таблицы (в нашем случае это таблицы ).

    (рис 5.5) Выбор таблиц базы данных

    При завершении работы мастера создания EDM-модели в проект будет добавлен файл ), ссыки на необходимые библиотеки и конфигурационный файл.

    (рис 5.6) Проект с моделью EDM

    Автоматически сгенерированный класс TitlePersonalEntities, который наследуется от класса ObjectContext, представляет сущности базы данных TitlePerson, содержит свойства, моделирующие таблицы Employee и Title, связи между таблицами.

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

    <?xml version="1.0" encoding="utf-8"?>
    <configuration>
      <connectionStrings>
        <add name="TitlePersonalEntities" connectionString="metadata=res://*/TitlePersonal.csdl|res://*/TitlePersonal.ssdl|res://*
    /TitlePersonal.msl;provider=System.Data.SqlClient;provider connection string="
    Data Source=АЛЕКСЕЙ-ПК;Initial Catalog=TitlePresonal;Integrated Security=True;MultipleActiveResultSets=True"" 
    providerName="System.Data.EntityClient" />
      </connectionStrings>
    </configuration>

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

    Для взаимодействия приложения с базой данных необходимо в коде класса PageEmployee объявить статическое свойство контекста данных DataEntitiesEmployee сформированной EDM-модели. На данном этапе проектирования приложения это свойство можно объявлять только общедоступным, то есть publi c, но в дальнейшем нам потребуется использовать это свойство в других классах без создания экземпляра класса PageEmployee, что возможно только в случае, если это свойство будет статическим.

    public static TitlePresonalEntities DataEntitiesEmployee { get; set; }

    Также необходимо объявить коллекцию ListEmployee для работы приложения с коллекцией объектов Employee.

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

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

    Loaded="Page_Loaded"

    В код класса PageEmployee включаем обработчик 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 имеет тип , , .

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

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

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

    В результате проектирования в приложении сформирована коллекция с данными из таблицы Employee базы данных TitleEmployee. Следующей задачей является настройка сетки DataGridEmployee для корректного отображения данных.

    Вначале модифицируем общее описание DataGrid.

    <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"
    CanUserAddRows="False" 
    CanUserDeleteRows="False">
    

    Определим привязку для источника данных.

    ItemsSource="{Binding}"

    Отменим автоматическую генерацию столбцов.

    AutoGenerateColumns="False"

    Установим привязку к левому краю страницы.

    HorizontalAlignment="Left"

    Определим максимальные размеры сетки.

    MaxWidth="1000" MaxHeight="295"

    Установим основной и альтернативный цвета заливки сетки.

    RowBackground="#FFE6D3EF" 
    AlternatingRowBackground="#FC96CFD4"

    Определим цвет заливки и толщину линии для рамки сетки.

    BorderBrush="#FF1F33EB" 
    BorderThickness="3"

    Определим высоту строк сетки.

    RowHeight="25"

    Переопределим форму курсора при наведении указателя мыши на таблицу DataGridEmployee.

    Cursor="Hand"

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

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

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

    После запуска приложения страница .

    (рис 5.7) Страница PageEmployee с привязанными в таблице текстовыми полями

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

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

    Для обеспечения возможности работы с коллекцией в таблицы 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, который определен в классе PageEmployee. В классе ListTitle не рекомендуется создавать собственный контекст данных, а лучше использовать контекст, созданный в классе PageEmployee. Именно поэтому свойства DataEntitiesEmployee объявлено в классе PageEmployee статическим.

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

    Для использования класса ListTitle в XAML-документе класса PageEmployee необходимо объявить данный класс как ресурс. При этом нужно подключить пространство имен 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").

    При запуске приложения страница .

    (рис 5.8) Страница PageEmployee с привязанным столбцом "Должность"

    Ячейка столбца типа ). Если ячейка не выделена, то представление обычное текстовое (рис 5.9а). При выделении ячейки прорисовывается представление б). При раскрытии списка появляется список элемента в).

    (рис 5.9) Отображение ComboBox в сетке DataGrid

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

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

    Решение данной задачи осуществлено с помощью разработки двух шаблонов данных 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.

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

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

    При запуске приложения страница .

    (рис 5.10) Страница PageEmployee с привязанным столбцом "Дата рождения"

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

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

    Редактирование, вставка и удаление данных

    Для редактирования данных в приложении модифицируем метод 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);
            DataGridEmployee.ScrollIntoView(employee);
            DataGridEmployee.SelectedIndex = DataGridEmployee.Items.Count - 1;
            DataGridEmployee.Focus();
            DataGridEmployee.IsReadOnly = false;
            isDirty = false;
        }
        catch (DataServiceRequestException ex)
        {
            throw new ApplicationException(
    "Ошибка добавления нового сотрудника в контекст данных" + ex.ToString());
        }
    }

    В методе NewCommandBinding_Executed объект employee класса Employee создается с помощью метода CreateEmployee, параметрами которого являются ID, которое задается равным -1, Surname, Name и Patronymic, которым задается значение "не задано" и TitleID со значением 0. Кроме того, свойствам Telephone и Email объекта employee присваивается строковое значение "не задано". В блоке try ... catch созданный объект employee добавляется в контекст данных методом AddObject сущности Employees EDM-модели DataEntitiesEmployee, а также в коллекцию ListEmployee. Затем производится прокрутка сетки, выделение и перемещение фокуса на созданный объект.

    DataGridEmployee.ScrollIntoView(employee);
    DataGridEmployee.SelectedIndex = DataGridEmployee.Items.Count - 1;
    DataGridEmployee.Focus();

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

    (рис 5.12) Формирование данных по новому сотруднику

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

    private void DeleteCommandBinding_Executed(object sender, 
    									ExecutedRoutedEventArgs e)
    {
        Employee emp = DataGridEmployee.SelectedItem as Employee;
        if (emp != null)
        {
            MessageBoxResult result = MessageBox.Show("Удалить сотрудника: " + 
    emp.Surname + " " +emp.Name + " " + emp.Patronymic, 
    "Предупреждение", 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.

    Employee emp = DataGridEmployee.SelectedItem as Employee;

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

    MessageBoxResult result = MessageBox.Show("Удалить сотрудника: " + 
    emp.Surname + " " + emp.Name + " " + emp.Patronymic, "Предупреждение", MessageBoxButton.OKCancel);

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

    DataEntitiesEmployee.DeleteObject(emp);

    Далее формируется в сетке DataGridEmployee выделяемая строка.

    DataGridEmployee.SelectedIndex =
                DataGridEmployee.SelectedIndex == 0 ? 1 : DataGridEmployee.SelectedIndex - 1;

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

    ListEmployee.Remove(emp);

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

    DataEntitiesEmployee.SaveChanges();

    Страница .

    (рис 5.13) Удаление данных по сотруднику

    Метод 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;
    }

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

    Проверка ввода данных

    При вводе и редактировании данных важной задачей эффективного интерфейса является валидация – проверка вводимых данных. Если с данными можно связать определенные бизнес-правила (например, соответствие допустимому диапазону, корректность преобразования при вводе, определенный шаблон), то целесообразно проводить валидацию данных при их вводе. Элемент управления DataGrid позволяет выполнять проверку на уровне ячеек и на уровне строк. В строке DataGrid хранится определенный объект. В сетке DataGridEmployee отображаются объекты Employee. Каждая колонка сетки связана с определенным свойством класс Employee. Когда пользователь обновляет какое-либо свойство объекта Employee, в ходе проверки на уровне ячеек проверяются отдельные свойства объекта, привязанного к данным. Когда пользователь вносит изменения в строку, то есть в объект Employee, в ходе проверки на уровне строк проверяются целые объекты данных. Для улучшения качества интерфейса можно реализовать визуальную реакцию на ошибки проверки или использовать стандартные сигналы, предоставляемые элементом управления DataGrid.

    Поставим задачу проверки шаблона при вводе свойства Email, то есть адреса электронной почты. В строке ввода должны присутствовать символы @ и точка.

    Спроектируем правило проверки для привязки, которое будет использоваться со столбцом "Электронная почта". Для этого необходимо создать класс правила проверки EmailRule, который должен наследоваться от базового класса ValidationRule. Класс EmailRule поместим во вновь созданную папку ValidationRules проекта.

    public class EmailRule: ValidationRule
    {
        public override ValidationResult Validate(object value,
                        System.Globalization.CultureInfo cultureInfo)
        {
            string email = string.Empty;
            if (value != null)
            {
                email = value.ToString();
            }
            else
                return new ValidationResult(false, " Адрес электронной почты не задан! ");
            if (email.Contains("@")  email.Contains("."))
            {
                return new ValidationResult(true, null);
            }
            else
            {
                return new ValidationResult(false, 
    					"Адрес электронной почты должен содержать 
    					символы @ и (.) точки \n Шаблон адреса:			
    					adres@mymail.com");
            }
        }
    }

    В классе правил проверки необходимо переопределить метод Validate, который возвращает результат проверки – экземпляр класса ValidationResult. Если проверка проходит успешно, тогда возвращаемым значением является объект класса ValidationResult, который создается с параметрами true и null.

    return new ValidationResult(true, null);

    Если проверка приводит к выявлению несоответствия, то класс ValidationResult, создается с параметрами false и строка сообщения об ошибке.

    return new ValidationResult(false, "Адрес электронной почты должен 
    содержать символы @ и (.) точки \n Шаблон адреса:   
    adres@mymail.com");

    Для использования объекта EmailRule в XAML-документе страницы PageEmployee добавим в её описание пространство имен WpfApplProject.ValidationRules.

    xmlns:rule="clr-namespace:WpfApplProject.ValidationRules"

    Внесем изменения в XAML-описание колонки Электронная почта.

    <DataGridTextColumn  Header="Электронная почта" EditingElementStyle="{StaticResource errorStyle}">
        <DataGridTextColumn.Binding >
            <Binding Path="Email" Mode="TwoWay" UpdateSourceTrigger="PropertyChanged"
    ValidatesOnExceptions ="True" >
                <Binding.ValidationRules>
                     <rule:EmailRule />
              </Binding.ValidationRules>
            </Binding>
        </DataGridTextColumn.Binding>
    </DataGridTextColumn>

    Для привязки Binding свойству ValidatesOnExceptions зададим значение true. Задание данного свойства обеспечивает использование элемента ExceptionValidationRule. ExceptionValidationRule предоставляет встроенное правило проверки, проверяющее возникновение исключений при обновлении свойства источника. В случае возникновения ошибки механизм привязки создает ValidationError с исключением и добавляет его в коллекцию Validation.Errors привязанного элемента. Отсутствие ошибок сбрасывает это состояние обратной связи проверки, если другое правило не вызовет событие проверки.

    Правило проверки задается для свойства ValidationRules класса Binding.

    <Binding.ValidationRules>
       <rule:EmailRule />
    </Binding.ValidationRules>

    Модель привязки данных WPF позволяет связывать ValidationRules с объектом Binding, используя пользовательские правила. Подсистема привязки проверяет каждое из ValidationRule, связанных с привязкой, каждый раз, когда вводимое значение (значение свойства цель привязки) переносится в свойство источник привязки.

    В WPF имеются стандартные стили для отображения ячеек сетки при возникновении ошибки ввода, но они являются недостаточно информативными. Для более эффектного выделения ячейки сетки при ошибке ввода создадим специальный стиль errorStyle.

    <Style x:Key="errorStyle" TargetType="{x:Type TextBox}">
        <Setter Property="Padding" Value="-2"/>
        <Style.Triggers>
            <Trigger Property="Validation.HasError" Value="True">
                <Setter Property="Background" Value="Red"/>
                <Setter Property="BorderThickness" Value="1" />
                <Setter Property="ToolTip"
                        Value="{Binding RelativeSource={RelativeSource Self},
                    Path=(Validation.Errors)[0].ErrorContent}"/>
            </Trigger>
        </Style.Triggers>
    </Style>

    В стиле errorStyle триггер срабатывает, когда свойство Validation.HasError принимает значение true. При этом цвет заливки ячейки становится красным и при наведении на ячейку указателя мыши выводится сообщение-подсказка (свойство ToolTip ), текст которого определяется в правиле проверки EmailRule при обнаружении ошибки (Validation.Errors)[0].ErrorContent).

    Стандартные средства WPF при обнаружении ошибки ввода в ячейке помечают строку сетки красным восклицательным знаком. Создадим шаблон ControlTemplate для визуального указания ошибки при проверке строки. Данный шаблон задается для свойства RowValidationErrorTemplate сетки DataGrid. Проектируемый шаблон должен обеспечить вывод красного круга с белым восклицательным знаком слева от строки сетки с обнаруженной ошибкой ввода. При наведении указателя мыши на круг должна выводиться подсказка, сформированная в классе правил проверки ввода.

    <DataGrid.RowValidationErrorTemplate>
        <ControlTemplate>
            <Grid Margin="0,-2,0,-2"
                  ToolTip="{Binding RelativeSource={RelativeSource FindAncestor,
                                               AncestorType={x:Type DataGridRow}},
                Path=(Validation.Errors)[0].ErrorContent}">
                <Ellipse StrokeThickness="0" Fill="Red" Width="{TemplateBinding FontSize}"
                                                     Height="{TemplateBinding FontSize}" />
                <TextBlock Text="!" FontSize="{TemplateBinding FontSize}" FontWeight="Bold"
                                        Foreground="White" HorizontalAlignment="Center"  />
            </Grid>
        </ControlTemplate>
    </DataGrid.RowValidationErrorTemplate>

    На рис 5.14 и рис 5.15 приведены результаты тестирования страницы PageEmployee при наличии ошибки ввода адреса электронной почты.

    (рис 5.15) Визуализация ошибки ввода при наведении указателя мыши на ячейки сетки(рис 5.14) Визуализация ошибки ввода при наведении указателя мыши на предупреждение для строки

    Поиск данных

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

    (рис 5.16) Дизайн страницы PageEmployee с элементами управления для поиска

    XAML-описание элементов управления, поддерживающих поиск имеет следующий вид.

    <TextBlock Name="TextBlockSurname" Text="Фамилия" />
    <TextBlock Name="TextBlockTitle" Text="Должность" />
    <TextBox Name="TextBoxSurname" TextChanged="TextBoxSurname_TextChanged"/>
    <ComboBox Name="ComboBoxTitle"
    ItemsSource="{Binding Source={StaticResource listTitle}}"
    DisplayMemberPath="Title1"
    SelectionChanged="ComboBoxTitle_SelectionChanged"/>
    <Button Name="ButtonFindSurname" ToolTip="Поиск по фамилии"
    IsEnabled="False" Click="ButtonFindSurname_Click">
    <Image Source="Images/Search.jpg"/>
    </Button>
    <Button Name="ButtonFindTitle" ToolTip="Поиск по должности"
    IsEnabled="False" Click="ButtonFindTitle_Click">
    <Image Source="Images/Search.jpg" />
    </Button>

    Элементы управления размещаем в рамке BorderFind, для которой делаем градиентную заливку. В рамке располагаем сетку gridFind с тремя колонками и строками. Первая строка в объединенных колонках содержит текстовый блок find с текстом "Поиск". Во второй и третьей строках первой колонки размещены текстовые блоки TextBlockSurname и TextBlockTitle соответственно. Во второй колонке размещаются элементы управления для ввода информации: текстовый блок TextBoxSurname (вторая строка) и выпадающий список ComboBoxTitle (третья строка). В третьей колонке размещаются кнопки: для поиска по фамилии ButtonFindSurname (вторая строка) и для поиска по должности ButtonFindTitle (третья строка).

    При загрузке страницы ).

    private void FindCommandBinding_Executed(object sender, 
    ExecutedRoutedEventArgs e)
            {
                BorderFind.Visibility = System.Windows.Visibility.Visible;
            }
    (рис 5.17) Страница PageEmployee с элементами управления для поиска

    Если в текстовом блоке TextBoxSurname не введена информация или в выпадающем списке ComboBoxTitle не сделан выбор, то кнопки ButtonFindSurname и ButtonFindTitle будут недоступны.

    При вводе информации в текстовый блок TextBoxSurname запускается обработчик TextBoxSurname_TextChanged, который делает доступной кнопку ButtonFindSurname.

    private void TextBoxSurname_TextChanged(object sender, TextChangedEventArgs e)
    {
        ButtonFindSurname.IsEnabled = true;
        ButtonFindTitle.IsEnabled = false;
        ComboBoxTitle.SelectedIndex = -1;
    }

    При нажатии кнопки ButtonFindSurname запускается обработчик ButtonFindSurname_Click.

    private void ButtonFindSurname_Click(object sender, RoutedEventArgs e)
    {
        string surname = TextBoxSurname.Text;
        DataEntitiesEmployee = new TitlePresonalEntities();
        ListEmployee.Clear();
        ObjectQuery<Employee> employees = DataEntitiesEmployee.Employees;
        var queryEmployee = from employee in employees
                                where employee.Surname == surname
                                select employee;
        foreach (Employee emp in queryEmployee)
        {
            ListEmployee.Add(emp);
        }
        if (ListEmployee.Count > 0)
        {
            DataGridEmployee.ItemsSource = ListEmployee;
            ButtonFindSurname.IsEnabled = true;
            ButtonFindTitle.IsEnabled = false;
        }
        else
            MessageBox.Show("Сотрудник с фамилией \n"+surname+"\n не найдан",
                 "Предупреждение", MessageBoxButton.OK, MessageBoxImage.Warning);
    }

    В данном обработчике события Click выполняется LINQ запрос на получение данных из базы TitlePersonal в соответствии с заданной фамилией сотрудника.

    var queryEmployee = from employee in employees
                            where employee.Surname == surname
                            select employee;

    При выполнении запроса по фамилии страница

    (рис 5.18) Поиск по фамилии

    Найденные данные по сотруднику можно редактировать и удалять.

    Если поиск в базе данных не привел к нахождению информации, то выдается информационное сообщение (рис 5.19).

    (рис 5.19) Неудачный поиск по фамилии

    Для поиска информации из базы данных по должности сотрудников необходимо сделать выбор из выпадающего списка ComboBoxTitle. В результате выбора срабатывает обработчик ComboBoxTitle_SelectionChanged, который делает доступной кнопку ButtonFindTitle.

    private void ComboBoxTitle_SelectionChanged(object sender, SelectionChangedEventArgs e)
    {
        ButtonFindTitle.IsEnabled = true;
        ButtonFindSurname.IsEnabled = false;
        TextBoxSurname.Text = "";
    }

    При нажатии на кнопку ButtonFindTitle срабатывает обработчик ButtonFindTitle_Click.

    private void ButtonFindTitle_Click(object sender, RoutedEventArgs e)
    {
        DataEntitiesEmployee = new TitlePresonalEntities();
        ListEmployee.Clear();
        
        Title title = ComboBoxTitle.SelectedItem as Title;
        ObjectQuery<Employee> employees = DataEntitiesEmployee.Employees;
        var queryEmployee = from employee in employees
                            where employee.TitleID == title.ID
                            orderby employee.Surname
                            select employee;
        foreach (Employee emp in queryEmployee)
        {
            ListEmployee.Add(emp);
        }
        DataGridEmployee.ItemsSource = ListEmployee;
    }

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

    (рис 5.20) Поиск сотрудников по должности

    Для обновления выводимого списка сотрудников в приложение добавлена функция обновления, которая реализуется командой Refresh. Данная команда добавлена в коллекцию команд страницы PageEmployee.

    <Page.CommandBindings>
    ....
            <CommandBinding Command="Refresh" Executed="RefreshCommandBinding_Executed" />
    ... 
        </Page.CommandBindings>

    Вызов команды добавлен в меню

    <MenuItem Header="Обновить" Command="Refresh"/>

    и панель инструментов

    <Button Name="Refresh" Margin="5,2,5,2"  Command="Refresh"
            ToolTip="обновить данные по сотрудникам">
        <Image Source="Images/Refresh.jpg" ></Image>
    </Button>

    Основное назначение обработчика команды RefreshCommandBinding_Executed – обновление списка ListEmployee.

    private void RefreshCommandBinding_Executed(object sender, 
    ExecutedRoutedEventArgs e)
    {
        RewriteEmployee();
        DataGridEmployee.IsReadOnly = false;
        isDirty = false;
        SetUnvisibilityFind();
    }

    Команда "Обновить" обычно используется после поиска данных или ввода данных по новому сотруднику.

    Ключевые термины

    Модель "сущность-связь", модель EDM, концептуальная модель, тип сущности, тип ассоциации, привязка данных, проверка ввода данных, правило проверки, поиск данных.

    Entity Data Model, EDM, ADO.NET, ObjectContext, ObservableCollection, ObjectQuery, Binding, ValidationRule

    Краткие итоги

    Рассмотрены вопросы построения EDM-модели в приложении. Выполнена привязка данных к интерфейсным элементам: текстовым, выпадающему списку, дате. Сконструирован код методов взаимодействия приложения с базой данных: редактирование, вставка, удаление данных и поиск данных. На основе пользовательских правил проиллюстриравана возможность проверки данных при их вводе.

    Ресурсы для углубленного изучения

  • Платформа ADO.NET Entity Framework // http://msdn.microsoft.com/ru-ru/library/bb399572.aspx.
  • Привязка данных // http://msdn.microsoft.com/ru-ru/library/cc278072(v=VS.95).aspx.
  • , стр. 315 – 363.
  • , стр. 535 – 588.
  • , стр. 329 – 357, 978 – 1004.
  • , стр. 740 – 779, 832 – 866.
  • , стр. 463 – 492, 857 – 890, 1048 - 1102
  • Вопросы для самопроверки

  • Объясните назначение и основные понятия EDM –модели.
  • Какие структуры и типы данных может описывать EDM-модель?
  • Какие основные понятия используются в модель EDM для описания структуры данных?
  • Поясните содержание и назначение типа сущности в EDM модели.
  • Поясните содержание и назначение типа ассоциации в EDM модели.
  • Поясните содержание и назначение свойств в EDM модели.
  • Как в проекте задается строка соединения с базой данных для EDM модели?
  • Для чего необходимо в проекте иметь свойство контекста данных EDM модели?
  • Поясните назначение и возможности коллекции типа ObservableCollection<Т>.
  • Поясните назначение и возможности класса ObjectQuery<Т>.
  • Поясните назначение и возможности свойства UpdateSourceTrigger, используемого при привязке данных.
  • Поясните особенности привязки данных к колонкам таблиц типа DataGridComboBoxColumn.
  • Поясните особенности привязки данных к колонкам таблиц с помощью шаблона DateTemplate.
  • С помощью какого метода фиксируются проведенные изменения в источнике данных?
  • Поясните назначение валидации данных.
  • Каким требованиям должен соответствовать класс, используемый для валидации данных?
  • Какими способами можно реализовать поиск и фильтрацию данных из источника данных?
  • Страницы:

    Цель

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

    Основные положения EDM модели

    Взаимодействие с источником данных WCF-приложения базируется на модели , , . Модель EDM представляет набор основных понятий, которые описывают структуру данных независимо от формы хранения. Описываемые в модели EDM данные могут иметь различную структуру: реляционную, текстовые файлы, файлы XML, электронные таблицы и отчеты. Модель EDM описывает структуры данных на основе сущностей и связей, которые являются независимыми от схем хранения. В результате такого подхода форма хранения данных отделена от приложения и не влияет на его разработку. Это обеспечивается тем, что сущности и связи описывают структуру данных так, как она используется в приложении. Модель EDM является концептуальной моделью, которая описывает представление структуры данных в виде сущностей и связей.

    Модель EDM использует три основных понятия для описания структуры данных:

  • тип сущности;
  • тип ассоциации;
  • свойство.
  • Тип сущности используется для описания структуры данных при помощи модели EDM. В концептуальной модели типы сущностей конструируются из свойств и описывают структуру основных концептуальных элементов верхнего уровня, таких как сотрудники и роли. Тип сущности является шаблоном для сущностей. Сущность представляет определенный объект (например, определенного сотрудника или его роль в бизнес-процессе). Каждая сущность должна иметь уникальный ключ внутри набора сущностей. Набор сущностей представляет собой коллекцию экземпляров определенного типа сущности. Наборы сущностей (и наборы ассоциаций) логически сгруппированы в контейнеры сущностей.

    Тип ассоциации применяется для построения описаний связей в модели EDM. В концептуальной модели ассоциация представляет собой связь между двумя типами сущностей. Каждая ассоциация имеет две конечные точки ассоциации, которые определяют типы сущностей, участвующие в ассоциации. Каждая конечная точка ассоциации также определяет кратность конечной точки ассоциации, которая указывает число сущностей, могущих присутствовать на одной конечной точке ассоциации.

    Типы сущностей содержат свойства, которые определяют их структуру и характеристики. Например, тип сущности "Сотрудник" может иметь свойства, такие как идентифицирующий номер, фамилию, имя, отчество, должность, дату рождения и адрес электронной почты.

    Свойства в концептуальной модели аналогичны свойствам, которые определены применительно к классу в приложении или атрибуту реляционной базы данных. Свойство может содержать примитивные данные (такие как строка, целое число, дата, логическое значение) или структурированные данные (такие как сложный тип).

    приведена схема концептуальной модели базы данных TitlePersonal – "Персонал" с двумя типами сущностей ( Employee – Сотрудник и Title –роль/должность) и одной ассоциативной связью 1:* (один ко многим).

    (рис 5.1) Концептуальная модель базы данных

    Таблица Employee содержит данные по сотруднику. Атрибутами таблицы являются:

  • ID – код сотрудника;
  • Surname – фамилия;
  • Name – имя;
  • Patronymic – отчество;
  • BirstDate – дата рождения;
  • Telephone – телефон;
  • Email – адрес электронной почты;
  • TitleIDвнешний ключ для связи с таблицей Title.
  • Таблица Title является справочником должностей, имеющихся на предприятии, и включает следующие атрибуты:

  • ID – код должности;
  • Title – наименование должности.
  • Создание EDM-модели

    Для создания EDM-модели данных добавим в проект новый элемент – модель ADO.NET EDM, присвоив файлу модели имя ).

    (рис 5.2) Добавление в проект модели EDM

    В мастере создания EDM-модели выберем опцию "Создать из базы данных" (рис 5.3). Для создания соединения с базой данных в окне "Выбор подключения к данным" укажем соединение с базой данных (имя_сервера.имя_базы данных, в рассматриваемом примере – алексей-пк.TitlePerson) и зададим имя модели данных - ).

    (рис 5.4) Создание модели EDM из базы данных(рис 5.3) Создание соединения с базой данных

    В окне "Выбор объектов базы данных" отметим необходимые для приложения таблицы (в нашем случае это таблицы ).

    (рис 5.5) Выбор таблиц базы данных

    При завершении работы мастера создания EDM-модели в проект будет добавлен файл ), ссыки на необходимые библиотеки и конфигурационный файл.

    (рис 5.6) Проект с моделью EDM

    Автоматически сгенерированный класс TitlePersonalEntities, который наследуется от класса ObjectContext, представляет сущности базы данных TitlePerson, содержит свойства, моделирующие таблицы Employee и Title, связи между таблицами.

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

    <?xml version="1.0" encoding="utf-8"?>
    <configuration>
      <connectionStrings>
        <add name="TitlePersonalEntities" connectionString="metadata=res://*/TitlePersonal.csdl|res://*/TitlePersonal.ssdl|res://*
    /TitlePersonal.msl;provider=System.Data.SqlClient;provider connection string="
    Data Source=АЛЕКСЕЙ-ПК;Initial Catalog=TitlePresonal;Integrated Security=True;MultipleActiveResultSets=True"" 
    providerName="System.Data.EntityClient" />
      </connectionStrings>
    </configuration>

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

    Для взаимодействия приложения с базой данных необходимо в коде класса PageEmployee объявить статическое свойство контекста данных DataEntitiesEmployee сформированной EDM-модели. На данном этапе проектирования приложения это свойство можно объявлять только общедоступным, то есть publi c, но в дальнейшем нам потребуется использовать это свойство в других классах без создания экземпляра класса PageEmployee, что возможно только в случае, если это свойство будет статическим.

    public static TitlePresonalEntities DataEntitiesEmployee { get; set; }

    Также необходимо объявить коллекцию ListEmployee для работы приложения с коллекцией объектов Employee.

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

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

    Loaded="Page_Loaded"

    В код класса PageEmployee включаем обработчик 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 имеет тип , , .

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

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

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

    В результате проектирования в приложении сформирована коллекция с данными из таблицы Employee базы данных TitleEmployee. Следующей задачей является настройка сетки DataGridEmployee для корректного отображения данных.

    Вначале модифицируем общее описание DataGrid.

    <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"
    CanUserAddRows="False" 
    CanUserDeleteRows="False">
    

    Определим привязку для источника данных.

    ItemsSource="{Binding}"

    Отменим автоматическую генерацию столбцов.

    AutoGenerateColumns="False"

    Установим привязку к левому краю страницы.

    HorizontalAlignment="Left"

    Определим максимальные размеры сетки.

    MaxWidth="1000" MaxHeight="295"

    Установим основной и альтернативный цвета заливки сетки.

    RowBackground="#FFE6D3EF" 
    AlternatingRowBackground="#FC96CFD4"

    Определим цвет заливки и толщину линии для рамки сетки.

    BorderBrush="#FF1F33EB" 
    BorderThickness="3"

    Определим высоту строк сетки.

    RowHeight="25"

    Переопределим форму курсора при наведении указателя мыши на таблицу DataGridEmployee.

    Cursor="Hand"

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

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

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

    После запуска приложения страница .

    (рис 5.7) Страница PageEmployee с привязанными в таблице текстовыми полями

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

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

    Для обеспечения возможности работы с коллекцией в таблицы 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, который определен в классе PageEmployee. В классе ListTitle не рекомендуется создавать собственный контекст данных, а лучше использовать контекст, созданный в классе PageEmployee. Именно поэтому свойства DataEntitiesEmployee объявлено в классе PageEmployee статическим.

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

    Для использования класса ListTitle в XAML-документе класса PageEmployee необходимо объявить данный класс как ресурс. При этом нужно подключить пространство имен 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").

    При запуске приложения страница .

    (рис 5.8) Страница PageEmployee с привязанным столбцом "Должность"

    Ячейка столбца типа ). Если ячейка не выделена, то представление обычное текстовое (рис 5.9а). При выделении ячейки прорисовывается представление б). При раскрытии списка появляется список элемента в).

    (рис 5.9) Отображение ComboBox в сетке DataGrid

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

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

    Решение данной задачи осуществлено с помощью разработки двух шаблонов данных 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.

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

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

    При запуске приложения страница .

    (рис 5.10) Страница PageEmployee с привязанным столбцом "Дата рождения"

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

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

    Редактирование, вставка и удаление данных

    Для редактирования данных в приложении модифицируем метод 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);
            DataGridEmployee.ScrollIntoView(employee);
            DataGridEmployee.SelectedIndex = DataGridEmployee.Items.Count - 1;
            DataGridEmployee.Focus();
            DataGridEmployee.IsReadOnly = false;
            isDirty = false;
        }
        catch (DataServiceRequestException ex)
        {
            throw new ApplicationException(
    "Ошибка добавления нового сотрудника в контекст данных" + ex.ToString());
        }
    }

    В методе NewCommandBinding_Executed объект employee класса Employee создается с помощью метода CreateEmployee, параметрами которого являются ID, которое задается равным -1, Surname, Name и Patronymic, которым задается значение "не задано" и TitleID со значением 0. Кроме того, свойствам Telephone и Email объекта employee присваивается строковое значение "не задано". В блоке try ... catch созданный объект employee добавляется в контекст данных методом AddObject сущности Employees EDM-модели DataEntitiesEmployee, а также в коллекцию ListEmployee. Затем производится прокрутка сетки, выделение и перемещение фокуса на созданный объект.

    DataGridEmployee.ScrollIntoView(employee);
    DataGridEmployee.SelectedIndex = DataGridEmployee.Items.Count - 1;
    DataGridEmployee.Focus();

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

    (рис 5.12) Формирование данных по новому сотруднику

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

    private void DeleteCommandBinding_Executed(object sender, 
    									ExecutedRoutedEventArgs e)
    {
        Employee emp = DataGridEmployee.SelectedItem as Employee;
        if (emp != null)
        {
            MessageBoxResult result = MessageBox.Show("Удалить сотрудника: " + 
    emp.Surname + " " +emp.Name + " " + emp.Patronymic, 
    "Предупреждение", 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.

    Employee emp = DataGridEmployee.SelectedItem as Employee;

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

    MessageBoxResult result = MessageBox.Show("Удалить сотрудника: " + 
    emp.Surname + " " + emp.Name + " " + emp.Patronymic, "Предупреждение", MessageBoxButton.OKCancel);

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

    DataEntitiesEmployee.DeleteObject(emp);

    Далее формируется в сетке DataGridEmployee выделяемая строка.

    DataGridEmployee.SelectedIndex =
                DataGridEmployee.SelectedIndex == 0 ? 1 : DataGridEmployee.SelectedIndex - 1;

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

    ListEmployee.Remove(emp);

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

    DataEntitiesEmployee.SaveChanges();

    Страница .

    (рис 5.13) Удаление данных по сотруднику

    Метод 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;
    }

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

    Проверка ввода данных

    При вводе и редактировании данных важной задачей эффективного интерфейса является валидация – проверка вводимых данных. Если с данными можно связать определенные бизнес-правила (например, соответствие допустимому диапазону, корректность преобразования при вводе, определенный шаблон), то целесообразно проводить валидацию данных при их вводе. Элемент управления DataGrid позволяет выполнять проверку на уровне ячеек и на уровне строк. В строке DataGrid хранится определенный объект. В сетке DataGridEmployee отображаются объекты Employee. Каждая колонка сетки связана с определенным свойством класс Employee. Когда пользователь обновляет какое-либо свойство объекта Employee, в ходе проверки на уровне ячеек проверяются отдельные свойства объекта, привязанного к данным. Когда пользователь вносит изменения в строку, то есть в объект Employee, в ходе проверки на уровне строк проверяются целые объекты данных. Для улучшения качества интерфейса можно реализовать визуальную реакцию на ошибки проверки или использовать стандартные сигналы, предоставляемые элементом управления DataGrid.

    Поставим задачу проверки шаблона при вводе свойства Email, то есть адреса электронной почты. В строке ввода должны присутствовать символы @ и точка.

    Спроектируем правило проверки для привязки, которое будет использоваться со столбцом "Электронная почта". Для этого необходимо создать класс правила проверки EmailRule, который должен наследоваться от базового класса ValidationRule. Класс EmailRule поместим во вновь созданную папку ValidationRules проекта.

    public class EmailRule: ValidationRule
    {
        public override ValidationResult Validate(object value,
                        System.Globalization.CultureInfo cultureInfo)
        {
            string email = string.Empty;
            if (value != null)
            {
                email = value.ToString();
            }
            else
                return new ValidationResult(false, " Адрес электронной почты не задан! ");
            if (email.Contains("@")  email.Contains("."))
            {
                return new ValidationResult(true, null);
            }
            else
            {
                return new ValidationResult(false, 
    					"Адрес электронной почты должен содержать 
    					символы @ и (.) точки \n Шаблон адреса:			
    					adres@mymail.com");
            }
        }
    }

    В классе правил проверки необходимо переопределить метод Validate, который возвращает результат проверки – экземпляр класса ValidationResult. Если проверка проходит успешно, тогда возвращаемым значением является объект класса ValidationResult, который создается с параметрами true и null.

    return new ValidationResult(true, null);

    Если проверка приводит к выявлению несоответствия, то класс ValidationResult, создается с параметрами false и строка сообщения об ошибке.

    return new ValidationResult(false, "Адрес электронной почты должен 
    содержать символы @ и (.) точки \n Шаблон адреса:   
    adres@mymail.com");

    Для использования объекта EmailRule в XAML-документе страницы PageEmployee добавим в её описание пространство имен WpfApplProject.ValidationRules.

    xmlns:rule="clr-namespace:WpfApplProject.ValidationRules"

    Внесем изменения в XAML-описание колонки Электронная почта.

    <DataGridTextColumn  Header="Электронная почта" EditingElementStyle="{StaticResource errorStyle}">
        <DataGridTextColumn.Binding >
            <Binding Path="Email" Mode="TwoWay" UpdateSourceTrigger="PropertyChanged"
    ValidatesOnExceptions ="True" >
                <Binding.ValidationRules>
                     <rule:EmailRule />
              </Binding.ValidationRules>
            </Binding>
        </DataGridTextColumn.Binding>
    </DataGridTextColumn>

    Для привязки Binding свойству ValidatesOnExceptions зададим значение true. Задание данного свойства обеспечивает использование элемента ExceptionValidationRule. ExceptionValidationRule предоставляет встроенное правило проверки, проверяющее возникновение исключений при обновлении свойства источника. В случае возникновения ошибки механизм привязки создает ValidationError с исключением и добавляет его в коллекцию Validation.Errors привязанного элемента. Отсутствие ошибок сбрасывает это состояние обратной связи проверки, если другое правило не вызовет событие проверки.

    Правило проверки задается для свойства ValidationRules класса Binding.

    <Binding.ValidationRules>
       <rule:EmailRule />
    </Binding.ValidationRules>

    Модель привязки данных WPF позволяет связывать ValidationRules с объектом Binding, используя пользовательские правила. Подсистема привязки проверяет каждое из ValidationRule, связанных с привязкой, каждый раз, когда вводимое значение (значение свойства цель привязки) переносится в свойство источник привязки.

    В WPF имеются стандартные стили для отображения ячеек сетки при возникновении ошибки ввода, но они являются недостаточно информативными. Для более эффектного выделения ячейки сетки при ошибке ввода создадим специальный стиль errorStyle.

    <Style x:Key="errorStyle" TargetType="{x:Type TextBox}">
        <Setter Property="Padding" Value="-2"/>
        <Style.Triggers>
            <Trigger Property="Validation.HasError" Value="True">
                <Setter Property="Background" Value="Red"/>
                <Setter Property="BorderThickness" Value="1" />
                <Setter Property="ToolTip"
                        Value="{Binding RelativeSource={RelativeSource Self},
                    Path=(Validation.Errors)[0].ErrorContent}"/>
            </Trigger>
        </Style.Triggers>
    </Style>

    В стиле errorStyle триггер срабатывает, когда свойство Validation.HasError принимает значение true. При этом цвет заливки ячейки становится красным и при наведении на ячейку указателя мыши выводится сообщение-подсказка (свойство ToolTip ), текст которого определяется в правиле проверки EmailRule при обнаружении ошибки (Validation.Errors)[0].ErrorContent).

    Стандартные средства WPF при обнаружении ошибки ввода в ячейке помечают строку сетки красным восклицательным знаком. Создадим шаблон ControlTemplate для визуального указания ошибки при проверке строки. Данный шаблон задается для свойства RowValidationErrorTemplate сетки DataGrid. Проектируемый шаблон должен обеспечить вывод красного круга с белым восклицательным знаком слева от строки сетки с обнаруженной ошибкой ввода. При наведении указателя мыши на круг должна выводиться подсказка, сформированная в классе правил проверки ввода.

    <DataGrid.RowValidationErrorTemplate>
        <ControlTemplate>
            <Grid Margin="0,-2,0,-2"
                  ToolTip="{Binding RelativeSource={RelativeSource FindAncestor,
                                               AncestorType={x:Type DataGridRow}},
                Path=(Validation.Errors)[0].ErrorContent}">
                <Ellipse StrokeThickness="0" Fill="Red" Width="{TemplateBinding FontSize}"
                                                     Height="{TemplateBinding FontSize}" />
                <TextBlock Text="!" FontSize="{TemplateBinding FontSize}" FontWeight="Bold"
                                        Foreground="White" HorizontalAlignment="Center"  />
            </Grid>
        </ControlTemplate>
    </DataGrid.RowValidationErrorTemplate>

    На рис 5.14 и рис 5.15 приведены результаты тестирования страницы PageEmployee при наличии ошибки ввода адреса электронной почты.

    (рис 5.15) Визуализация ошибки ввода при наведении указателя мыши на ячейки сетки(рис 5.14) Визуализация ошибки ввода при наведении указателя мыши на предупреждение для строки

    Поиск данных

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

    (рис 5.16) Дизайн страницы PageEmployee с элементами управления для поиска

    XAML-описание элементов управления, поддерживающих поиск имеет следующий вид.

    <TextBlock Name="TextBlockSurname" Text="Фамилия" />
    <TextBlock Name="TextBlockTitle" Text="Должность" />
    <TextBox Name="TextBoxSurname" TextChanged="TextBoxSurname_TextChanged"/>
    <ComboBox Name="ComboBoxTitle"
    ItemsSource="{Binding Source={StaticResource listTitle}}"
    DisplayMemberPath="Title1"
    SelectionChanged="ComboBoxTitle_SelectionChanged"/>
    <Button Name="ButtonFindSurname" ToolTip="Поиск по фамилии"
    IsEnabled="False" Click="ButtonFindSurname_Click">
    <Image Source="Images/Search.jpg"/>
    </Button>
    <Button Name="ButtonFindTitle" ToolTip="Поиск по должности"
    IsEnabled="False" Click="ButtonFindTitle_Click">
    <Image Source="Images/Search.jpg" />
    </Button>

    Элементы управления размещаем в рамке BorderFind, для которой делаем градиентную заливку. В рамке располагаем сетку gridFind с тремя колонками и строками. Первая строка в объединенных колонках содержит текстовый блок find с текстом "Поиск". Во второй и третьей строках первой колонки размещены текстовые блоки TextBlockSurname и TextBlockTitle соответственно. Во второй колонке размещаются элементы управления для ввода информации: текстовый блок TextBoxSurname (вторая строка) и выпадающий список ComboBoxTitle (третья строка). В третьей колонке размещаются кнопки: для поиска по фамилии ButtonFindSurname (вторая строка) и для поиска по должности ButtonFindTitle (третья строка).

    При загрузке страницы ).

    private void FindCommandBinding_Executed(object sender, 
    ExecutedRoutedEventArgs e)
            {
                BorderFind.Visibility = System.Windows.Visibility.Visible;
            }
    (рис 5.17) Страница PageEmployee с элементами управления для поиска

    Если в текстовом блоке TextBoxSurname не введена информация или в выпадающем списке ComboBoxTitle не сделан выбор, то кнопки ButtonFindSurname и ButtonFindTitle будут недоступны.

    При вводе информации в текстовый блок TextBoxSurname запускается обработчик TextBoxSurname_TextChanged, который делает доступной кнопку ButtonFindSurname.

    private void TextBoxSurname_TextChanged(object sender, TextChangedEventArgs e)
    {
        ButtonFindSurname.IsEnabled = true;
        ButtonFindTitle.IsEnabled = false;
        ComboBoxTitle.SelectedIndex = -1;
    }

    При нажатии кнопки ButtonFindSurname запускается обработчик ButtonFindSurname_Click.

    private void ButtonFindSurname_Click(object sender, RoutedEventArgs e)
    {
        string surname = TextBoxSurname.Text;
        DataEntitiesEmployee = new TitlePresonalEntities();
        ListEmployee.Clear();
        ObjectQuery<Employee> employees = DataEntitiesEmployee.Employees;
        var queryEmployee = from employee in employees
                                where employee.Surname == surname
                                select employee;
        foreach (Employee emp in queryEmployee)
        {
            ListEmployee.Add(emp);
        }
        if (ListEmployee.Count > 0)
        {
            DataGridEmployee.ItemsSource = ListEmployee;
            ButtonFindSurname.IsEnabled = true;
            ButtonFindTitle.IsEnabled = false;
        }
        else
            MessageBox.Show("Сотрудник с фамилией \n"+surname+"\n не найдан",
                 "Предупреждение", MessageBoxButton.OK, MessageBoxImage.Warning);
    }

    В данном обработчике события Click выполняется LINQ запрос на получение данных из базы TitlePersonal в соответствии с заданной фамилией сотрудника.

    var queryEmployee = from employee in employees
                            where employee.Surname == surname
                            select employee;

    При выполнении запроса по фамилии страница

    (рис 5.18) Поиск по фамилии

    Найденные данные по сотруднику можно редактировать и удалять.

    Если поиск в базе данных не привел к нахождению информации, то выдается информационное сообщение (рис 5.19).

    (рис 5.19) Неудачный поиск по фамилии

    Для поиска информации из базы данных по должности сотрудников необходимо сделать выбор из выпадающего списка ComboBoxTitle. В результате выбора срабатывает обработчик ComboBoxTitle_SelectionChanged, который делает доступной кнопку ButtonFindTitle.

    private void ComboBoxTitle_SelectionChanged(object sender, SelectionChangedEventArgs e)
    {
        ButtonFindTitle.IsEnabled = true;
        ButtonFindSurname.IsEnabled = false;
        TextBoxSurname.Text = "";
    }

    При нажатии на кнопку ButtonFindTitle срабатывает обработчик ButtonFindTitle_Click.

    private void ButtonFindTitle_Click(object sender, RoutedEventArgs e)
    {
        DataEntitiesEmployee = new TitlePresonalEntities();
        ListEmployee.Clear();
        
        Title title = ComboBoxTitle.SelectedItem as Title;
        ObjectQuery<Employee> employees = DataEntitiesEmployee.Employees;
        var queryEmployee = from employee in employees
                            where employee.TitleID == title.ID
                            orderby employee.Surname
                            select employee;
        foreach (Employee emp in queryEmployee)
        {
            ListEmployee.Add(emp);
        }
        DataGridEmployee.ItemsSource = ListEmployee;
    }

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

    (рис 5.20) Поиск сотрудников по должности

    Для обновления выводимого списка сотрудников в приложение добавлена функция обновления, которая реализуется командой Refresh. Данная команда добавлена в коллекцию команд страницы PageEmployee.

    <Page.CommandBindings>
    ....
            <CommandBinding Command="Refresh" Executed="RefreshCommandBinding_Executed" />
    ... 
        </Page.CommandBindings>

    Вызов команды добавлен в меню

    <MenuItem Header="Обновить" Command="Refresh"/>

    и панель инструментов

    <Button Name="Refresh" Margin="5,2,5,2"  Command="Refresh"
            ToolTip="обновить данные по сотрудникам">
        <Image Source="Images/Refresh.jpg" ></Image>
    </Button>

    Основное назначение обработчика команды RefreshCommandBinding_Executed – обновление списка ListEmployee.

    private void RefreshCommandBinding_Executed(object sender, 
    ExecutedRoutedEventArgs e)
    {
        RewriteEmployee();
        DataGridEmployee.IsReadOnly = false;
        isDirty = false;
        SetUnvisibilityFind();
    }

    Команда "Обновить" обычно используется после поиска данных или ввода данных по новому сотруднику.

    Ключевые термины

    Модель "сущность-связь", модель EDM, концептуальная модель, тип сущности, тип ассоциации, привязка данных, проверка ввода данных, правило проверки, поиск данных.

    Entity Data Model, EDM, ADO.NET, ObjectContext, ObservableCollection, ObjectQuery, Binding, ValidationRule

    Краткие итоги

    Рассмотрены вопросы построения EDM-модели в приложении. Выполнена привязка данных к интерфейсным элементам: текстовым, выпадающему списку, дате. Сконструирован код методов взаимодействия приложения с базой данных: редактирование, вставка, удаление данных и поиск данных. На основе пользовательских правил проиллюстриравана возможность проверки данных при их вводе.

    Ресурсы для углубленного изучения

  • Платформа ADO.NET Entity Framework // http://msdn.microsoft.com/ru-ru/library/bb399572.aspx.
  • Привязка данных // http://msdn.microsoft.com/ru-ru/library/cc278072(v=VS.95).aspx.
  • , стр. 315 – 363.
  • , стр. 535 – 588.
  • , стр. 329 – 357, 978 – 1004.
  • , стр. 740 – 779, 832 – 866.
  • , стр. 463 – 492, 857 – 890, 1048 - 1102
  • Вопросы для самопроверки

  • Объясните назначение и основные понятия EDM –модели.
  • Какие структуры и типы данных может описывать EDM-модель?
  • Какие основные понятия используются в модель EDM для описания структуры данных?
  • Поясните содержание и назначение типа сущности в EDM модели.
  • Поясните содержание и назначение типа ассоциации в EDM модели.
  • Поясните содержание и назначение свойств в EDM модели.
  • Как в проекте задается строка соединения с базой данных для EDM модели?
  • Для чего необходимо в проекте иметь свойство контекста данных EDM модели?
  • Поясните назначение и возможности коллекции типа ObservableCollection<Т>.
  • Поясните назначение и возможности класса ObjectQuery<Т>.
  • Поясните назначение и возможности свойства UpdateSourceTrigger, используемого при привязке данных.
  • Поясните особенности привязки данных к колонкам таблиц типа DataGridComboBoxColumn.
  • Поясните особенности привязки данных к колонкам таблиц с помощью шаблона DateTemplate.
  • С помощью какого метода фиксируются проведенные изменения в источнике данных?
  • Поясните назначение валидации данных.
  • Каким требованиям должен соответствовать класс, используемый для валидации данных?
  • Какими способами можно реализовать поиск и фильтрацию данных из источника данных?
  • Вернуться к учебному плану