При выполнении в лабораторной работе упражнения №1 , а именно при выполнении нижеследующего кода: using System; using System.Collections.Generic; using System.ComponentModel; using System.Data; using System.Drawing; using System.Text; using System.Windows.Forms; using Microsoft.Xna.Framework.Graphics;
namespace Application1 { public partial class MainForm : Form { // Объявим поле графического устройства для видимости в методах GraphicsDevice device;
public MainForm() { InitializeComponent();
// Подпишемся на событие Load формы this.Load += new EventHandler(MainForm_Load);
// Попишемся на событие FormClosed формы this.FormClosed += new FormClosedEventHandler(MainForm_FormClosed); }
void MainForm_FormClosed(object sender, FormClosedEventArgs e) { // Удаляем (освобождаем) устройство device.Dispose(); // На всякий случай присваиваем ссылке на устройство значение null device = null; }
void MainForm_Load(object sender, EventArgs e) { // Создаем объект представления для настройки графического устройства PresentationParameters presentParams = new PresentationParameters(); // Настраиваем объект представления через его свойства presentParams.IsFullScreen = false; // Включаем оконный режим presentParams.BackBufferCount = 1; // Включаем задний буфер // для двойной буферизации // Переключение переднего и заднего буферов // должно осуществляться с максимальной эффективностью presentParams.SwapEffect = SwapEffect.Discard; // Устанавливаем размеры заднего буфера по клиентской области окна формы presentParams.BackBufferWidth = this.ClientSize.Width; presentParams.BackBufferHeight = this.ClientSize.Height;
// Создадим графическое устройство с заданными настройками device = new GraphicsDevice(GraphicsAdapter.DefaultAdapter, DeviceType.Hardware, this.Handle, presentParams); }
protected override void OnPaint(PaintEventArgs e) { device.Clear(Microsoft.Xna.Framework.Graphics.Color.CornflowerBlue);
base.OnPaint(e); } } } Выбрасывается исключение: Невозможно загрузить файл или сборку "Microsoft.Xna.Framework, Version=3.0.0.0, Culture=neutral, PublicKeyToken=6d5c3888ef60e27d" или один из зависимых от них компонентов. Не удается найти указанный файл. Делаю все пунктуально. В чем может быть проблема? |
Привязка WPF к таблице данных ADO.NET
Вкладка Page4. Привязка к таблице Employees через набор данных DataSet
Источником для свойства DataContext можно назначить набор DataSet инфраструктуры ADO.NET, а не DataTable. DataSet считается аналогом реляционной БД, только находится в оперативной памяти. Поскольку объект DataSet способен одновременно инкапсулировать много различных таблиц-объектов DataTable (и воспроизводить отношения между ними), то в выражениях привязки интерфейсных элементов XAML следует делать уточнения, к какой именно таблице набора привязываться. Рассмотрим это на примере вкладки Page4.
Загрузим в набор данных ADO.NET три таблицы сразу, которые в дальнейшем, при необходимости, можно будет использовать для рассмотрения других способов привязки. Но в текущей вкладке Page4 привяжем для отображения только одну таблицу Employees, чтобы глубже почувствовать разницу с предыдущими способами на одинаковом результате. Мы уже неоднократно убеждались в том, что .NET Framework позволяет решать одну и ту же задачу разными способами - поистине мощная библиотека.
- Дополните класс StoreNorthwindDB в одноименном файле новым методом ReadDataSet() со следующим содержимым
//********************************************************* // Метод извлечения нетипизированного набора // данных из хранилища данных Northwind.mdb //********************************************************* DataSet ds = null;// Ссылка на нетипизированный набор DataSet public DataSet ReadDataSet() { // Загрузим набор данных только один раз if (ds != null) return ds; ds = new DataSet();// Создаем множественный набор данных // Заполняем множественный набор данных из БД using (OleDbConnection conn = new OleDbConnection(connectionString)) { OleDbCommand selectCommand = conn.CreateCommand(); OleDbDataAdapter adapter = new OleDbDataAdapter(selectCommand); // Загружаем всю таблицу Employees selectCommand.CommandText = "SELECT * FROM Employees"; adapter.Fill(ds);// Сам открывает, загружает и закрывает // Назначаем загруженным данным в наборе такое же имя, // как и в хранилище, чтобы в дальнейшем не путаться ds.Tables[0].TableName = "Employees"; // Загружает всю таблицу Customers selectCommand.CommandText = "SELECT * FROM Customers"; // Назначаем загружаемым данным в наборе имя, в этом способе // надо переопределить дежурное имя Table до заполнения набора adapter.TableMappings.Add("Table", "Customers");// Должен стоять перед adapter.Fill(ds); // Должен стоять после // Загружает всю таблицу Orders selectCommand.CommandText = "SELECT * FROM Orders"; // Загружаем и сразу этой порции данных назначаем имя в наборе adapter.Fill(ds, "Orders"); // Для проверки способов именования в панели Output режима Debug System.Diagnostics.Debug.WriteLine(ds.Tables[0].TableName); System.Diagnostics.Debug.WriteLine(ds.Tables[1].TableName); System.Diagnostics.Debug.WriteLine(ds.Tables[2].TableName); return ds; } }
Чтобы развеять сомнения в том, что таблицы действительно загружены в набор данных и получили заданные имена, в конце метода ReadDataSet() добавлен отладочный код, который работает только для режима Debug. Он выводит результат в панель Output оболочки и его удалять необязательно, потому что в рабочем режиме Release он сам отключится.
- Чтобы проверить работу метода, вставьте его вызов в обработчик Window_Loaded() класса Window1 файла Window1.xaml.cs так
private void Window_Loaded(object sender, RoutedEventArgs e) { Page1(); Page2(); Page3(); App.StoreNorthwindDB.ReadDataSet(); }
- Запустите проект и проверьте отладочный вывод в панели Output (панель можно открыть командой меню оболочки View/Output )
В конце отладочной информации найдем строки
Employees
Customers
Orders
Можно для пробы поменять имена таблиц TableName в наборе данных объектной модели метода и убедиться, что код верно реагирует на эти изменения. При этом строки самих SQL -запросов трогать не следует, поскольку это зона ответственности самого хранилища данных, а не объектной модели ADO.NET.
Теперь осталось сделать вкладку Page4 для размещения интерфейсных элементов отображения данных и привязать к ним заполненный набор данных.
- Добавьте в контейнер TabControl файла Window1.xaml копию вкладки Page3 и сделайте очевидные переименования, чтобы разметка новой вкладки Page4 стала такой
<!-- Привязка к таблице Employees через объект DataSet из ADO.NET --> <TabItem Header="Page4"> <Grid DataContext="{Binding ElementName=listEmployees4, Path=SelectedItem}" > <Grid.ColumnDefinitions> <ColumnDefinition Width="Auto" /> <ColumnDefinition /> </Grid.ColumnDefinitions> <Grid.RowDefinitions> <RowDefinition Height="*" /> <RowDefinition Height="Auto" /> <RowDefinition Height="Auto" /> <RowDefinition Height="Auto" /> <RowDefinition Height="Auto" /> <RowDefinition Height="Auto" /> </Grid.RowDefinitions> <ListBox Grid.Row="0" Grid.Column="0" Grid.ColumnSpan="2" Margin="0,0,0,3" ScrollViewer.VerticalScrollBarVisibility="Auto" Name="listEmployees4" ItemsSource="{Binding}" DisplayMemberPath="FullName" /> <TextBlock Grid.Row="1" Margin="0,0,5,0">EmployeeID:</TextBlock> <TextBox Grid.Row="1" Grid.Column="1" Text="{Binding Path=EmployeeID, Mode=OneWay}" Focusable="False" /> <TextBlock Grid.Row="2">FullName:</TextBlock> <TextBox Grid.Row="2" Grid.Column="1" Text="{Binding Path=FullName, Mode=OneWay}" Focusable="False" /> <TextBlock Grid.Row="3">Address:</TextBlock> <TextBox Grid.Row="3" Grid.Column="1" Text="{Binding Path=Address, Mode=OneWay}" Focusable="False" /> <TextBlock Grid.Row="4">BirthDate:</TextBlock> <TextBox Grid.Row="4" Grid.Column="1" Text="{Binding Path=BirthDate, Mode=OneWay}" Focusable="False" /> <TextBlock Grid.Row="5">Region:</TextBlock> <TextBox Grid.Row="5" Grid.Column="1" Text="{Binding Path=Region, Mode=OneWay}" Focusable="False" /> </Grid> </TabItem>
Заметим, что список ListBox имеет имя для того, чтобы в процедурном коде к нему можно было обратиться и присвоить свойству DataContext значение источника привязки - набора данных.
#region Вкладка Page4 private void Page4() { // Настраиваем списковый элемент ListBox listEmployees4.SelectedIndex = 0; listEmployees4.Focus(); // Назначаем источником набор данных DataSet listEmployees4.DataContext = App.StoreNorthwindDB.ReadDataSet(); } #endregion
- Примените в обработчике события Loaded класса Window1 вызов функции Page4()
private void Window_Loaded(object sender, RoutedEventArgs e) { Page1(); Page2(); Page3(); //App.StoreNorthwindDB.ReadDataSet(); Page4(); }
- Измените дескриптор списка вкладки Page4 следующим образом
Было |
<ListBox Grid.Row="0" Grid.Column="0" Grid.ColumnSpan="2" Margin="0,0,0,3" ScrollViewer.VerticalScrollBarVisibility="Auto" Name="listEmployees4" ItemsSource="{Binding}" DisplayMemberPath="FullName" /> |
---|---|
Стало |
<ListBox Grid.Row="0" Grid.Column="0" Grid.ColumnSpan="2" Margin="0,0,0,3" ScrollViewer.VerticalScrollBarVisibility="Auto" Name="listEmployees4" ItemsSource="{Binding Path=Employees}" DisplayMemberPath="FullName" /> |
- Запустите приложение - вкладка Page4 стала работать, но только частично
Текстовые поля TextBox функционируют (кроме поля FullName ) и список тоже, только в нем не отображается информация, установленная в настройках DisplayMemberPath="FullName".
- При запущенном приложении загляните в панель Output оболочки
Мы видим, что механизм привязки хоть и не возбуждает исключений, но под управлением оболочки все-таки сигнализирует об ошибке, выдавая серию предупреждений, примерно таких
System.Windows.Data Error: 39 : BindingExpression path error: 'FullName' property not found on 'object' ''DataRowView' (HashCode=48835636)'. BindingExpression:Path=FullName; DataItem='DataRowView' (HashCode=48835636); target element is 'TextBox' (Name=''); target property is 'Text' (type 'String')
- Установите в разметке настроек списка ListBox вкладки Page4 другое значение отображаемого столбца объектной модели источника
Было |
<ListBox Grid.Row="0" Grid.Column="0" Grid.ColumnSpan="2" Margin="0,0,0,3" ScrollViewer.VerticalScrollBarVisibility="Auto" Name="listEmployees4" ItemsSource="{Binding Path=Employees}" DisplayMemberPath="FullName" /> |
---|---|
Стало |
<ListBox Grid.Row="0" Grid.Column="0" Grid.ColumnSpan="2" Margin="0,0,0,3" ScrollViewer.VerticalScrollBarVisibility="Auto" Name="listEmployees4" ItemsSource="{Binding Path=Employees}" DisplayMemberPath="FirstName" /> |
- Запустите приложение - список стал отображать столбец FirstName, который был загружен из хранилища, а текстовый элемент для поля FullName, которое мы хотели показать, попрежнему остается пустым
Причина тут в том, что физически столбца с именем FullName ни в хранилище, ни в объектной модели набора данных просто не сущестует. Он является вычислимым, то есть формируется налету при извлечении данных. Ранее мы его могли использовать потому, что заранее придумали в выражении SQL -запроса и он попадал в коллекцию источника уже заполненным.