В процессе выполнения работы необходимо разработать страничное/оконное WPF приложение.
Для разработки приложения необходимо создать WPF-проект (рис 6.1). В окне " ), выбрать шаблоны Windows (2 – на рис 6.1), а среди имеющихся шаблонов задать Приложение WPF и в поле " Имя " ввести имя проекта WpfApplProject.
(рис 6.1) Окно создания проектаПосле нажатия кнопки "ОК" будет сформирован шаблон проекта. При этом инструментальная система сгенерирует следующий
<Window x:Class="WpfApplProject.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
Title="MainWindow" Height="350" Width="525">
<Grid>
</Grid>
</Window>
Дизайнер проекта приведен на рис 6.2.
(рис 6.2) Окно дизайнера проектаВ приведенном
x:Class="WpfApplProject.MainWindow
два пространства имен
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" xmlns:x=http://schemas.microsoft.com/winfx/2006/xaml
и три свойства:
Title="MainWindow" Height="350" Width="525"
Каждый атрибут соответствует определенному свойству класса .
(рис 6.3) Окно MainWindow проектаКогда выполняется компиляция приложения,
Метод InitializeComponent() генерируется во время компиляции приложения и в исходном коде не присутствует.
Для программного управления элементами управления, описанными в
<Grid Name="grid">
</Grid>
Страницы приложения можно размещать внутри окон и внутри других страниц. В WPF при создании страничных приложений контейнером наивысшего уровня могут быть следующие объекты:
Для вставки страницы внутрь окна будем использовать класс ). Автоматически будет сгенерирован экземпляр класса ).

(рис 6.5) Выбор класса Frame в панели инструментов(рис 6.4) Фрейм с фиксированными границамиВ
<Frame Height="100" HorizontalAlignment="Left" Name="frame1" VerticalAlignment="Top" Width="200" />
С учетом того, что создается страничное приложение размеры фрейма не нужно фиксировать, поэтому изменим описание свойств фрейма:
<Frame Name="frame1" Margin="3" />
В результате фрейм заполнит всё окно (рис 6.6):
(рис 6.6) Фрейм, заполняющий всё окноСозданный фрейм необходим для размещения в нем страницы WPF-приложения. Класс Page допускает наличие только одного вложенного элемента. Он не является элементом управления содержимым и наследуется от класса FrameworkElement. Класс Page имеет небольшой набор дополнительных свойств, которые позволяют настраивать его внешний вид, взаимодействовать с контейнером и использовать навигацию. Для перехода на другую страницу необходимо использовать навигацию.
Добавьте в проект начальную страницу. Для этого в ), а в раскрывающимся меню - пункт ).
В окне ).

