При выполнении в лабораторной работе упражнения №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" или один из зависимых от них компонентов. Не удается найти указанный файл. Делаю все пунктуально. В чем может быть проблема? |
Разработка приложений ADO.NET для OLE DB
Связывание элементов представления с набором данных
Обратите внимание, что при выполнении предыдущих разделов мы заполняли набор данных из двух полей таблицы Customers в соответствии с командой Select поставщика oleDbDataAdapter1: поля CustomerID и CompanyName, а использовали только данные поля CompanyName, настроив свойство DisplayMember объекта списка listBox согласно приведенной ранее таблице
Пришла пора присоединить к объекту listBox и поле CustomerID, значения которого будут заполняться в коллекцию Items списка. Эта коллекция не будет видна пользователю, но значение ее любого элемента всегда можно получить для выделенной строки списка и в дальнейшем использовать как параметр в команде второго поставщика.
- Выделите на форме объект listBox и установите его свойство ValueMember в значение Customers.CustomerID через раскрывающийся список
- Выделите объект listBox и через панель Properties в режиме Events создайте для события SelectedIndexChanged обработчик, который заполните так
private void listBox_SelectedIndexChanged(object sender, EventArgs e) { // Очистить второй набор данных dataSet21.Clear(); // Если выбран элемент списка, заполнить второй набор if (listBox.SelectedIndex != -1) { oleDbSelectCommand2.Parameters[0].Value = listBox.SelectedValue; oleDbDataAdapter2.Fill(dataSet21); } }
- Запустите приложение - вы увидите пустые значения текстовых полей
Это происходит потому, что хоть второй набор данных и заполняется в обработчике записью, соответствующей переданному в качестве параметра SQL-команды значению поля CustomerID, но эти данные никак не связаны с элементами текстовых полей. Исправим это:
- Выделите на форме элемент txtCustomerID текстового поля, в панели Properties раскройте его свойство (DataBindings).Text и присоедините к нему поле CustomerID из второго набора данных, как показано на снимке
- Проделайте то же самое для остальных элементов TextBox формы, связывая их с соответствующими полями второго набора данных
- Запустите приложение - теперь все в порядке, для выбранной в списке строки в элементах TextBox отображаются значения всех полей текущей записи таблицы Customers
Здесь есть одна несогласованность, которую желательно сразу же устранить. Дело в том, что текстовые поля мы используем только для просмотра данных, а они еще и допускают видимость редактирования, которая на данном этапе никак не отразится на самих данных в БД. Поэтому, чтобы не вводить пользователя в заблуждение, редактирование в них нужно запретить. Это можно сделать установкой их свойства ReadOnly в значение true, но тогда они станут "тускло некрасивыми". Но есть другой способ их 'стерилизовать':
- Поместите на форму над текстовыми полями компонент CheckBox и настройте его так
- Выделите на форме все расположенные справа элементы TextBox и в панели Properties в режиме Events создайте для них общий обработчик события KeyPress, который заполните так
private void txtCustomerID_KeyPress(object sender, KeyPressEventArgs e) { if(!checkEdit.Checked) e.Handled = true; }
Если сейчас запустить приложение, то можно убедиться (при сброшенном флажке), что хоть теперь текстовые поля не реагируют на ввод с клавиатуры символов, но осталась функциональность управляющих редактированием клавиш Delete, Home, End и стрелок. Чтобы окончательно 'добить' текстовые поля, выполните следующее:
- Обведите блоком курсора все текстовые поля, чтобы сразу опять выделить их, и создайте для них общий обработчик события KeyDown, который заполните так
private void txtFax_KeyDown(object sender, KeyEventArgs e) { if(!checkEdit.Checked) e.Handled = true; }
Несмотря на то, что в двух последних обработчиках использован одинаковый код, не пытайтесь их объединить, поскольку они возбуждаются разными событиями и передаваемый в них объект e имеет разный тип.
- Запустите приложение - теперь (при сброшенном флажке) поля не реагируют и на управляющие редактированием клавиши, но в то же время остаются светлыми и даже лучезарными
Редактирование и обновление полей данных с использованием связанных элементов управления
Форма, развитая в предыдущем разделе, пригодна только для просмотра данных БД. Даже если пользователь будет пытаться редактировать текстовые поля, это никак не отразится на хранящихся в БД данных. Однако более важной является возможность редактирование данных в текстовых полях и обновление их в БД. В этом разделе мы введем такую функциональность в приложение.
- Поместите на форму (перед этим выделите форму) над текстовыми полями две кнопки Button, которые настройте в соответствии с таблицей свойств
Кнопка Save будет сохранять содержимое полей редактирования в БД, а кнопка Cancel - восстанавливать поля повторным чтением соответствующей записи из БД. Вначале запрограммируем кнопку Cancel. Обработчик события кнопки Cancel должен выполнить ту же самую работу по заполнению текстовых полей из базы, что у нас уже делает обработчик SelectedIndexChanged списка ListBox, поэтому
- Выделите на форме кнопку Cancel, зайдите в панель Properties в режиме Events и для события Click через раскрывающийся список его поля присоедините созданный ранее обработчик listBox_SelectedIndexChanged
- Запустите приложение и убедитесь в правильном функционировании кнопки Cancel
Для сбора редактируемой в текстовых полях информации нужно с них ее собрать и обновить кэш набора данных, после чего выполнить метод Update() поставщика данных, чтобы измененния набора данных сохранились в БД. Для сбора данных с редактируемых полей, связанных с данными, мы применим экземпляр библиотечного класса System.Windows.Forms.BindingContext.
Объект BindingContext наследуется как общедоступное виртуальное свойство класса Control, следовательно содержится в любом элементе управления, в том числе и в самой форме. Этот объект позволяет управлять процессом редактирования и обновления данных, а также их передачей в набор данных. Он обеспечивает синхронизацию наборов данных со связанными элементами управления. Воспользуемся объектом BindingContext как встроенным свойством формы для обновления набора данных.
- Двойным щелчком на кнопке Save создайте для нее обработчик события Click, который заполните так
private void btnSave_Click(object sender, EventArgs e) { SaveRecord(); } private void SaveRecord() { // Применить свойство формы типа BindingContext // и вытолкнуть изменения со всех дочерних элементов формы, // связанных с данными, в кэш набора данных для заданной таблицы this.BindingContext[dataSet21, "Customers"].EndCurrentEdit(); // Обновить данные в БД из кэша набора данных dataSet21 oleDbDataAdapter2.Update(dataSet21, "Customers"); // Принять изменения // (лишнее!!! - вызывается автоматически методом Update) //dataSet21.AcceptChanges(); // Обновить первый набор данных и список listBox // с сохранением позиции выделенного элемента списка // на случай, если вдруг редактировалось поле CompanyName, // отображаемое в списке int index = listBox.SelectedIndex; NewLoad(); int count = listBox.Items.Count - 1; index = index < count ? index : count; // Однострочный условный оператор listBox.SelectedIndex = index; }
- Запустите приложение - будет выброшено исключение, что адаптер данных не поддерживает команду Update()
Да, действительно, наш второй поставщик данных был ранее настроен только на чтение данных из базы (как и первый), поэтому нужно его перенастроить, чтобы добавить функциональность обновлять данные в БД.
- В режиме View Designer формы вызовите контекстное меню для объекта oleDbDataAdapter2 и выполните команду Configure Data Adapter
- Дойдите до вкладки Generate the SQL statements мастера, щелкните на кнопке Advanced Options и установите все флажки вкладки Advanced Options
- Дойдите до последней вкладки мастера настройки поставщика, убедитесь, что включены все режимы Select, Insert, Update, Delete и щелкните на кнопке Finish, чтобы завершить настройку
В обработчике кнопки Save мы использовали обновление данных в базе в два этапа: вначале вытолкнули изменения из связанных элементов управления в набор данных, и лишь потом дали команду на окончательное внесение изменений из набора в саму базу. Такой подход реализован в среде ADO.NET для обеспечения отсоединенного подключения к базе данных.
Механизм работы таков: поставщик данных при выполнении запроса SELECT на выборку данных кратковременно подключается к базе данных, загружает в кэш найденные данные и сразу же отключается от базы. Это дает возможность работать с базой данных максимальному количеству пользователей. Поставщик данных после разъединения имитирует теперь собой базу данных в кэше оперативной памяти и пользователь работает с данными через набор данных не замечая, что фактически работает с копией данных.
Пользователь в отсоединенном режиме может сохранять данные якобы в базе, но при каждом сохранении поставщик данных фактически создает у себя по несколько версий изменений, которые можно всегда отменить (наподобие уровней откатов). При окончательном сохранении изменений в базе данных разработчик через программирование поставщика данных может реализовать необходимую стратегию обновления и разрешения конфликтных ситуаций.
Механизм ADO.NET работы в отсоединенном режиме опирается на принцип оптимистического параллелизма обработки данных. Данные в самой базе не блокируются и могут потребляться сразу несколькими пользователями. Поставщики данных каждого пользователя при попытке сохранить проверяют изменения подведомственных данных в базе и позволят сохранить изменения только тех данных, с которыми не работают в данный момент другие пользователи.
Для многопользовательского режима работы с БД наиболее распространены следующие типы проверки нарушения параллелизма и стратегий обновления данных
- Изменения заносятся в базу данных в том случае, если значение первичного ключа обновленной строки совпадает со значением первичного ключа существующей строки.
- Изменения заносятся в базу данных в том случае, если ни один из столбцов, обновленных данным пользователем, не был изменен никем другим с тех пор, как строка была считана данным пользователем.
- Изменения заносятся в базу данных в том случае, если ни один из столбцов строки не был изменен никем другим с тех пор, как строка была считана данным пользователем.
- Изменения заносятся в базу данных в том случае, если метка времени последнего обновления строки не изменилась с тех пор, как строка была считана данным пользователем. По своему смыслу этот тип проверки аналогичен предыдущему.
Функционирование среды ADO.NET в отсоединенном режиме основано на создании временных данных в файле на языке XML. Чтобы убедиться в этом, достаточно заглянуть в панель Solution Explorer и увидеть файлы с расширением .xsd и с именами классов, созданных оболочкой для наших наборов данных. Если открыть, например, файл DataSet2.xsd через команду контекстного меню Open With/Source Code (Text) Editor, то можно увидеть схему на языке XML, настраивающую работу поставщика.
Для нас из этой скромной теории важно то, что значение ключевого поля существующей записи редактировать нельзя.
Ранее мы запрограммировали кнопку Cancel немного нерационально, посадив ее на обработчик события SelectedIndexChanged списка. Это ведет к лишним затратам ресурсов, заставляя приложение при откатах заново считывать данные во второй набор данных dataSet21. Более правильный способ - заставить набор данных вытолкнуть свое неизменившееся содержимое в элементы редактирования.
- В режиме View Designer формы выделите кнопку Cancel и через панель Properties в режиме Events очистите поле события Click кнопки
- Двойным щелчком на кнопке Cancel создайте для нее обработчик, который заполните следующим кодом
private void btnCancel_Click(object sender, EventArgs e) { // Вытолкнуть первоначальные данные из // набора данных в поля редактирования this.BindingContext[dataSet21, "Customers"].CancelCurrentEdit(); }
- Запустите приложение - теперь восстановление текстовых полей редактирования из набора данных происходит мгновенно