Освоить основные приемы построения модели данных, реализации привязки интерфейсных элементов к данным, конструирования методов взаимодействия приложения с базой данных и проверки данных при их вводе.
Взаимодействие с источником данных WCF-приложения базируется на модели , , . Модель
Модель
Тип сущности используется для описания структуры данных при помощи модели
Тип ассоциации применяется для построения описаний связей в модели
Типы сущностей содержат свойства, которые определяют их структуру и характеристики. Например,
Свойства в
приведена схема
(рис 5.1) Концептуальная модель базы данныхТаблица Employee содержит данные по сотруднику. Атрибутами таблицы являются:
Таблица Title является справочником должностей, имеющихся на предприятии, и включает следующие атрибуты:
Для создания
(рис 5.2) Добавление в проект модели EDMВ мастере создания

(рис 5.4) Создание модели EDM из базы данных(рис 5.3) Создание соединения с базой данныхВ окне "Выбор
(рис 5.5) Выбор таблиц базы данныхПри завершении работы мастера создания
(рис 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 сформированной
public static TitlePresonalEntities DataEntitiesEmployee { get; set; }
Также необходимо объявить коллекцию ListEmployee для работы приложения с коллекцией объектов Employee.
public PageEmployee()
{
DataEntitiesEmployee = new TitlePresonalEntities();
InitializeComponent();
ListEmployee = new ObservableCollection<Employee>();
}
Формирование данных для приложения, которые должны предоставляться из базы данных, будем создавать при загрузке страницы PageEmployee. Для этого в
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 с привязанными в таблице текстовыми полями
Для обеспечения возможности работы с коллекцией в таблицы 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 в
xmlns:core ="clr-namespace:WpfApplProject.Model"
Затем определим ресурс страницы с ключом listTitle.
<Page.Resources> <core:ListTitle x:Key="listTitle" /> </Page.Resources>
Далее модифицируем
<DataGridComboBoxColumn Header="Должность"
ItemsSource="{Binding Source={StaticResource listTitle}}"
DisplayMemberPath="Title1"
SelectedValueBinding="{Binding Path=TitleID, Mode=TwoWay,
UpdateSourceTrigger=PropertyChanged}"
SelectedValuePath="ID" />
Источник выпадающего списка задается как статический ресурс {StaticResource listTitle}. Выводимое в ячейки колонки поле должно соответствовать полю Title1 таблицы Title
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>
Разработанные шаблоны необходимо добавить к ресурсам в
<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
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("Выберите строку для удаления");
}
}
В методе определяем объект
Employee emp = DataGridEmployee.SelectedItem as Employee;
Если объект emp найден, то с помощью диалогового окна класса MessageBoxResult выводим сообщение с данными о сотруднике, которого предполагается удалить.
MessageBoxResult result = MessageBox.Show("Удалить сотрудника: " +
emp.Surname + " " + emp.Name + " " + emp.Patronymic, "Предупреждение", MessageBoxButton.OKCancel);
При подтверждении операции удаления объект
DataEntitiesEmployee.DeleteObject(emp);
Далее формируется в сетке DataGridEmployee выделяемая строка.
DataGridEmployee.SelectedIndex =
DataGridEmployee.SelectedIndex == 0 ? 1 : DataGridEmployee.SelectedIndex - 1;
Объект
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;
}
При запуске команды Отмена будет отменено редактирование в текущей ячейке сетки, а если редактировались несколько ячеек, то при повторном запуске команды будет обновлена вся строка.
При вводе и редактировании данных важной задачей эффективного интерфейса является
Поставим задачу проверки шаблона при вводе свойства 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 в
xmlns:rule="clr-namespace:WpfApplProject.ValidationRules"
Внесем изменения в
<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 имеются стандартные стили для отображения ячеек сетки при возникновении ошибки ввода, но они являются недостаточно информативными. Для более эффектного выделения ячейки сетки при ошибке ввода создадим специальный стиль 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 с элементами управления для поиска<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) Неудачный поиск по фамилииДля поиска информации из базы данных по должности сотрудников необходимо сделать выбор из выпадающего списка 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();
}
Команда "Обновить" обычно используется после поиска данных или ввода данных по новому сотруднику.
Модель "
Entity
Рассмотрены вопросы построения
Освоить основные приемы построения модели данных, реализации привязки интерфейсных элементов к данным, конструирования методов взаимодействия приложения с базой данных и проверки данных при их вводе.
Взаимодействие с источником данных WCF-приложения базируется на модели , , . Модель
Модель
Тип сущности используется для описания структуры данных при помощи модели
Тип ассоциации применяется для построения описаний связей в модели
Типы сущностей содержат свойства, которые определяют их структуру и характеристики. Например,
Свойства в
приведена схема
(рис 5.1) Концептуальная модель базы данныхТаблица Employee содержит данные по сотруднику. Атрибутами таблицы являются:
Таблица Title является справочником должностей, имеющихся на предприятии, и включает следующие атрибуты:
Для создания
(рис 5.2) Добавление в проект модели EDMВ мастере создания