(рис 6.8) Меню создания страницы(рис 6.7) Окно Добавления нового элементаВ дизайнере проекта сгенерируется страница ).
(рис 6.9) Дизайнер страницы PageMain.xamlВ сгенерированной странице в качестве контейнера верхнего уровня используется Grid. Замените Grid на контейнер StackPanel.
(рис 6.10) Главная страница WPF-приложения в дизайнереИзмените свойство Title окна класса Window, в соответствии с вариантом лабораторной работы.
Основная страница должна обеспечивать переход на другие страницы, обеспечивающие инферфейс для отдельных функций и выход из системы. Для перехода на другие страцицы используйте гиперссылки. Гиперссылки позволяют пользователю перемещаться с одной страницы на другую. Элемент гиперссылки, соответствующий объекту класса Hyperlink, определяется следующей строкой:
<Hyperlink >Текст Гиперссылки</Hyperlink>
Класс Hyperlink имеет свойство NavigateUri. Данное свойство определяет на какую страницу будет переходить приложение при щелчке на соответствующей гиперссылке. Например, NavigateUri="Page2.xaml".
В WPF гиперссылки являются не отдельными, а внутристроковыми потоковыми элементами, которые должны размещаться внутри другого поддерживающего их элемента. Это можно сделать, например в элементе TextBlock, который для гиперссылки является контейнером.
На первой странице создайте гиперссылки для перехода на страницы приложния, в соответствии в заданием.
Создание основного меню. Создайте основное меню с помощью класса Menu, который представляет Windows элементы управления меню, позволяющие иерархически организовать элементы, связанные с командами и обработчиками событий. Меню формируют из объектов MenuItem (имя пункта меню) и Separator (разделитель). Класс MenuItem имеет свойство Header, которое определяет текст элемента меню. Данный класс может хранить коллекцию объектов MenuItem, которая соответствует подпунктам меню. Класс Separator отображает горизонтальную линию, разделяющую пункты меню.
Добавьте в StackPanel страницы приложения
<Menu>
<MenuItem Header="Действие" >
<MenuItem Header="Отменить" ></MenuItem>
<Separator></Separator>
<MenuItem Header="Создать" ></MenuItem>
<MenuItem Header="Редактировать" ></MenuItem>
<MenuItem Header="Сохранить" ></MenuItem>
<MenuItem Header="Найти" />
<Separator></Separator>
<MenuItem Header="Удалить" ></MenuItem>
</MenuItem>
<MenuItem Header="Отчет"></MenuItem>
</Menu>
Создание панели инструментов. Панель инструментов представляет специализированный контейнер для хранения коллекции элементов, обычно кнопок. Расположите в панели инструментов кнопки, функциональное назначение которых будет соответствовать подпунктам меню Действие, то есть Отменить, Создать, Редактировать, Сохранить, Найти и Удалить.
На лицевой стороне кнопок поместите графическое изображение соответствующего действия. Для этого добавьте в файл проекта папку Images и в неё включите графические объекты, которые можно найти в библиотеке графических объектов Visual Studio (папка VS2010ImageLibrary).
После добавления графических файлов в проект они будут отображены в обозревателе решений (рис 6.11).
(рис 6.11) Включение в проект папки Images с файлами рисунковДля каждой кнопки задайте свойство Name – имя объекта в программе и свойство ToolTip с текстом всплывающей подсказки при наведении указателя мыши на кнопку.
Свойство Margin определяет
<ToolBar Name="ToolBar1" Margin="3"> <Button Name="Undo" ToolTip="Отменить редактирование/создание" Margin="5,2,5,2"> <Image Source="Images/Undo.jpg" /> </Button> ... </ToolBar>
Проектирование интерфейсных элементов. При проектировании интерфейсных элементов для страницы приложения используются элементов контроля ListBox, ListView, TextBox, TextBlock, ComboBox, DataGrid и других.
Класс DataGrid представляет элемент управления, отображающий данные в настраиваемой сетке строк и столбцов. По умолчанию DataGrid автоматически создает столбцы, на основе источника данных. При этом генерируются следующие типы столбцов:
Если разработчика не устраивает автоматическая генерация столбцов DataGrid можно её отключить. Для этого свойству AutoGenerateColumns необходимо присвоить значение false. Далее можно создать собственный набор столбцов ( Columns ), используя существующие типы столбцов или создать новый тип столбца с помощью шаблона DataGridTemplateColumn.
DataGrid поддерживает множество способов настройки отображения данных.
Для реализации функциональности приложения необходимо для страницы приложения установить механизм запуска задач при выборе соответствующих пунктов меню и нажатии на кнопки панели инструментов. Технология WPF предлагает модель команд для выполнения такой привязки.
Модель команд обеспечивает
Классы, реализующие команды должны поддерживать интерфейс ICommand. В этом интерфейсе определены два метода Execute, CanExecute и событие CanExecuteChanged.
public interface ICommand1
{
void Execute(object par);
bool CanExecute(object par);
event EventHandler CanExecuteChanged;
}
Метод Execute может содержать код, реализующий бизнес-логику приложения. Метод CanExecute возвращает информацию о состоянии команды. Событие CanExecuteChanged вызывается при изменении состояния команды.
В WPF имеется библиотека базовых команд. Команды доступны через статические свойства следующих статических классов:
Для создания пользовательских команд целесообразно использовать классы RoutedCommand, который имеет
Для разработки пользовательских команд добавьте в проект папку Commands и в ней создайте класс DataCommands.
public class DataCommands
{
public static RoutedCommand Delete { get; set;}
public static RoutedCommand Edit { get; set; }
static DataCommands()
{
InputGestureCollection inputs = new InputGestureCollection();
inputs.Add(new KeyGesture(Key.E, ModifierKeys.Control, "Ctrl+E"));
edit = new RoutedCommand("Edit", typeof(DataCommands), inputs);
inputs = new InputGestureCollection();
inputs.Add(new KeyGesture(Key.D, ModifierKeys.Control, "Ctrl+D"));
delete = new RoutedCommand("Delete", typeof(DataCommands), inputs);
}
}
В классе DataCommands объявлены два свойства Delete и Edit типа RoutedCommand. Класс RoutedCommand определяет команду, реализующую ICommand. В конструкторе данного класса определяется объект inputs типа InputGestureCollection. Класс InputGestureCollection представляет упорядоченную коллекцию объектов InputGesture, которые позволяют с помощью класса KeyGesture задать комбинацию клавиш для вызова команды.
Для использования страницей приложения пользовательских команд в
xmlns:command="clr-namespace:WpfApplProject.Commands"
Теперь для страницы приложения сформируйте коллекцию объектов CommandBinding, которая осуществляет привязку команд для данного элемента и объявляет связь между командой, ее событиями и обработчиками.
<Page.CommandBindings>
<CommandBinding Command="Undo"
Executed="UndoCommandBinding_Executed"
CanExecute="SaveCommandBinding_CanExecute" />
...
</Page.CommandBindings>
Для класса CommandBinding свойство Command определяет ссылку на соответствующую команду, а свойства Executed и CanExecute задают обработчики событий при выполнении команды.
На странице приложения используются следующие команды: Отменить, Создать, Редактировать, Поиск, Сохранить и Удалить. Команды могут быть доступны или недоступны пользователю при работе приложения. Это проверяет метод CanExecute при генерации события CanExecuteChanged, которое вызывается при изменении состояния команды. Доступность команд определяется состоянием, в котором находится приложение. В тоже время выполнение какой-либо команды переводит, как правило, приложение в какое-либо другое состояние. Для проектируемого приложения можно определить следующие состояния:
В код программы класса страницы приложения введите логическое поле isDirty для управления доступностью команд.
private bool isDirty = true;
В код класса добавьте обработчики (реализация метода Executed ), определяющие бизнес-логику приложения. На данном этапе проектирования системы обработчики будут содержать только вывод сообщений о вызове команды и изменение поля isDirty. Код обработчика команды Удалить приведен ниже.
private void UndoCommandBinding_Executed(object sender,
ExecutedRoutedEventArgs e)
{
MessageBox.Show("Отмена");
isDirty = true;
}
В дальнейшем в обработчики необходима будет добавить код для обеспечения требуемой функциональности.
В коде класса необходимо добавить обработчики (реализация метода CanExecute ), которые управляют доступностью команд.
private void EditCommandBinding_CanExecute(object sender, CanExecuteRoutedEventArgs e)
{
e.CanExecute = isDirty;
}
private void SaveCommandBinding_CanExecute(object sender, CanExecuteRoutedEventArgs e)
{
e.CanExecute = !isDirty;
}
Необходимо модифицировать
При описании меню (Menu)
<Menu>
<MenuItem Header="Действие" BorderThickness="3" >
<MenuItem Header="Отменить" Command="Undo"></MenuItem>
...
</Menu>
Соответствующие изменения
<ToolBar Name="ToolBar1" Margin="3">
<Button Name="Undo" Margin="5,2,5,2" Command="Undo"
ToolTip="Отменить редактирование/создание" >
<Image Source="Images/Undo.jpg">
</Image>
</Button>
...
</ToolBar>
Для создания
(рис 6.12) Добавление в проект модели EDMВ мастере создания

