|
При загрузке данных из БД возникает исключение InvalidOperationException с сообщением: Элемент коллекции должен быть пустым перед использованием ItemsSource. Знаю, что для заполнения DataGrid можно использовать коллекции Items или ItemsSource, но одновременно их использовать нельзя: если задано значение для свойства ItemsSource и в коде C# добавляется элемент в Items, возникает исключение. |
Разработка Silverlight-приложения
Задание 4. Произвести привязку данных к элементам контроля – 2 часа
Привязка данных к элементу списка ListBox. При использовании в приложении списка ListBox необходимо выполнить привязку к нему данных. Методика привязки данных к списку ListBox рассматривается на примере списка сотрудников базы данных Personal.
Для привязки используем шаблон 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>В списке должны отображаться фамилия имя и отчества сотрудника.
Для привязки отображения отдельных данные из списка к детальным данным по сотруднику необходимо для контента основной 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}">Для элемента управления TextBox привязка присоединяемого свойства Text к текущему выделению списка listBox будет иметь следующий вид:
Text="{Binding Path= EmployeeSurname, Mode=TwoWay}"Для объекта привязки Binding задается только привязываемое свойство ( Path=EmployeeSurname ), а элемент привязки был ранее определен в контенте основной Grid (свойство DataContext ). Свойство Mode задается значение TwoWay, что определяет двустороннюю привязку.
Привязка данных к элементу ComboBox. При использовании в приложении выпадающего списка ComboBox необходимо выполнить привязку к нему данных. Методика привязуки данных к выпадающего списка ComboBox рассматривается на примере списка должностей сотрудников базы данных Personal.
Для обеспечения работы с ограниченным списком добавьте в приложение специальный класс 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 создайте экземпляр коллекции 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;
}После формирования коллекции 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 " />Привязка данных из связанных таблиц. При использовании в приложении связанных таблиц необходимо выполнить привязку к ним данных. Методика привязуки данных к связанным таблицам рассматривается на примере списка сотрудников базы данных Personal.
Должности сотрудников хранятся с отдельной таблице 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" />