|
При выполнении в лабораторной работе упражнения №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
Добавление жестов
Когда мы создавали источники - элементы меню, то бодро прописали вместе с названиями задач и жесты клавиатурного ввода. Но ни один из них в настоящий момент не работает, поскольку не подкреплен соответствующим механизмом. Далее мы этот же пример повторим с применением команд, а там, мы говорили, эта функциональность уже встроена. Но на данном этапе нам такую возможность придется реализовывать вручную, и мы это сейчас выполним - для тренировки!
-
Добавьте к узлу текущего
проекта пустой файл KeyGestures.cs
-
Заполните файл KeyGestures.cs следующим кодом
using System;
using System.Windows;
using System.Windows.Input;
using System.Collections.Generic;// Для Dictionary<TKey, TValue>
namespace Notepad1
{
partial class Window1
{ // Еще один вариант в Петцольд, WPF, с.316 !!!
// Определяем ассоциативный словарь
Dictionary<KeyGesture, RoutedEventHandler> gests =
new Dictionary<KeyGesture, RoutedEventHandler>();
void CreateGestures()
{
// File
gests.Add(new KeyGesture(Key.N, ModifierKeys.Control), NewOnExecute);//_New
gests.Add(new KeyGesture(Key.O, ModifierKeys.Control), OpenOnExecute);//_Open...
gests.Add(new KeyGesture(Key.S, ModifierKeys.Control), SaveOnExecute);//_Save
gests.Add(new KeyGesture(Key.F2, ModifierKeys.Control), PrintPreviewOnExecute);//P_rint Preview
gests.Add(new KeyGesture(Key.P, ModifierKeys.Control), PrintOnExecute);//_Print...
// Edit
gests.Add(new KeyGesture(Key.Z, ModifierKeys.Control), UndoOnExecute);//_Undo
gests.Add(new KeyGesture(Key.Y, ModifierKeys.Control), RedoOnExecute);//_Redo
gests.Add(new KeyGesture(Key.X, ModifierKeys.Control), CutOnExecute);//Cu_t
gests.Add(new KeyGesture(Key.C, ModifierKeys.Control), CopyOnExecute);//_Copy
gests.Add(new KeyGesture(Key.V, ModifierKeys.Control), PasteOnExecute);//_Paste
gests.Add(new KeyGesture(Key.Delete, ModifierKeys.None), DeleteOnExecute);//De_lete
gests.Add(new KeyGesture(Key.F, ModifierKeys.Control), FindOnExecute);//_Find...
gests.Add(new KeyGesture(Key.F3, ModifierKeys.None), FindNextOnExecute);//Find _Next
gests.Add(new KeyGesture(Key.H, ModifierKeys.Control), ReplaceOnExecute);//_Replace...
gests.Add(new KeyGesture(Key.G, ModifierKeys.Control), GoToOnExecute);//_Go To...
gests.Add(new KeyGesture(Key.A, ModifierKeys.Control), SelectAllOnExecute);//Select _All
// Format
gests.Add(new KeyGesture(Key.W, ModifierKeys.Control), WordWrapOnExecute);//_Word Wrap
}
// Перекрываем стандартный обработчик
protected override void OnPreviewKeyDown(KeyEventArgs e)
{
base.OnPreviewKeyDown(e);
// Ищем жест, останавливаем событие и исполняем обработчик
foreach (KeyGesture gest in gests.Keys)
if (gest.Matches(null, e)) // Сравниваем перехваченный жест с заданным в объекте
{
gests[gest](this, e); // Вызываем обработчик через словарь
e.Handled = true; // Останавливаем событие
break; // Прерываем цикл
}
}
}
}-
Добавьте в конструктор
класса Window1 файла Window1.xaml.cs вызов нашей функции создания жестов
public Window1()
{
InitializeComponent();
// Создание жестов
this.CreateGestures();
}-
Запустите приложение
и убедитесь, что клавиатурные жесты работают
Логика отключения источников задач
Одной из важных задач управления пользовательским интерфейсом является своевременное отключение источников команд, когда выполнение команды может противоречить логике работы приложения. Например, если в редакторе текста буфер обмена пуст, команду вставки следует сделать недоступной, или, если в загруженном файле не было изменений, то кнопку сохранения тоже нужно отключить.
Для нашего приложения проведем ревизию правил, по которым элементы интерфейса должны менять свое состояние в зависимости от возможности выполнения тех или иных задач на текущий момент.
Раздел File:
- Задача New: должна быть доступна всегда, поскольку пользователь может захотеть в любой момент создать новый документ. Если в текущем документе есть несохраненные изменения, то нужно вывести диалоговое окно с предложением их сохранить, проигнорировать или отменить задачу. При утвердительном ответе пользователя следует проверить, если новый документ сохраняется впервые, то нужно предоставить диалоговое окно записи. Реализована!
- Задача Open: аналогична задаче New, только после решения о сохранении текущих изменений следует предоставить диалог открытия файла. Реализована!
- Задача Save: при новом документе предоставить диалог записи. Если документ уже сохранялся и имеет имя, но текущих изменений нет, то задачу следует сделать недоступной, отключив соответствующие источники. При первом же изменении нужно немедленно освободить источники выполнения этой задачи. Нереализована!
- Задача Save As: должна быть доступна всегда. При запросе выполнения этой задачи сразу предоставить пользователю диалог сохранения файла. Реализована!
- Задачи Page Setup, Print Preview, Print: должны быть доступны всегда
- Задача Exit: должна быть доступна всегда. Если есть несохраненные изменения, частично выполнить задачу New и завершить приложение (возможно, с сохранением текущего состояния приложения в ресурсном файле). Реализована частично!
Раздел Edit:
- Задачи Undo, Redo: делать недоступными, когда восстанавливать нечего. Нереализована!
- Задачи Cut, Copy: делать недоступными, если нет выделения текста. Нереализована!
- Задача Paste: делать недоступной, если буфер обмена пуст или в нем сохранена нетекстовая информация. Нереализована!
- Задача Delete: делать недоступной, если нет выделения текста. Нереализована!
- Задачи Find, Find Next, Replace, Go To: доступны всегда
- Задача Select All: недоступна в случае, если текстовое поле пустое. Нереализована!
Раздел Format:
- Задачи Font, Word Wrap: доступны всегда
Раздел Help:
- Задача About: доступна всегда
Реализация логики отключения источников задачи Save
Чтобы продемонстрировать трудность реализации логики отключения источников, ограничимся только одной задачей Save. В последующем упражнении, где будет использован механизм команд, все решится гораздо проще. А пока только одна задача - Save, чтобы зря не тратить силы.
Не хочется вмешиваться в ранее разработанный код, поскольку логику отключения мы оставили на потом и сейчас это может повлечь ошибки. Поэтому, наиболее разумно, добавить автономный код, не меняя прежнего, и разместить его в отдельном файле.
-
Выделите узел текущего
проекта и командой Project/Add Class добавьте новый файл с именем EnabledControls.cs,
который заполните так
using System;
using System.Collections.Generic;
using System.Text;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Data;
using System.Windows.Documents;
using System.Windows.Input;
using System.Windows.Media;
using System.Windows.Media.Imaging;
using System.Windows.Navigation;
using System.Windows.Shapes;
using System.Windows.Controls.Primitives;// Для ButtonBase
namespace Notepad1
{
partial class Window1
{
}
}Первое, что приходит на ум - использовать событие texBox1.TextChanged, в котором проверять состояние флага IsModified и принимать решение о недоступности или доступности источников задачи Save.
-
Добавьте в файл EnabledControls.cs следующий
код, регистрирующий еще один обработчик события texBox1.TextChanged
namespace Notepad1
{
partial class Window1
{
// Вызов размещен в конструкторе класса
void AdditionalHandlers()
{
// Еще один обработчик
// обычного события TextChanged
txtBox1.TextChanged += EnabledControls_TextChanged;
}
private void EnabledControls_TextChanged(object sender,
System.Windows.Controls.TextChangedEventArgs e)
{
// Изменяем состояние интерфейсных элементов _Save
itemSave.IsEnabled = btnSave.IsEnabled = IsModified;
}
}
}-
В файле Window1.xaml.cs добавьте в конструктор класса Window1 последней строкой вызов функции AdditionalHandlers() так
public Window1()
{
InitializeComponent();
// Создание жестов
this.CreateGestures();
// Дополнительные обработчики в файле EnabledControls.cs
AdditionalHandlers();
}-
Запустите приложение
- до первого изменения текста кнопки источники Save блокированы, а потом
все работает не так. И жест Ctrl+S тоже доступен.
Дело здесь в том, что событие TextChanged срабатывает раньше, чем будет установлен флаг IsModified. Поэтому нужно обрабатывать не событие изменения текста, а событие изменения флага IsModified. Следующим шагом мы преобразуем поле IsModified в свойство на базе нового логического поля modified и создадим свое событие, в обработчике которого и решим управление доступностью источников задачи Save.
-
В файле Window1.xaml.cs найдите объявление поля IsModified и переименуйте его в modified
Было bool IsModified = false; // Флаг изменений содержимого Стало bool modified = false; // Флаг изменений содержимого
Поле modified будет базовым для свойства IsModified. Это все изменения, которые мы вынуждены были провести в прежнем коде. Остальные изменения будем вносить в файл EnabledControls.cs.
-
В файле EnabledControls.cs удалите весь код, связанный с событием texBox1.TextChanged и его обработчиком
namespace Notepad1
{
partial class Window1
{
// Вызов размещен в конструкторе класса
void AdditionalHandlers()
{
// Еще один обработчик
// обычного события TextChanged
txtBox1.TextChanged += EnabledControls_TextChanged;
}
private void EnabledControls_TextChanged(object sender,
System.Windows.Controls.TextChangedEventArgs e)
{
// Изменяем состояние интерфейсных элементов _Save
itemSave.IsEnabled = btnSave.IsEnabled = IsModified;
}
}
}-
Добавьте в файл EnabledControls.cs новый код, чтобы файл стал таким
using System;
using System.Collections.Generic;
using System.Text;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Data;
using System.Windows.Documents;
using System.Windows.Input;
using System.Windows.Media;
using System.Windows.Media.Imaging;
using System.Windows.Navigation;
using System.Windows.Shapes;
using System.Windows.Controls.Primitives;// Для ButtonBase
namespace Notepad1
{
partial class Window1
{
// Объявляем внутреннее событие
private event EventHandler ChangeModifiedEvent;
// Упаковываем базовое поле modified в свойство
private bool IsModified
{
get { return modified; }
set
{
if (modified != value)
{
modified = !modified;
// Инициируем событие, если есть обработчик
if (ChangeModifiedEvent != null)
ChangeModifiedEvent(this, EventArgs.Empty);
}
}
}
// Вызов размещен в конструкторе класса
void AdditionalHandlers()
{
// Начальные запрещения для _Save
itemSave.IsEnabled = btnSave.IsEnabled = false;
// Удаляем созданный в CreateGestures() жест _Save
foreach (KeyGesture gest in gests.Keys)
if (gests[gest] == SaveOnExecute)
{
gests.Remove(gest);
break;
}
// Регистрируем обработчик изменения свойства
this.ChangeModifiedEvent += Window1_ChangeModifiedEvent;
}
void Window1_ChangeModifiedEvent(object sender, EventArgs e)
{
//MessageBox.Show("Modify");
// Проверяем состояние любого из источников _Save
if (btnSave.IsEnabled == false)
// Добавляем жест _Save
gests.Add(new KeyGesture(Key.S, ModifierKeys.Control),
SaveOnExecute);//_Save
else
// Удаляем жест _Save
foreach (KeyGesture gest in gests.Keys)
if (gests[gest] == SaveOnExecute)
{
gests.Remove(gest);
break;
}
// Изменяем состояние интерфейсных элементов _Save
itemSave.IsEnabled = btnSave.IsEnabled = IsModified;
}
}
}-
Запустите приложение
и убедитесь, что управление источниками задачи Save, включая жесты, во всех
режимах работает как и положено. Разберитесь с кодом!!!
Для управления доступностью других задач приложения нужно построить что-то подобное. Мы этого здесь делать не будем, однако и сейчас уже ясно, что это непростая задача. Для желающих продолжить управление отключениями источников можно посоветовать дополнить файл EnabledControls.cs новыми заготовками так
using System;
using System.Collections.Generic;
using System.Text;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Data;
using System.Windows.Documents;
using System.Windows.Input;
using System.Windows.Media;
using System.Windows.Media.Imaging;
using System.Windows.Navigation;
using System.Windows.Shapes;
using System.Windows.Controls.Primitives;// Для ButtonBase
namespace Notepad1
{
partial class Window1
{
// Объявляем внутреннее событие
private event EventHandler ChangeModifiedEvent;
// Упаковываем базовое поле modified в свойство
private bool IsModified
{
get { return modified; }
set
{
if (modified != value)
{
modified = !modified;
// Инициируем событие, если есть обработчик
if (ChangeModifiedEvent != null)
ChangeModifiedEvent(this, EventArgs.Empty);
}
}
}
// Вызов размещен в конструкторе класса
void AdditionalHandlers()
{
// Регистрируем один и тот же обработчик
// всплывающих событий кнопок, элементов меню,
// клавиатурных жестов для окна
this.AddHandler(ButtonBase.ClickEvent,
new RoutedEventHandler(this.Window1_ButtonClick));
this.AddHandler(MenuItem.ClickEvent,
new RoutedEventHandler(this.Window1_ItemClick));
this.AddHandler(Keyboard.KeyDownEvent,
new RoutedEventHandler(this.Window1_Gesture));
// Дополнительный общий обработчик элементов контекстного меню
contextCut.Click += new RoutedEventHandler(item_Context);
contextCopy.Click += new RoutedEventHandler(item_Context);
contextPaste.Click += item_Context; // Упрощенный синтаксис
contextDelete.Click += item_Context;
// Начальные запрещения для _Save
itemSave.IsEnabled = btnSave.IsEnabled = false;
// Удаляем созданный в CreateGestures() жест _Save
foreach (KeyGesture gest in gests.Keys)
if (gests[gest] == SaveOnExecute)
{
gests.Remove(gest);
break;
}
// Регистрируем обработчик изменения свойства
this.ChangeModifiedEvent += Window1_ChangeModifiedEvent;
}
void Window1_ChangeModifiedEvent(object sender, EventArgs e)
{
//MessageBox.Show("Modify");
// Проверяем состояние любого из источников _Save
if (btnSave.IsEnabled == false)
// Добавляем жест _Save
gests.Add(new KeyGesture(Key.S, ModifierKeys.Control),
SaveOnExecute);//_Save
else
// Удаляем жест _Save
foreach (KeyGesture gest in gests.Keys)
if (gests[gest] == SaveOnExecute)
{
gests.Remove(gest);
break;
}
// Изменяем состояние интерфейсных элементов _Save
itemSave.IsEnabled = btnSave.IsEnabled = IsModified;
}
private void Window1_ButtonClick(object sender, RoutedEventArgs e)
{
//MessageBox.Show("Button");
// Повышаем полномочия ссылки
Button btn = sender as Button;
if (btn == btnSave)
{
;
}
}
private void Window1_ItemClick(object sender, RoutedEventArgs e)
{
//MessageBox.Show("Item");
// Повышаем полномочия ссылки
MenuItem item = sender as MenuItem;
if (item == itemSave)
{
;
}
}
private void Window1_Gesture(object sender, RoutedEventArgs e)
{
//MessageBox.Show("Key");
}
private void item_Context(object sender, RoutedEventArgs e)
{
//MessageBox.Show("Context");
}
}
}Добавленный код пока ни на что не влияет, но может стать отправной точкой для дальнейших действий по блокированию других задач. В следующим упражнении мы все решим гораздо проще, используя встроенный в WPF механизм команд.
Упражнение 7. Разработка простого блокнота с использованием механизма команд
Мы уже столько потрудились над этим блокнотом, который так наивно назвали простым, что неразумно будет начинать все заново. Проще скопировать полученный проект в новый и там проводить все необходимые изменения. Но трудились мы не для сего блокнота, а ради будущих наших профессиональных успехов (Se La Vi - такова жизнь).
