Формирование запросов к службе данных
Сервисы WCF Data Services позволяют выполнять запросы к службе данных из клиентского приложения на основе библиотеки .NET Framework с использованием сформированных клиентских классов службы данных. Клиентская библиотека преобразует запрос, определенный на клиенте как экземпляр класса DataServiceQuery, в сообщение запроса HTTP GET. Библиотека получает ответное сообщение и преобразует его в экземпляры классов клиентской службы данных. Эти классы отслеживаются экземпляром DataServiceContext, которому принадлежит объект DataServiceQuery.
Универсальный класс DataServiceQuery представляет запрос, который возвращает коллекцию экземпляров заданного типа сущности. Запрос к службе данных всегда относится к контексту существующей службы данных. Этот контекст поддерживает URI службы и сведения о метаданных, необходимые для создания и выполнения запроса.
При выполнении запросов можно явно вызывать метод асинхронного вызова BeginExecute с объектом DataServiceQuery<T> или выполнить запрос LINQ к именованному объекту DataServiceQuery<T>, который получен из контекста DataServiceContext.
Использование метода асинхронного вызова BeginExecute
Для загрузки в приложение таблицы Employee базы данных Personal добавим в коде класса MainPage ссылки на библиотеку Data.Services.Client и созданный сервис данных Personal
using System.Data.Services.Client;
using SilverlightAppPersonal.Personal;
а также контекст сущностей context сервиса модели данных EmployeeEntities и экземпляр коллекции employees класса DataServiceCollection<T>
EmployeeEntities context;
DataServiceCollection<Employee> employees;
После внесенных изменений код класса MainPage будет следующим:
using System.Windows.Controls;
using System.Data.Services.Client;
using SilverlightAppPersonal.Personal;
namespace SilverlightAppPersonal
{
public partial class MainPage : UserControl
{
EmployeeEntities context;
DataServiceCollection<Employee> employees;
public MainPage()
{
InitializeComponent();
}
}
}
Класс DataServiceCollection<T> представляет коллекцию динамических сущностей, обеспечивающую выдачу уведомлений при добавлении и удалении из неё элементов или при обновлении списка. Службы данных WCF используют класс DataServiceCollection<T> с целью поддержки привязки извлекаемых из базы данных для элементов управления Silverlight.
При загрузке главной страницы необходимо выполнить следующие подготовительные действия:
создать экземпляр контекста сущностей сервиса модели данных Personal ;
создать экземпляр коллекции employees ;
определить событие загрузки коллекции employees.
Добавим в XAML-код описание события Loaded класса MainPage со ссылкой на обработчик MainPage_Loaded:
Loaded="MainPage_Loaded"
В коде класса MainPage добавим обработчик MainPage_Loaded:
private void MainPage_Loaded(object sender, RoutedEventArgs e)
{
context = new EmployeeEntities(new Uri(
"WfcDataServicePerson.svc", UriKind.Relative));
employees = new DataServiceCollection<Employee>();
employees.LoadCompleted += new
EventHandler<LoadCompletedEventArgs>(employees_LoadCompleted);
ButtonOpen.IsEnabled = true;
}
С учетом того, что при генерации страницы все кнопки создаются как недоступные, то после загрузки необходимо кнопку загрузки данных из базы сделать доступной:
ButtonOpen.IsEnabled = true;
Загрузка данных из базы данных в приложение будет проведена по щелчку кнопки "Загрузить". В XAML-описании добавим для кнопки ButtonOpen обработчик для события Click - Open_Click.
<Button Content="Загрузить" Height="25" Name="ButtonOpen" Margin="35,0,0,0"
Padding="10,5,10,5" Click="Open_Click" />
В код класса MainPage добавим метод Open_Click обработчика события Click кнопки ButtonOpen.
private void Open_Click(object sender, RoutedEventArgs e)
{
ResetBindingData();
DataServiceQuery<Employee> queryEmployee =
context.Employees.AddQueryOption("$orderby", "EmployeeSurname");
try
{
queryEmployee.BeginExecute(OnEmployeeQueryComplete, queryEmployee);
}
catch (Exception ex)
{
MessageBox.Show(ex.Message);
}
}
Метод ResetBindingData() предназначен для очистка коллекции данных employees, списков сотрудников listBoxEmployees и должностей comboBoxTitle:
private void ResetBindingData()
{
employees.Clear();
listBoxEmployees.ItemsSource = null;
comboBoxTitle.ItemsSource = null;
LayoutRoot.UpdateLayout();
}
Далее формируется запрос queryEmployee на получение данных, при этом задается режим сортировки данных по фамилии сотрудника ( EmployeeSurname ):
DataServiceQuery<Employee> queryEmployee =
context.Employees.AddQueryOption("$orderby", "EmployeeSurname");
С помощью метода AddQueryOption в запрос добавляются параметры. Службы данных WCF поддерживают следующие параметры запросов, приведенные в табл. 6.1.
Таблица 7.1. Параметры запросов протокола Open Data |
|
Параметр запроса |
Описание |
$orderby |
Определяет порядок сортировки по умолчанию для сущностей в возвращенном канале |
$top |
Указывает количество сущностей, которые необходимо включить в возвращаемый канал |
$skip |
Указывает количество сущностей, которые необходимо пропустить перед возвратом сущностей в канал |
$filter |
Определяет выражение, фильтрующее сущности, которые возвращаются в канал на основе определенного критерия. Этот параметр запроса поддерживает набор операторов логического сравнения, арифметических операторов и заранее заданных функций запроса, которые используются для оценки критерия фильтра |
$expand |
Указываются связанные сущности, возвращаемые запросом. Связанные сущности включаются либо в качестве канала, либо в качестве записи, встроенной в сущность, возвращаемую запросом |
$format |
Указывает формат возвращаемого канала. По умолчанию для каналов задается формат Atom |
$select |
Указывает проекцию, определяющую свойства сущности, возвращаемые в проекции. По умолчанию в канале возвращаются все свойства сущности |
$inlinecount |
Запрашивает включение в поток количества сущностей, возвращаемых в канале |
Запросы службы данных WCF для Silverlight выполняются асинхронно. Это реализуется методом BeginExecute(), который использует в качестве параметров делегат OnEmployeeQueryComplete и запрос queryEmployee.
queryEmployee.BeginExecute(OnEmployeeQueryComplete, queryEmployee);
Делегат OnEmployeeQueryComplete использует класс Dispatcher для того, чтобы обеспечить асинхронное получение результата в правильном потоке.
private void OnEmployeeQueryComplete(IAsyncResult result)
{
Dispatcher.BeginInvoke(() =>
{
DataServiceQuery<Employee> queryEmployee =
result.AsyncState as DataServiceQuery<Employee>;
try
{
employees.LoadAsync(queryEmployee);
}
catch (DataServiceQueryException ex)
{
MessageBox.Show(string.Format("Ошибка запроса в БД: {0} - {1}",
ex.Response.StatusCode.ToString(), ex.Response.Error.Message));
}
});
}
На данном этапе нам осталось добавить в код класса MainPage делегат employees_LoadCompleted для события LoadCompleted коллекции employees, в котором после завершения формирования коллекции employees задается источник данных для списка listBoxEmployees и производится выделение первого элемента списка.
private void employees_LoadCompleted(object sender, LoadCompletedEventArgs e)
{
if (e.Error == null)
{
if (employees.Continuation != null)
{
employees.LoadNextPartialSetAsync();
}
else
{
listBoxEmployees.ItemsSource = employees;
listBoxEmployees.UpdateLayout();
if (listBoxEmployees.Items.Count > 0)
listBoxEmployees.SelectedIndex = 0;
ButtonOpen.IsEnabled = false;
}
}
else
{
MessageBox.Show(string.Format("Ошибка формирования коллекции: {0}",
e.Error.Message));
ButtonOpen.IsEnabled = true;
}
}
Использование запроса LINQ
Класс DataServiceQuery реализует интерфейс IQueryable, определяемый языком LINQ. Клиентская библиотека службы WCF Data Services может преобразовывать запросы LINQ к данным набора сущностей в URI, который представляет выражение запроса, вычисляемое для ресурса службы данных.
Сформируем запрос LINQ для получения приложением данных сущности Employee.
var allEmpoyees = from emp in context.Employees
orderby emp.EmployeeSurname
select emp;
Формирование коллекции employees класса DataServiceCollection<Employee> для приложения Silverlight может осуществляться только асинхронно, что определяет необходимость использования метода LoadAsync, который требует приведения параметра метода к типу DataServiceQuery<T>, в нашем случае к типу DataServiceQuery<Employee>.
employees.LoadAsync(allEmpoyees as DataServiceQuery<Employee>);
При асинхронной загрузке коллекции employees необходимо сформировать и обработать события загрузки коллекции типа employees.LoadCompleted для сущности Employee и события titles.LoadCompleted для сущности JobTitle.
Для случая использования запроса LINQ метод Open_Click() будет представлен следующим кодом.
private void Open_Click(object sender, RoutedEventArgs e)
{
ResetBindingData();
listBoxEmployees.ItemsSource = null;
comboBoxTitle.ItemsSource = null;
LayoutRoot.UpdateLayout();
try
{
var allEmpoyees = from emp in context.Employees
orderby emp.EmployeeSurname
select emp;
employees.LoadAsync(allEmpoyees as DataServiceQuery<Employee>);
var allTitle = from titl in context.JobTitles
select titl;
titles.LoadAsync(allTitle as DataServiceQuery<JobTitle>);
}
catch (DataServiceClientException ex)
{
MessageBox.Show(ex.Message);
}
}
Следует отметить, что по сравнению с загрузкой данных с использованием метода асинхронного вызова BeginExecute, при применение LINQ-запросов отпадает необходимость использования делегатов OnEmployeeQueryComplete и OnTitleQueryComplete.
http://www.intuit.ru/department/se/dawpfsl/7/4.html
Привязка данных
Для тестирования нашего приложения необходимо сделать привязку списка listBoxEmployees к данным. Для этого используем шаблон DataTemplate.
<ListBox Grid.Row="1" Name="listBoxEmployees" HorizontalAlignment="Center"
Margin="5,2,15,9" Padding="3" Width="364" >
<ListBox.ItemTemplate>
<DataTemplate>
<StackPanel Orientation="Horizontal">
<TextBlock Text="{Binding Path=EmployeeSurname}" />
<TextBlock Text=" " />
<TextBlock Text="{Binding Path=EmployeeName}" />
<TextBlock Text=" " />
<TextBlock Text="{Binding Path=EmployeePatronymic}" />
</StackPanel>
</DataTemplate>
</ListBox.ItemTemplate>
</ListBox>
В списке будем отображать фамилию, имя и отчества сотрудника.
Рис. 7.13. Фрагмент главной страницы с заполненным списком
Для привязки отображения отдельных данные из списка к детальным данным по сотруднику необходимо для контента основной Grid (LayoutRoot) выполнить привязку к текущему выделению списка listBoxEmployees:
<Grid x:Name="LayoutRoot" Background="White" Margin="10"
VerticalAlignment="Top" Height="590" Width="779"
DataContext="{Binding ElementName=listBoxEmployees, Path=SelectedItem, Mode=TwoWay}">
Начнем привязку детальных данных по сотруднику с текстовых полей: LastName, FirstName, SecondtName и NetName. Для элемента управления TextBox LastName привязка присоединяемого свойства Text к текущему выделению списка listBoxEmployees будет иметь следующий вид:
Text="{Binding Path= EmployeeSurname, Mode=TwoWay}"
Для объекта привязки Binding задается только привязываемое свойство ( Path=EmployeeSurname ), а элемент привязки был ранее определен в контенте основной Grid (свойство DataContext ). Свойство Mode задается значение TwoWay, что определяет двустороннюю привязку. Измененный XAML-код для интерфейсных TextBox примет следующий вид:
<TextBox Name="LastName" Height="25" Margin="5,2,2,2"
Text="{Binding Path=EmployeeSurname, Mode=TwoWay}"></TextBox>
<TextBox Name="FirstName" Height="25" Margin="2"
Text="{Binding Path=EmployeeName, Mode=TwoWay}"></TextBox>
<TextBox Name="SecondtName" Height="25" Margin="2"
Text="{Binding Path= EmployeePatronymic, Mode=TwoWay}"></TextBox>
<TextBox Name="NetName" Height="25" Margin="2"
Text="{Binding Path=NetName, Mode=TwoWay}"></TextBox>
После запуска приложения и данных страница примет вид, приведенный на рис. 7.14.
Рис. 7.14. Фрагмент главной страницы с детальными данными
В базе данных Person атрибут EmployeeStatus является целым числом, значение которого соответствует индексу списка ComboBox comboBoxStatus приложения. Функциональность приложения определяет следующий список статуса сотрудника: не задано, активен, выходной, в отпуске, болеет, не работает. Привязка для данного элемента управления выполняется для свойства SelectedIndex.
<ComboBox Name="comboBoxStatus" Height="25" Margin="2"
SelectedIndex = "{Binding Path=EmployeeStatus, Mode=TwoWay, BindsDirectlyToSource=True}">
<ComboBoxItem Content="не задано" />
<ComboBoxItem Content="активен" />
<ComboBoxItem Content="выходной" />
<ComboBoxItem Content="в отпуске" />
<ComboBoxItem Content="болеет" />
<ComboBoxItem Content="не работает" />
<ComboBoxItem Content="помечен как удаленный" />
</ComboBox>
Уровень доступа сотрудника к ресурсам информационной системы – Access в базе данных хранится в виде символьной строки. Однако перечень уровней доступа в системе фиксирован и меняется редко. Для обеспечения работы с ограниченным списком уровней доступа пользователей введем в приложение специальный класс Access. Для этого добавим в приложение SilverlightAppPersonal класс Access, имеющий свойство AccessRole, характеризующее уровень доступа сотрудника к ресурсам сети.
public class Access
{
public string AccessRole { set; get; }
public Access(string accessRole)
{
AccessRole = accessRole;
}
}
В коде класса MainPage добавим ссылку на библиотеку
System.Collections.ObjectModel.
using System.Collections.ObjectModel;
Затем определим коллекцию уровней доступа accesses.
ObservableCollection<Access> accesses;
В коде класса MainPage добавим ссылку на библиотеку System.Collections.ObjectModel.
using System.Collections.ObjectModel;
Затем определим коллекцию уровней доступа accesses.
ObservableCollection<Access> accesses;
В конструкторе класса MainPage создадим экземпляр коллекции accesses и сформируем её из следующего списка: не задано, оператор, старший оператор, начальник смены, администратор, аналитик. Измененный код конструктора класса MainPage приведен ниже.
public MainPage()
{
InitializeComponent();
accesses = new ObservableCollection<Access>();
accesses.Add(new Access("не задано"));
accesses.Add(new Access("оператор"));
accesses.Add(new Access("старший оператор"));
accesses.Add(new Access("начальник смены"));
accesses.Add(new Access("администратор"));
accesses.Add(new Access("аналитик"));
comboBoxAccess.ItemsSource = accesses;
}
После формирования коллекции её значение присваивается источнику данных выпадающего списка comboBoxAccess.
comboBoxAccess.ItemsSource = accesses;
В XAML-коде класса MainPage для выпадающего списка comboBoxAccess добавим привязку для свойства SelectedValue и зададим значение SelectedValuePath для вывода в ComboBox.
<ComboBox Height="25" Name="comboBoxAccess" Margin="5,2,2,2"
SelectedValue="{Binding Path=Access, Mode=TwoWay}"
SelectedValuePath="AccessRole" DisplayMemberPath="AccessRole " />
Должности сотрудников хранятся с отдельной таблице JobTitle базы данных Person. Перед загрузкой в приложение таблицы JobTitle немного усовершенствуем созданный ранее код. В методе Open_Click() всё, что связано с загрузкой в приложение данных таблицы Employee выделим в отдельный метод LoadEmployees():
private void LoadEmployees()
{
DataServiceQuery<Employee> queryEmployee =
context.Employees.AddQueryOption("$orderby", "EmployeeSurname");
try
{
queryEmployee.BeginExecute(OnEmployeeQueryComplete, queryEmployee);
}
catch (Exception ex)
{
MessageBox.Show(ex.Message);
}
}
С учетом введения метода LoadEmployees() метод Open_Click() примет следующий вид:
private void Open_Click(object sender, RoutedEventArgs e)
{
LoadEmployees();
}
Загрузка данных таблицы JobTitle выполняется аналогично загрузке данных таблицы Employee.
Определим в классе MainPage коллекцию titles.
DataServiceCollection<JobTitle> titles;
В методе MainPage_Loaded добавим код создания экземпляра titles коллекции
DataServiceCollection< JobTitle >:
titles = new DataServiceCollection<JobTitle>();
и зарегистрируем для коллекции событие LoadCompleted с делегатом titles_LoadCompleted:
titles.LoadCompleted +=new EventHandler<LoadCompletedEventArgs>(titles_LoadCompleted);
Измененный метод MainPage_Loaded будет иметь следующий вид:
private void MainPage_Loaded(object sender, RoutedEventArgs e)
{
context =
new PersonalEntities(new Uri("WfcDataServicePerson.svc", UriKind.Relative));
employees = new DataServiceCollection<Employee>();
titles = new DataServiceCollection<JobTitle>();
employees.LoadCompleted +=
new EventHandler<LoadCompletedEventArgs>(employees_LoadCompleted);
titles.LoadCompleted +=
new EventHandler<LoadCompletedEventArgs>(titles_LoadCompleted);
ButtonOpen.IsEnabled = true;
}
По аналогии с методом LoadEmployees() создадим метод LoadTitles().
private void LoadTitles()
{
DataServiceQuery<JobTitle> queryTitle = context.JobTitles;
try
{
queryTitle.BeginExecute(OnTitleQueryComplete, queryTitle);
}
catch (Exception ex)
{
MessageBox.Show(ex.Message);
}
}
Далее спроектируем код делегата асинхронного запроса к службе данных.
private void OnTitleQueryComplete(IAsyncResult result)
{
Dispatcher.BeginInvoke(() =>
{
DataServiceQuery<JobTitle> queryTitle = result.AsyncState as DataServiceQuery<JobTitle>;
try
{
titles.LoadAsync(queryTitle);
}
catch (DataServiceQueryException ex)
{
MessageBox.Show(string.Format("Ошибка запроса в БД: {0} - {1}",
ex.Response.StatusCode.ToString(), ex.Response.Error.Message));
}
}
);
}
Делегат обработки события формирования коллекции типа DataServiceCollection<JobTitle> будет иметь следующий вид:
private void titles_LoadCompleted(object sender, LoadCompletedEventArgs e)
{
if (e.Error == null)
{
if (titles.Continuation != null)
{
titles.LoadNextPartialSetAsync();
}
else
{
comboBoxTitle.ItemsSource = titles;
comboBoxTitle.UpdateLayout();
}
}
else
{
MessageBox.Show(string.Format("An error has occured: {0}", e.Error.Message));
}
}
Добавим в код метода Open_Click() вызов метода LoadTitles().
private void Open_Click(object sender, RoutedEventArgs e)
{
LoadEmployees();
LoadTitles();
}
В XAML-коде класса MainPage для выпадающего списка comboBoxTitle добавим привязку для свойства SelectedValue, SelectedValuePath и зададим значение DisplayMemberPath для вывода в ComboBox, а также обработчик события SelectionChanged:
<ComboBox Height="25" Name="comboBoxTitle" Margin="5,2,2,2"
SelectedValue="{Binding Path=JobRoleID, Mode=TwoWay}"
SelectedValuePath="ID" DisplayMemberPath="Title" />
Следует пояснить, что привязка SelectedValue="{Binding Path=JobRoleID, Mode=TwoWay}" и задание свойства для привязки SelectedValuePath="ID" используют поля JobRoleID и ID таблицы Employee через контент Grid, а выводимое в ComboBox значение задается как DisplayMemberPath="Title" и при этом используется атрибут Title таблицы JobTitle коллекции titles, являющейся источником данных для comboBoxTitle.
Для календаря DatePicker необходимо построить привязку для свойства SelectedDate.
<controls:DatePicker Name="datePickerFirstDate"
SelectedDate="{Binding Path=FirstDate, Mode=TwoWay}" Margin="5,2,2,2"/>
<controls:DatePicker Name="datePickerLastDate"
SelectedDate="{Binding Path=LastDate, Mode=TwoWay}" Margin="5,2,2,2" />
На этом привязка данных закончена и можно протестировать приложение. В результате запуска программы страница приложения должна иметь вид, приведенный на рис. 7.15.
Рис. 7.15. Фрагмент главной страницы с привязанными данными
Управление доступностью кнопок приложения
Silverlight не предлагает встроенную поддержку для модели команд WPF поэтому в разрабатываемом приложении функциональность, связанная с кнопками, должна реализовываться обработчиками событий, а доступность тех или иных кнопок управляться программно.
На странице приложения размещены шесть кнопок: Сохранить, Отменить, Создать, Загрузить, Редактировать и Удалить. Кнопки могут быть доступны или недоступны пользователю при работе приложения. Это задается свойством IsEnable. Доступность кнопок определяется состоянием, в котором находится приложение. В тоже время нажатие какой-либо кнопки переводит, как правило, приложение в какое-либо другое состояние. Для проектируемого приложения можно определить следующие состояния:
первоначальная загрузка страницы (1);
просмотр данных по всем сотрудникам (2);
редактирование данных по отдельному сотруднику (3);
создание новой записи по сотруднику в базе данных (4).
На рис. 7.16 приведена диаграмма состояний приложения, на которой кружками обозначены состояния, а дуги соответствуют переходам при нажатии определенной кнопки.
Рис. 7.16. Диаграмма состояний приложения
На основе диаграммы состояний построим таблицу доступности кнопок в различных состояниях (таблица 7.2).
Таблица 7.2. Доступность кнопок в различных состояниях приложения |
||||||
Состояние |
Доступность кнопок - IsEnable |
|||||
Сохранить |
Отменить |
Создать |
Загрузить |
Редактировать |
Удалить |
|
1 |
false |
false |
false |
true |
false |
false |
2 |
false |
false |
true |
false |
true |
true |
3 |
true |
true |
false |
false |
false |
false |
4 |
true |
true |
false |
false |
false |
false |
В XAML-коде класса MainPage все кнопки определены как недоступные, то есть свойство IsEnable –false. После загрузки страницы (состояние 1) кнопка Загрузить становится доступной, а после загрузки данных из базы во всех остальных состояниях она недоступна. Режим доступности кнопок Сохранить, Отменить, Создать, Редактировать и Удалить в состояниях 2,3 и 4 изменяется в соответствии данными, приведенными в табл. 6.2. Для управления доступностью кнопок добавим в код класса MainPage метод ButtonIsEnabled().
private void ButtonIsEnabled(bool enabele)
{
ButtonEdit.IsEnabled = enabele;
ButtonSave.IsEnabled = !enabele;
ButtonNew.IsEnabled = enabele;
ButtonUndo.IsEnabled = !enabele;
ButtonDelete.IsEnabled = enabele;
}
Кроме управления доступностью кнопок в приложении целесообразно управлять и возможностью редактирования детальных данных по сотруднику. Детальные данные должны быть доступны для редактирования в состояниях редактирования (3) и создания (4) новой записи, а в режиме просмотра – должны быть доступны только для чтения. Для управления доступностью детальных данных по сотруднику добавим в код класса MainPage метод ReadOnly().
private void ReadOnly(bool readOnly)
{
LastName1.IsReadOnly = !readOnly;
FirstName1.IsReadOnly = !readOnly;
SecondtName1.IsReadOnly = !readOnly;
NetName1.IsReadOnly = !readOnly;
comboBoxTitle.IsEnabled = readOnly;
comboBoxAccess.IsEnabled = readOnly;
comboBoxStatus.IsEnabled = readOnly;
datePickerFirstDate.IsEnabled = readOnly;
datePickerLastDate.IsEnabled = readOnly;
}
В XAML-коде класса MainPage для кнопок Сохранить, Отменить, Создать, Редактировать и Удалить добавим событие Click и обработчики событий Save_Click(), Undo_Click(), New_Click(), Edit_Click() и Delete_Click().
<Button Name="ButtonSave" Content="Сохранить" Margin="5" Padding="5"
Height="25" IsEnabled="False" Click="Save_Click" />
<Button Name="ButtonUndo" Content="Отменить" Margin="5" Padding="5"
Height="25" IsEnabled="False" Click="Undo_Click"/>
<Button Name="ButtonNew" Content="Создать" Margin="5" Padding="5"
Height="25" IsEnabled="False" Click="New_Click"/>
<Button Name="ButtonEdit" Content="Редактировать" Margin="5"
Padding="5" Height="25" IsEnabled="False" Click="Edit_Click"/>
<Button Name="ButtonDelete" Content="Удалить" Padding="5"
Margin="35,0,0,0" Height="25" IsEnabled="False" Click="Delete_Click" />
При нажатии кнопки Удалить не изменяется состояние приложения – это видно из рис. 7.16, где действию Удалить соответствует петля для состояния 2, поэтому на текущем этапе разработки приложения не будем определять код метода и Delete_Click(), а для остальных созданных методов добавим код для управления доступности кнопок и элементов управления.
private void Save_Click(object sender, RoutedEventArgs e)
{
ReadOnly(false);
ButtonIsEnabled(true);
listBoxEmployees.IsEnabled = true;
}
private void Undo_Click(object sender, RoutedEventArgs e)
{
ReadOnly(false);
ButtonIsEnabled(true);
listBoxEmployees.IsEnabled = true;
}
private void Edit_Click(object sender, RoutedEventArgs e)
{
ButtonIsEnabled(false);
listBoxEmployees.IsEnabled = false;
ReadOnly(true);
}
private void New_Click(object sender, RoutedEventArgs e)
{
ButtonIsEnabled(false);
ReadOnly(true);
listBoxEmployees.IsEnabled = false;
}
private void Delete_Click(object sender, RoutedEventArgs e)
{
MessageBoxResult result = MessageBox.Show("Удалить сотрудника: " +
emp.EmployeeSurname + " " + emp.EmployeeName + " " + emp.EmployeePatronymic,
"Предупреждение", MessageBoxButton.OKCancel);
if (result == MessageBoxResult.OK)
{
// Удалить выбранного сотрудника из коллекции данных
}
}
Кроме обработчиков событий кнопок необходимо внести изменения в код метода employees_LoadCompleted(), который завершает загрузку данных из базы по нажатию кнопки Загрузить.
private void employees_LoadCompleted(object sender, LoadCompletedEventArgs e)
{
if (e.Error == null)
{
if (employees.Continuation != null)
{
employees.LoadNextPartialSetAsync();
}
else
{
listBoxEmployees.ItemsSource = employees;
listBoxEmployees.UpdateLayout();
if (listBoxEmployees.Items.Count > 0)
listBoxEmployees.SelectedIndex = 0;
ButtonIsEnabled(true);
ReadOnly(false);
ButtonOpen.IsEnabled = false;
}
}
else
{
MessageBox.Show(string.Format("An error has occured: {0}", e.Error.Message));
ButtonOpen.IsEnabled = true;
}
}