(рис 6.14) Создание модели EDM из базы данных(рис 6.13) Создание соединения с базой данныхВ окне "Выбор
(рис 6.15) Выбор таблиц базы данныхПри завершении работы мастера создания
(рис 6.16) Выбор таблиц базы данныхАвтоматически сгенерированный класс TitlePresonalEntities, который наследуется от класса ObjectContext, представляет сущности базы данных TitlePerson, содержит свойства, моделирующие таблицы Employee и Title, связи между таблицами.
При создании
<?xml version="1.0" encoding="utf-8"?>
<configuration>
<connectionStrings>
<add name="TitlePresonalEntities" 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>
Для взаимодействия приложения с базой данных необходимо в коде класса страницы объявить статическое свойство контекста данных сформированной
Например:
public static TitlePresonalEntities DataEntitiesEmployee { get; set; }
Также необходимо объявить обобщенную коллекцию типа ObservableCollection<Employee> для работы приложения с коллекцией
using System.Collections.ObjectModel;
Экземпляры свойств контекста данных и коллекции необходимо создать в конструкторе класса страницы.
Например:
public PageEmployee()
{
InitializeComponent();
DataEntitiesEmployee = new TitlePresonalEntities();
ListEmployee = new ObservableCollection<Employee>();
}
Формирование данных для приложения, которые должны предоставляться из базы данных, буде проводиться при загрузке страницы приложения. Для этого в
Loaded="Page_Loaded"
В код класса страницы приложения включаем обработчик Page_Loaded.
private void Page_Loaded(object sender, RoutedEventArgs e)
{
ObjectQuery<Employee> employees = DataEntitiesEmployee.Employees;
var queryEmployee = from employee in employees
orderby employee.Surname
select employee;
foreach (Employee emp in queryEmployee)
{
ListEmployee.Add(emp);
}
DataGridEmployee.ItemsSource = ListEmployee;
}
Поле employees имеет тип ObjectQuery<Employee>. Класс ObjectQuery<Т> представляет запрос, возвращающий коллекцию типизированных сущностей с любым количеством элементов. Запрос сформируем с помощью технологии LINQ.
var queryEmployee = from employee in employees
orderby employee.Surname
select employee;
Результаты запроса целесообразно отсортировать, например по фамилии сотрудника ( orderby employee.Surname ). Далее формируем коллекцию
foreach (Employee emp in queryEmployee)
{
ListEmployee.Add(emp);
}
DataGridEmployee.ItemsSource = ListEmployee;
В результате проектирования в приложении сформирована коллекция с данными из таблицы базы данных.
Необходимо настроить сетку DataGrid для корректного отображения данных.
Модифицируйте общее описание DataGrid.
ItemsSource="{Binding}"
AutoGenerateColumns="False"
HorizontalAlignment="Left"
MaxWidth="1000" MaxHeight="295"
RowBackground="#FFE6D3EF" AlternatingRowBackground="#FC96CFD4"
BorderBrush="#FF1F33EB" BorderThickness="3"
RowHeight="25"
Модифицированное
Cursor="Hand"
<DataGrid Name="DataGridEmployee"
ItemsSource="{Binding}"
AutoGenerateColumns="False"
HorizontalAlignment="Left"
MaxWidth="1000" MaxHeight="295"
RowBackground="#FFE6D3EF"
AlternatingRowBackground="#FC96CFD4"
BorderBrush="#FF1F33EB"
BorderThickness="3"
IsReadOnly="True"
RowHeight="25"
Cursor="Hand">
Для каждого текстового столбца задайте свойство привязки Binding. В расширении разметки привязки данного свойства определите свойство класса источника даннных, к которому привязывается колонка. Далее укажите режим двусторонней привязки ( Mode=TwoWay ), который определяет синхронное обновление как источника, так и приемника привязки. Способ обновления источника привязки задается свойством UpdateSourceTrigger, которое принимает значение PropertyChanged, что соответствует немедленному обновлению источника привязки каждый раз при изменении свойства цели привязки.
Например:
<DataGridTextColumn Header="Фамилия"
Binding="{Binding Surname, Mode=TwoWay,
UpdateSourceTrigger=PropertyChanged}"/>
Например, для обеспечения возможности работы с коллекцией таблицы Title в приложении добавте в проект папку Model и в ней создайте класс ListTitle.
public class ListTitle: ObservableCollection<Title>
{
public ListTitle()
{
ObjectQuery<Title> titles = PageEmployee.DataEntitiesEmployee.Titles;
var queryTitle = from title in titles select title;
foreach (Title titl in queryTitle)
{
this.Add(titl);
}
}
}
Класс ListTitle наследуется от класса обобщенной коллекции ObservableCollection<T> и его назначение создавать коллекцию объектов Title. Поле titles является запросом типа ObjectQuery<Title>. Данному полю присваивается свойство Titles контекста данных DataEntitiesEmployee, который определен в
Запрос LINQ получает данные из базы данных в поле queryTitle и затем в цикле foreach формируется коллекция класса ListTitle.
Для использования класса ListTitle в
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").
При привязке даты ставится задача для невыделенной ячейки представлять дату в виде цифр дня, месяца и года, разделенных точками, а для выделенной ячейки – использовать класс для выбора даты.
Решение данной задачи осуществлено с помощью разработки двух шаблонов данных 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.
Ячейка столбца для отображения даты типа ). Если ячейка не выделена, то представление обычное текстовое (рис 6.17а). При выделении ячейки прорисовывается свернутое содержание класса б). При щелчке на ячейке появляется календарь (рис 6.17в).
(рис 6.17) Отображение даты в сетке DataGrid
Задание 5. Разработать методы манипулирования данными – 4 часа.
Для редактирования данных в приложении модифицируйте метод EditCommandBinding_Executed
private void EditCommandBinding_Executed(object sender,
ExecutedRoutedEventArgs e)
{
DataGridEmployee.IsReadOnly = false;
DataGridEmployee.BeginEdit();
isDirty = false;
Свойству IsReadOnly сетки DataGridEmployee присвойте значение false, что обеспечит возможность редактирования строк и ячеек сетки. Далее используйте метод BeginEdit для начала редактирования ячейки, на которую наведен указатель мыши.
Результаты редактирования необходимо сохранить в базе данных. Для этого необходимо вызвать метод SaveCommandBinding_Executed.
private void SaveCommandBinding_Executed(object sender,
ExecutedRoutedEventArgs e)
{
dataEntitiesEmployee.SaveChanges();
isDirty = true;
DataGridEmployee.IsReadOnly = true;
}
В методе SaveCommandBinding_Executed вызывается метод SaveChanges класса dataEntitiesEmployee. Метод SaveChanges сохраняет все обновления в источнике данных.
Для создания новых данных модифицируйте метод NewCommandBinding_Executed.
Например:
private void NewCommandBinding_Executed(object sender,
ExecutedRoutedEventArgs e)
{
Employee employee = Employee.CreateEmployee(-1, "не задано", "не задано",
"не задано", 0);
employee.Telephone = "не задано";
employee.Email = "не задано";
try
{
DataEntitiesEmployee.Employees.AddObject(employee);
ListEmployee.Add(employee);
isDirty = false;
}
catch (DataServiceRequestException ex)
{
throw new ApplicationException(
"Ошибка добавления данных" + ex.ToString());
}
}
В методе NewCommandBinding_Executed объект employee класса Employee создается с помощью метода CreateEmployee. В блоке try ... catch созданный объект employee добавляется в контекст данных методом AddObject сущности Employees
При инициализации команды создания данных в сетке формируется первоначальная строка с данными "по умолчанию". В сформированную строку необходимо ввести реальные данные сохранить их, вызвав команду "Сохранить".
Для удаления данных по сотруднику используем метод DeleteCommandBinding_Executed.
private void DeleteCommandBinding_Executed(object sender,
ExecutedRoutedEventArgs e)
{
Employee emp = DataGridEmployee.SelectedItem as Employee;
if (emp != null)
{
MessageBoxResult result = MessageBox.Show("Удалить данные ",
"Предупреждение", MessageBoxButton.OKCancel);
if (result == MessageBoxResult.OK)
{
DataEntitiesEmployee.DeleteObject(emp);
DataGridEmployee.SelectedIndex =
DataGridEmployee.SelectedIndex == 0 ? 1 : DataGridEmployee.SelectedIndex - 1;
ListEmployee.Remove(emp);
DataEntitiesEmployee.SaveChanges();
}
}
else
{
MessageBox.Show("Выберите строку для удаления");
}
}
В методе определяется объект
Если объект emp найден, то с помощью диалогового окна класса MessageBoxResult выводится сообщение с данными, которые предполагается удалить.
При подтверждении операции удаления объект emp удаляется из контекста методом DeleteObject.
DataEntitiesEmployee.DeleteObject(emp);
Объект emp удаляется из коллекции ListEmployee.
ListEmployee.Remove(emp);
Изменения вносятся в базу данных.
DataEntitiesEmployee.SaveChanges();
Метод UndoCommandBinding_Executed реализует отмену редактирования последних элементов сетки DataGridEmployee и переводит её в режим просмотра.
Для реализации данной функциональности необходимо провести изменения в коде приложения. Для метода Page_Loaded основной код выделите в метод GetEmployees.
private void GetEmployees()
{
ObjectQuery<Employee> employees = DataEntitiesEmployee.Employees;
var queryEmployee = from employee in employees
orderby employee.Surname
select employee;
foreach (Employee emp in queryEmployee)
{
ListEmployee.Add(emp);
}
DataGridEmployee.ItemsSource = ListEmployee;
}
С учетом созданного метода GetEmployees метод Page_Loaded будет иметь следующий вид.
private void Page_Loaded(object sender, RoutedEventArgs e)
{
GetEmployees();
DataGridEmployee.SelectedIndex = 0;
}
Создайте метод RewriteEmployee, который будет обновлять контекст данных и коллекцию ListEmployee.
private void RewriteEmployee()
{
DataEntitiesEmployee = new TitlePresonalEntities();
ListEmployee.Clear();
GetEmployees();
}
С учетом проведенных модификаций кода метод UndoCommandBinding_Executed будет иметь следующий вид.
private void UndoCommandBinding_Executed(object sender,
ExecutedRoutedEventArgs e)
{
RewriteEmployee();
DataGridEmployee.IsReadOnly = true;
isDirty = true;
}
При запуске команды Отмена будет отменено редактирование в текущей ячейке сетки, а если редактировались несколько ячеек, то при повторном запуске команды будет обновлена вся строка.
Разработку пользовательского правила
Спроектируйте правило проверки для привязки, которое будет использоваться со столбцом "Электронная почта". Для этого необходимо создать класс правила проверки 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>
Функцию поиска данных в приложений необходимо выполнить на основе данных, имеющихся в базе данных варианта лабораторной работы. параметры поиска необходимо согласовать с преподавателем.
Общий подход к поиску реализации поиска данных, получаемых из базы данных, рассмотрим на примере поиска сотрудника по фамилии и должности. Для реализации функций поиска добавьте на страницу приложения элементы контроля в соответствии с рис 6.18.
(рис 6.18) Дизайн страницы с элементами контроля для поиска<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 (третья строка).
При загрузке страницы PageEmployee рамка BorderFind невидима, так как свойству Visibility задано значение "Hidden". При активизации команды Find из меню или панели инструментов запускается обработчик FindCommandBinding_Executed, который делает рамку ButtonFindTitle видимой.
private void FindCommandBinding_Executed(object sender,
ExecutedRoutedEventArgs e)
{
BorderFind.Visibility = System.Windows.Visibility.Visible;
}
Если в текстовом блоке 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;
При выполнении запроса по фамилии страница
(рис 6.19) Поиск по фамилииНайденные данные по сотруднику можно редактировать и удалять.
Если
(рис 6.20) Неудачный поиск по фамилииДля поиска информации из базы данных по должности сотрудников необходимо сделать выбор из выпадающего списка 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-запрос к базе данных TitlePersonal для выборки данных по сотрудникам, имеющим заданную должность.
Для обновления выводимого списка сотрудников в приложение добавлена функция обновления, которая реализуется командой 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();
}
Команда "Обновить" обычно используется после поиска данных или ввода данных по новому сотруднику.
Для получения официальных документов о завершении программы дополнительного профессионального образования (удостоверения о повышении квалификации, дипломов о профессиональной переподготовке и MBA) необходимо предоставить:
Внимание! Вы можете не заказывать доставку бумажной версии официального документы, а скачать его в электронном виде и распечатать самостоятельно. Информация о выданном документе в течение 1 месяца загружается в Федеральную информационную систему «Федеральный реестр сведений о документах об образовании и (или) о квалификации, документах об обучении» - ФИС ФРДО.
Доступ на новый сайт осуществляется с использованием адреса электронной почты, который был указан вами при регистрации на "старом". Мы постарались перенести все ваши данные с прежнего ресурса, однако не исключена вероятность потери части информации.
При возникновении проблемы со входом, воспользуйтесь функцией сброса пароля
Если вы обнаружите несоответствия, пожалуйста, сообщите нам.