При выполнении в лабораторной работе упражнения №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
Прямой вызов команд
Команды WPF можно вызывать и напрямую, а необязательно присоединять к источникам. Правда, это будет уже извращение и сам механизм команд будет использоваться только частично. При таком подходе придется реагировать на состояния источников и на возможность выполнения команды самостоятельным кодом. Но знать о существовании такой возможности программисту следует. Покажем это...
- Добавьте к интерфейсу окна еще четыре кнопки так
<Window x:Class="BindingCommandsXAML.Window1" xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" Title="Window1: Декларативная привязка" Height="300" Width="300" Background="#FFD4D0C8" WindowStartupLocation="CenterScreen" > <Window.CommandBindings> <CommandBinding Command="ApplicationCommands.Open" Executed="OpenCommand_Executed"> </CommandBinding> <CommandBinding Command="ApplicationCommands.Save" Executed="SaveCommand_Executed"> </CommandBinding> </Window.CommandBindings> <StackPanel Margin="5"> <Menu> <MenuItem Header="_File"> <MenuItem Command="ApplicationCommands.Open" /> <MenuItem Command="ApplicationCommands.Save" /> </MenuItem> </Menu> <Button Margin="5" Padding="5" Focusable="False" Command="ApplicationCommands.Open" Content="{x:Static ApplicationCommands.Open}" /> <Button Margin="5" Padding="5" Focusable="False" Command="ApplicationCommands.Save" Content="{Binding RelativeSource={RelativeSource Self}, Path=Command.Text}" /> <Button Margin="5" Padding="5" Focusable="False" Content="DirectCommandsOpen" Click="directCommandsOpen_Click" /> <Button Margin="5" Padding="5" Focusable="False" Content="DirectCommandsSave" Click="directCommandsSave_Click" /> <Button Margin="5" Padding="5" Focusable="False" Content="DirectBindingsOpen" Click="directBindingsOpen_Click" /> <Button Margin="5" Padding="5" Focusable="False" Content="DirectBindingsSave" Click="directBindingsSave_Click" /> </StackPanel> </Window>
- Вызовите контекстное меню для записей события Click и командой Navigate to Event Handler создайте четыре обработчика в файле процедурного кода
- Заполните обработчики следующим кодом прямого вызова команд
private void directCommandsOpen_Click(object sender, RoutedEventArgs e) { ApplicationCommands.Open.Execute(null, this); } private void directCommandsSave_Click(object sender, RoutedEventArgs e) { ApplicationCommands.Save.Execute(null, this); } private void directBindingsOpen_Click(object sender, RoutedEventArgs e) { this.CommandBindings[0].Command.Execute(null); } private void directBindingsSave_Click(object sender, RoutedEventArgs e) { this.CommandBindings[1].Command.Execute(null); }
В прямых вызовах двух первых обработчиков начальный параметр метода Execute() ожидает объект с дополнительной информацией, который будет передан на обработчик команды. Второй параметр принимает целевой элемент привязки команды с возможностями интерфейса IInputElement, который будет прослушивать команду и вызывать обработчик. В нашем случае это объект окна. Два следующих обработчика используют вызовы команд из коллекции прослушивающего элемента (окна), привязанные к нему в разметке.
- Запустите приложение и убедитесь, что добавленные кнопки инициируют прямой вызов команд
Упражнение 5. Привязка команд в процедурном коде
Частично повторим предыдущее упражнение, но привязку команд выполним в процедурном коде.
- Добавьте к решению командой File/Add/New Project новый проект с именем BindingCommandsCode и назначьте его стартовым
- Откорректируйте разметку окна Window1 так
<Window x:Class="BindingCommandsCode.Window1" xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" Title="Window1: Кодовая привязка" Height="300" Width="300" Background="#FFD4D0C8" WindowStartupLocation="CenterScreen" > <StackPanel Margin="5"> <Menu> <MenuItem Header="_File"> <MenuItem Command="ApplicationCommands.Open" /> <MenuItem Command="ApplicationCommands.Save" /> </MenuItem> </Menu> <Button Margin="5" Padding="5" Focusable="False" Command="ApplicationCommands.Open" Content="Open" /> <Button Margin="5" Padding="5" Focusable="False" Command="ApplicationCommands.Save" Content="Save" /> </StackPanel> </Window>
Заметьте, что в этой разметке отсутствует как секция привязки команд, так и ссылки на обработчики команд. Все это мы выполним в процедурной части окна.
- Внесите следующие изменения в класс Window1 файла процедурного кода Window1.xaml.cs
public partial class Window1 : Window { public Window1() { InitializeComponent(); // Регистрация обработчика this.Loaded += new RoutedEventHandler(Window1_Loaded); } void Window1_Loaded(object sender, RoutedEventArgs e) { // Привязка команд в коде CommandBinding binding = new CommandBinding(); binding.Command = ApplicationCommands.Open; binding.Executed += new ExecutedRoutedEventHandler(OpenCommand_Executed); this.CommandBindings.Add(binding); binding = new CommandBinding(); binding.Command = ApplicationCommands.Save; binding.Executed += new ExecutedRoutedEventHandler(SaveCommand_Executed); this.CommandBindings.Add(binding); } void OpenCommand_Executed(object sender, ExecutedRoutedEventArgs e) { MessageBox.Show("Выполнена команда Open"); } void SaveCommand_Executed(object sender, ExecutedRoutedEventArgs e) { MessageBox.Show("Выполнена команда Save"); } }
- Запустите приложение и убедитесь в работоспособности механизма команд WPF
Жесты как источники команд
Комбинации клавиш, предоставляющие прямой доступ к выполнению команд меню, ранее называли акселераторами (ускорителями), теперь - жестами. Жесты можно хранить в самой команде в коллекции InputGestures, а можно хранить в прослушивающем элементе в коллекции InputBindings наряду с командой. Кроме интерфейсных элементов жесты могут служить еще одним источником команд. Команда, имеющая жесты в своей коллекции, может вообще не быть присоединенной ни к одному из визуальных элементов, а возбуждаться только жестами.
Добавление жестов в команду
Жесты представленны абстрактным классом System.Windows.Input. InputGesture. Этот класс наследует двум типам жестов: клавиатурных и мыши, которые представлены объектами KeyGesture и MouseGesture соответственно. Команды, в свою очередь, имеют коллекцию жестов InputGestures, которую заранее можно наполнить объектами нужных жестов клавиатуры и мыши. Комбинации клавиш и кнопок мыши проще всего добавлять в объект жеста через его конструктор при создании экземпляра по схеме ( Key, ModifierKeys ) и ( MouseAction, ModifierKeys ).
В следующем примере показано, как в команду можно добавить жесты, которые будут ее запускать
// Клавиатурный жест Control+F InputGesture gesture = new KeyGesture(Key.F, ModifierKeys.Control, "Ctrl+F"); ApplicationCommands.Find.InputGestures.Add(gesture); // Комбинированный жест Control+LeftClick gesture = new MouseGesture(MouseAction.LeftClick, ModifierKeys.Control); ApplicationCommands.Find.InputGestures.Add(gesture); // Клавиатурный жест Control+Q KeyGesture keyGesture = new KeyGesture(Key.Q, ModifierKeys.Control, "Ctrl+Q"); ApplicationCommands.Find.InputGestures.Add(keyGesture); // Комбинированный жест Alt+LeftClick MouseGesture mouseGesture = new MouseGesture(); mouseGesture.MouseAction = MouseAction.LeftClick; mouseGesture.Modifiers = ModifierKeys.Alt; ApplicationCommands.Find.InputGestures.Add(mouseGesture); //gesture = new MouseGesture(MouseAction.LeftClick, ModifierKeys.Alt); //ApplicationCommands.Find.InputGestures.Add(gesture);
Теперь команда Find будет запускаться щелчком на кнопке, щелчком левой кнопки мыши с нажатой клавишей Ctrl, комбинациями клавиш Ctrl+F или Ctrl+Q.
Продолжим модификацию нашего упражнения, чтобы проиллюстрировать сказанное о жестах.
- Добавьте в обработчик события Loaded класса окна Window1 следующий код, модифицирующий жесты библиотечных команд Open и Save
public partial class Window1 : Window { public Window1() { InitializeComponent(); // Регистрация обработчика this.Loaded += new RoutedEventHandler(Window1_Loaded); } void Window1_Loaded(object sender, RoutedEventArgs e) { // Привязка команд в коде CommandBinding binding = new CommandBinding(); binding.Command = ApplicationCommands.Open; binding.Executed += new ExecutedRoutedEventHandler(OpenCommand_Executed); this.CommandBindings.Add(binding); binding = new CommandBinding(); binding.Command = ApplicationCommands.Save; binding.Executed += new ExecutedRoutedEventHandler(SaveCommand_Executed); this.CommandBindings.Add(binding); // Очистка коллекций прежних жестов команд ApplicationCommands.Open.InputGestures.Clear(); ApplicationCommands.Save.InputGestures.Clear(); // Добавление новых жестов клавиатуры Alt+O и Alt+S InputGesture key = new KeyGesture(Key.O, ModifierKeys.Alt, "Alt+O"); ApplicationCommands.Open.InputGestures.Add(key); // KeyGesture keyGesture = new KeyGesture(Key.S, ModifierKeys.Alt, "Alt+S"); ApplicationCommands.Save.InputGestures.Add(keyGesture); // Добавление новых жестов мыши Ctrl+LeftClick и Ctrl+RightClick InputGesture mouse = new MouseGesture(MouseAction.LeftClick, ModifierKeys.Control); ApplicationCommands.Open.InputGestures.Add(mouse); // MouseGesture mouseGesture = new MouseGesture(); mouseGesture.MouseAction = MouseAction.RightClick; mouseGesture.Modifiers = ModifierKeys.Control; ApplicationCommands.Save.InputGestures.Add(mouseGesture); } void OpenCommand_Executed(object sender, ExecutedRoutedEventArgs e) { MessageBox.Show("Выполнена команда Open"); } void SaveCommand_Executed(object sender, ExecutedRoutedEventArgs e) { MessageBox.Show("Выполнена команда Save"); } }
Вначале мы очистили коллекции команд от прежних жестов, затем добавили свои жесты, в том числе с участием мыши. Коллекции можно было и не очищать, тогда работали бы прежние жесты наряду с вновь добавленными.
- Запустите приложение и убедитесь, что теперь прежние клавиатурные комбинации Ctrl+O и Ctrl+S не работают, вместо них программа реагирует на жесты клавиатуры Alt+O и Alt+S, а также выполняются комбинированные жесты с участием мыши Ctrl+LeftClick и Ctrl+RightClick
В нашей программе после добавления жестов появился существенный недостаток - сами жесты мы поменяли, но в меню все-таки остались старые маркеры команд Ctrl+O и Ctrl+S. Хоть мы и пытались в конструкторе KeyGesture() определить в последнем параметре displayString новые маркеры для жестов в меню, но все осталось по прежнему. Дело здесь в том, что объект меню создается и инициализируется в конструкторе класса по объекту уже присоединенной к источнику команды. И это происходит раньше, чем мы в коде начинаем модифицировать саму команду.
Чтобы это исправить, можно воспользоваться несколькими способами:
Способ 1
- Определитете в разметке в элементах меню свойство InputGestureText следующим образом
<Menu> <MenuItem Header="_File"> <MenuItem Command="ApplicationCommands.Open" InputGestureText="Alt+O" /> <MenuItem Command="ApplicationCommands.Save" InputGestureText="Alt+S" /> </MenuItem> </Menu>
- Запустите приложение и убедитесь, что теперь маркеры команд меню изменились
Способ 2
Вместо разметки изменить свойство InputGestureText можно в процедурном коде, но для этого элементам меню нужно присвоить имена.
- Удалите в разметке из дескрипторов элементов меню параметры Command и InputGestureText, а добавьте вместо них имена элементов, как показано ниже
<Menu> <MenuItem Header="_File"> <MenuItem Name="openMenu" /> <MenuItem Name="saveMenu" /> </MenuItem> </Menu>
- Добавьте в самый конец обработчика события Loaded после кода добавления жестов следующую пару строк динамического присоединения модифицированных команд к меню-источнику
void Window1_Loaded(object sender, RoutedEventArgs e) { // Привязка команд в коде CommandBinding binding = new CommandBinding(); binding.Command = ApplicationCommands.Open; binding.Executed += new ExecutedRoutedEventHandler(OpenCommand_Executed); this.CommandBindings.Add(binding); binding = new CommandBinding(); binding.Command = ApplicationCommands.Save; binding.Executed += new ExecutedRoutedEventHandler(SaveCommand_Executed); this.CommandBindings.Add(binding); // Очистка коллекций прежних жестов команд ApplicationCommands.Open.InputGestures.Clear(); ApplicationCommands.Save.InputGestures.Clear(); // Добавление новых жестов клавиатуры Alt+O и Alt+S InputGesture key = new KeyGesture(Key.O, ModifierKeys.Alt, "Alt+O"); ApplicationCommands.Open.InputGestures.Add(key); // KeyGesture keyGesture = new KeyGesture(Key.S, ModifierKeys.Alt, "Alt+S"); ApplicationCommands.Save.InputGestures.Add(keyGesture); // Добавление новых жестов мыши Ctrl+LeftClick и Ctrl+RightClick InputGesture mouse = new MouseGesture(MouseAction.LeftClick, ModifierKeys.Control); ApplicationCommands.Open.InputGestures.Add(mouse); // MouseGesture mouseGesture = new MouseGesture(); mouseGesture.MouseAction = MouseAction.RightClick; mouseGesture.Modifiers = ModifierKeys.Control; ApplicationCommands.Save.InputGestures.Add(mouseGesture); // Присоединяем модифицированные команды к меню-источнику openMenu.Command = ApplicationCommands.Open; saveMenu.Command = ApplicationCommands.Save; }
- Запустите приложение и убедитесь, что теперь маркеры команд меню изменились в соответствии с введенными нами при формировании новых жестов
Способ 3
- Разметку меню сделайте такой
<Menu> <MenuItem Header="_File"> <MenuItem Name="openMenu" Command="ApplicationCommands.Open" /> <MenuItem Name="saveMenu" Command="ApplicationCommands.Save" /> </MenuItem> </Menu>
- Замените в обработчике Window1_Loaded () только что добавленный код на новый
// Присоединяем модифицированные команды к меню-источнику //openMenu.Command = ApplicationCommands.Open; //saveMenu.Command = ApplicationCommands.Save; openMenu.InputGestureText = "Alt+O"; saveMenu.InputGestureText = "Alt+S";
- Запустите приложение и убедитесь, что маркеры команд меню стали правильными
Добавление жестов в прослушивающий элемент
Есть еще один способ добавления жестов, о котором стоит упомянуть. Ранее обсуждалось, что все пользовательские элементы WPF наследуют от UIElement или ContentElement, а эти классы, в свою очередь, имеют в качестве свойства коллекцию InputBindings типа InputBindingCollection. Коллекция может быть заполнена классами KeyBinding или MouseBinding, каждый из которых связывает жест клавиатуры (представленный объектом KeyGesture ) или мыши (представленный объектом MouseGesture ) с командой. Оба класса наследуют тип InputBinding.
Например, привязать жесты с командой к объекту окна можно одним из следующих способов
<Window x:Class="Tmp.window" xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" Height="300" Width="300"> <Window.InputBindings> <KeyBinding Key="F" Modifiers="Control" Command="ApplicationCommands.Find" /> <MouseBinding MouseAction="Control+LeftClick" Command="ApplicationCommands.Find" /> <KeyBinding Key="Q" Modifiers="Control" Command="ApplicationCommands.Find" /> <MouseBinding Gesture="Alt+LeftClick" Command="ApplicationCommands.Find" /> </Window.InputBindings> <Grid> <Button Command="ApplicationCommands.Find" Height="23" Width="75" > Поиск </Button> </Grid> </Window>
В классе MouseBinding свойство Modifiers доступно только для чтения, поэтому жест для мыши нужно вводить в свойство MouseAction сразу целиком, как показано в только что приведенном примере.
// Клавиатурный жест Control+F KeyGesture keyGesture = new KeyGesture(Key.F, ModifierKeys.Control); KeyBinding keyBinding = new KeyBinding(ApplicationCommands.Find, keyGesture); this.InputBindings.Add(keyBinding); // Комбинированный жест Control+LeftClick MouseGesture mouseGesture = new MouseGesture(MouseAction.LeftClick, ModifierKeys.Control); MouseBinding mouseBinding = new MouseBinding(ApplicationCommands.Find, mouseGesture); this.InputBindings.Add(mouseBinding); // Клавиатурный жест Control+Q InputGesture gesture = new KeyGesture(Key.Q, ModifierKeys.Control); ICommand command = ApplicationCommands.Find; keyBinding = new KeyBinding(command, (KeyGesture)gesture); this.InputBindings.Add(keyBinding); // Комбинированный жест Alt+LeftClick gesture = new MouseGesture(); ((MouseGesture)gesture).MouseAction = MouseAction.LeftClick; ((MouseGesture)gesture).Modifiers = ModifierKeys.Alt; mouseBinding = new MouseBinding(); mouseBinding.Command = command; mouseBinding.Gesture = gesture; this.InputBindings.Add(mouseBinding);
Упражнение 6. Разработка простого блокнота без механизма команд
В данном упражнении мы разработаем приложение, частично имитирующее текстовый блокнот, но механизм команд WPF применять не будем. В последующем упражнении на базе разработанного кода мы создадим альтернативный вариант текстового блокнота, но уже с использованием команд. Ух-х-х, поехали!
- Командой Add/New Project добавьте к решению новый проект WPF Application с именем Notepad1 и назначьте его стартовым
Основную часть интерфейса приложения, как и положено, мы создадим декларативно в разметке XAML, а управление - в части процедурного кода C#.
- Отредактируйте интерфейсную часть окна следующим образом
<Window x:Class="Notepad1.Window1" xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" Title="Window1: Управление состоянием источников команд" Width="500" Height="375" MinWidth="500" MinHeight="375" WindowStartupLocation="CenterScreen" ResizeMode="CanResizeWithGrip" > <DockPanel> </DockPanel> </Window>
Создание главного меню
- Добавьте в дескриптор <DockPanel> </DockPanel> разметку создания меню
<DockPanel LastChildFill="True"> <!-- Меню --> <Menu DockPanel.Dock="Top"> <MenuItem Header="_File"> <MenuItem Header="_New" InputGestureText="Ctrl+N"> </MenuItem> <MenuItem Header="_Open..." InputGestureText="Ctrl+O" /> <MenuItem Header="_Save" InputGestureText="Ctrl+S" /> <MenuItem Header="Save _As..." /> <Separator /> <MenuItem Header="Page Set_up..." /> <MenuItem Header="P_rint Preview" InputGestureText="Ctrl+F2" /> <MenuItem Header="_Print..." InputGestureText="Ctrl+P" /> <Separator /> <MenuItem Header="E_xit" /> </MenuItem> <MenuItem Header="_Edit"> <MenuItem Header="_Undo" InputGestureText="Ctrl+Z" /> <MenuItem Header="_Redo" InputGestureText="Ctrl+Y" /> <Separator></Separator> <MenuItem Header="Cu_t" InputGestureText="Ctrl+X" /> <MenuItem Header="_Copy" InputGestureText="Ctrl+C" /> <MenuItem Header="_Paste" InputGestureText="Ctrl+V" /> <MenuItem Header="De_lete" InputGestureText="Del" /> <Separator></Separator> <MenuItem Header="_Find..." InputGestureText="Ctrl+F" /> <MenuItem Header="Find _Next" InputGestureText="F3" /> <MenuItem Header="_Replace..." InputGestureText="Ctrl+H" /> <MenuItem Header="_Go To..." InputGestureText="Ctrl+G" /> <Separator></Separator> <MenuItem Header="Select _All" InputGestureText="Ctrl+A" /> </MenuItem> <MenuItem Header="F_ormat"> <MenuItem Header="_Font..." /> <Separator /> <MenuItem Header="_Word Wrap" IsCheckable="True" IsChecked="True" InputGestureText="Ctrl+W" /> </MenuItem> <MenuItem Header="_Help"> <MenuItem Header="_About" /> </MenuItem> </Menu> </DockPanel>
Знаки подчеркивания мы использовали для того, чтобы выделить символы, по которым пользователь может раскрывать меню и запускать команды после нажатия клавиш Alt или F10. Отмеченные символы должны быть уникальными как на горизонтальной линейке на уровне разделов, так и в рамках одного раздела меню. Каждому пункту присвоено название команды и клавиатурного жеста. В команде Word Wrap свойство IsCheckable обеспечивает самопереключаемость флажка, а его начальное состояние задается свойством IsChecked.