(рис 5.4) Создание модели EDM из базы данных(рис 5.3) Создание соединения с базой данныхВ окне "Выбор
(рис 5.5) Выбор таблиц базы данныхПри завершении работы мастера создания
(рис 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 сформированной
public static TitlePresonalEntities DataEntitiesEmployee { get; set; }
Также необходимо объявить коллекцию ListEmployee для работы приложения с коллекцией объектов Employee.
public PageEmployee()
{
DataEntitiesEmployee = new TitlePresonalEntities();
InitializeComponent();
ListEmployee = new ObservableCollection<Employee>();
}
Формирование данных для приложения, которые должны предоставляться из базы данных, будем создавать при загрузке страницы PageEmployee. Для этого в
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 с привязанными в таблице текстовыми полями
Для обеспечения возможности работы с коллекцией в таблицы 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 в
xmlns:core ="clr-namespace:WpfApplProject.Model"
Затем определим ресурс страницы с ключом listTitle.
<Page.Resources> <core:ListTitle x:Key="listTitle" /> </Page.Resources>
Далее модифицируем
<DataGridComboBoxColumn Header="Должность"
ItemsSource="{Binding Source={StaticResource listTitle}}"
DisplayMemberPath="Title1"
SelectedValueBinding="{Binding Path=TitleID, Mode=TwoWay,
UpdateSourceTrigger=PropertyChanged}"
SelectedValuePath="ID" />
Источник выпадающего списка задается как статический ресурс {StaticResource listTitle}. Выводимое в ячейки колонки поле должно соответствовать полю Title1 таблицы Title
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>
Разработанные шаблоны необходимо добавить к ресурсам в
<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
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("Выберите строку для удаления");
}
}
В методе определяем объект
Employee emp = DataGridEmployee.SelectedItem as Employee;
Если объект emp найден, то с помощью диалогового окна класса MessageBoxResult выводим сообщение с данными о сотруднике, которого предполагается удалить.
MessageBoxResult result = MessageBox.Show("Удалить сотрудника: " +
emp.Surname + " " + emp.Name + " " + emp.Patronymic, "Предупреждение", MessageBoxButton.OKCancel);
При подтверждении операции удаления объект
DataEntitiesEmployee.DeleteObject(emp);
Далее формируется в сетке DataGridEmployee выделяемая строка.
DataGridEmployee.SelectedIndex =
DataGridEmployee.SelectedIndex == 0 ? 1 : DataGridEmployee.SelectedIndex - 1;
Объект
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;
}
При запуске команды Отмена будет отменено редактирование в текущей ячейке сетки, а если редактировались несколько ячеек, то при повторном запуске команды будет обновлена вся строка.
При вводе и редактировании данных важной задачей эффективного интерфейса является
Поставим задачу проверки шаблона при вводе свойства 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 в
xmlns:rule="clr-namespace:WpfApplProject.ValidationRules"
Внесем изменения в
<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 имеются стандартные стили для отображения ячеек сетки при возникновении ошибки ввода, но они являются недостаточно информативными. Для более эффектного выделения ячейки сетки при ошибке ввода создадим специальный стиль 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 с элементами управления для поиска<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) Неудачный поиск по фамилииДля поиска информации из базы данных по должности сотрудников необходимо сделать выбор из выпадающего списка 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();
}
Команда "Обновить" обычно используется после поиска данных или ввода данных по новому сотруднику.
Модель "
Entity
Рассмотрены вопросы построения
Для получения официальных документов о завершении программы дополнительного профессионального образования (удостоверения о повышении квалификации, дипломов о профессиональной переподготовке и MBA) необходимо предоставить:
Внимание! Вы можете не заказывать доставку бумажной версии официального документы, а скачать его в электронном виде и распечатать самостоятельно. Информация о выданном документе в течение 1 месяца загружается в Федеральную информационную систему «Федеральный реестр сведений о документах об образовании и (или) о квалификации, документах об обучении» - ФИС ФРДО.
Доступ на новый сайт осуществляется с использованием адреса электронной почты, который был указан вами при регистрации на "старом". Мы постарались перенести все ваши данные с прежнего ресурса, однако не исключена вероятность потери части информации.
При возникновении проблемы со входом, воспользуйтесь функцией сброса пароля
Если вы обнаружите несоответствия, пожалуйста, сообщите нам.