Упражнение 2. Прослушивание событий мыши
- Добавьте к решению командой File/Add/New Project новый проект с именем ListenerEvents и назначьте его стартовым
увеличить изображение
- Выполните команду Project/ListenerEvents Properties... и настройте выпадающий список Output type на значение Console Application, чтобы параллельно запускались графическое и консольное окна приложения
увеличить изображение
Мы будем воздействовать мышью на графическое окно, а вывод обработчиков событий наблюдать в консольном окне. Порядок выполнения обработчиков позволит проследить маршрут движение событий по дереву элементов.
- Заполните файл разметки Window1.xaml следующим кодом
<Window x:Class="ListenerEvents.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="Red" ToolTip="Элемент Window - Red" PreviewMouseDown="Window_PreviewMouseDown" MouseDown="Window_MouseDown" ButtonBase.Click="Window_Click" > <Window.ContextMenu> <ContextMenu> <MenuItem Header="Item1" /> </ContextMenu> </Window.ContextMenu> <DockPanel> <Menu DockPanel.Dock="Top" ToolTip="Элемент Menu - #FFD4D0C8" Background="#FFD4D0C8" > <MenuItem Header="File"> <MenuItem Header="_Open" /> <MenuItem Header="_Save" /> <MenuItem Header="Save_As" /> <MenuItem Header="E_xit" /> </MenuItem> <MenuItem Header="_Edit"> <MenuItem Header="Cu_t" /> <MenuItem Header="_Copy" /> <MenuItem Header="_Paste" /> </MenuItem> </Menu> <Grid Width="220" Height="200" Background="Green" ToolTip="Элемент Grid - Green" PreviewMouseDown="Grid_PreviewMouseDown" MouseDown="Grid_MouseDown" ButtonBase.Click="Grid_Click" > <UniformGrid Rows="3" Height="140" Width="130" Background="Blue" ToolTip="Элемент UniformGrid - Blue" PreviewMouseDown="UniformGrid_PreviewMouseDown" MouseDown="UniformGrid_MouseDown" ButtonBase.Click="UniformGrid_Click" > <TextBlock Background="Yellow" VerticalAlignment="Center" TextAlignment="Center" ToolTip="Элемент TextBlock - Yellow" PreviewMouseDown="TextBlock_PreviewMouseDown" MouseDown="TextBlock_MouseDown" ButtonBase.Click="TextBlock_Click" > Туннельное <LineBreak /> Пузырьковое </TextBlock> <TextBlock Background="Aqua" VerticalAlignment="Center" TextAlignment="Center" ToolTip="Элемент TextBlock - Aqua" MouseEnter="TextBlock_MouseEnter" MouseLeave="TextBlock_MouseLeave" PreviewMouseDown="TextBlock_PreviewMouseDown" MouseDown="TextBlock_MouseDown" ButtonBase.Click="TextBlock_Click" > Прямое MouseEnter <LineBreak /> Прямое MouseLeave </TextBlock> <Button Background="Orange" VerticalAlignment="Center" ToolTip="Элемент Button - Orange" PreviewMouseDown="Button_PreviewMouseDown" MouseDown="Button_MouseDown" Click="Button_Click" > Генератор Click </Button> </UniformGrid> </Grid> </DockPanel></Window> Обратите внимание, что все элементы, пока, мы сделали неименованными, но среда выполнения в точности определит, какой элемент возбудил или обработал событие. В названиях пунктов меню мы применили знаки подчеркивания для использования горячих клавиш, которые проявятся после нажатия клавиши Alt в работающем приложении. В Windows Forms для этой цели используется символ амперсанда &, но в XAML он конфликтовал бы с подобным управляющим символом &, поэтому был заменен на подчеркивание.
- Заполните файл поддержки разметки Window1.xaml.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; namespace ListenerEvents{ public partial class Window1 : Window { public Window1() { InitializeComponent(); } int count; private void Window_PreviewMouseDown(object sender, MouseButtonEventArgs e) { count = 0; Console.Clear(); if (e.ChangedButton == MouseButton.Left) Console.WriteLine("{0}) Window: Наблюдаю туннельное событие PreviewMouseDown", ++count); else e.Handled = true; } private void Window_MouseDown(object sender, MouseButtonEventArgs e) { Console.WriteLine("{0}) Window: Наблюдаю пузырьковое событие MouseDown", ++count); } private void Window_Click(object sender, RoutedEventArgs e) { Console.WriteLine("{0}) Window: Наблюдаю пузырьковое событие Click (как вложенное)", ++count); } private void Grid_PreviewMouseDown(object sender, MouseButtonEventArgs e) { Console.WriteLine("{0}) Grid: Наблюдаю туннельное событие PreviewMouseDown", ++count); } private void Grid_MouseDown(object sender, MouseButtonEventArgs e) { Console.WriteLine("{0}) Grid: Наблюдаю пузырьковое событие MouseDown", ++count); } private void Grid_Click(object sender, RoutedEventArgs e) { Console.WriteLine("{0}) Grid: Наблюдаю пузырьковое событие Click (как вложенное)", ++count); } private void UniformGrid_PreviewMouseDown(object sender, MouseButtonEventArgs e) { Console.WriteLine("{0}) UniformGrid: Наблюдаю туннельное событие PreviewMouseDown", ++count); } private void UniformGrid_MouseDown(object sender, MouseButtonEventArgs e) { Console.WriteLine("{0}) UniformGrid: Наблюдаю пузырьковое событие MouseDown", ++count); } private void UniformGrid_Click(object sender, RoutedEventArgs e) { Console.WriteLine("{0}) UniformGrid: Наблюдаю пузырьковое событие Click (как вложенное)", ++count); } private void TextBlock_PreviewMouseDown(object sender, MouseButtonEventArgs e) { Console.WriteLine("{0}) TextBlock: Наблюдаю туннельное событие PreviewMouseDown", ++count); } private void TextBlock_MouseDown(object sender, MouseButtonEventArgs e) { Console.WriteLine("{0}) TextBlock: Наблюдаю пузырьковое событие MouseDown", ++count); } private void TextBlock_Click(object sender, RoutedEventArgs e) { Console.WriteLine("{0}) TextBlock: Наблюдаю пузырьковое событие Click (как вложенное)", ++count); } private void TextBlock_MouseEnter(object sender, MouseEventArgs e) { Console.WriteLine("{0}) TextBlock: Возбуждаю прямое событие MouseEnter", ++count); } private void TextBlock_MouseLeave(object sender, MouseEventArgs e) { Console.WriteLine("{0}) TextBlock: Возбуждаю прямое событие MouseLeave", ++count); } private void Button_PreviewMouseDown(object sender, MouseButtonEventArgs e) { Console.WriteLine("{0}) Button: Наблюдаю туннельное событие PreviewMouseDown", ++count); } private void Button_MouseDown(object sender, MouseButtonEventArgs e) { Console.WriteLine("{0}) Button: Наблюдаю пузырьковое событие MouseDown", ++count); } private void Button_Click(object sender, RoutedEventArgs e) { Console.WriteLine("{0}) Button: Возбуждаю пузырьковое событие Click", ++count); } }} Обратите внимание, что в первом обработчике мы перехватываем туннельное событие щелчка мыши сразу же в корневом элементе и подавлем его для правой кнопки. В то же время это не влияет на работу контекстного меню, поскольку в этом случае среда выполнения создает свое всплывающее окно и передает ему фокус ввода.
- Запустите приложение - получим следующее графическое окно
Для элементов логического дерева, входящих в разметку, мы определили разный цвет фона, чтобы можно было их визуально различать. Цвет фона меню Background="#FFD4D0C8" мы назначили в стиле HTML для напоминания, хотя по умолчанию оно и так имеет такой фоновый системный цвет, типичный для большинства пользовательских элементов управления (первый байт FF определяет коэффициент непрозрачности Opacity, FF - полная непрозрачность, 0 - полная прозрачность).
Теперь осталось последовательно щелкать на отдельные элементы графического окна и в консольном окне наблюдать за маршрутизацией событий по последовательности срабатывания обработчиков. Для удобства можно пользоваться всплывающей подсказкой. Ниже приведены результаты, для группировки которых использован цвет фона элемента:
Маршрутизация событий при щелчках на элементах
|
Цвет (элемент)
| Вывод обработчиков
|
Red
| - Window: Наблюдаю туннельное событие PreviewMouseDown
- Window: Наблюдаю пузырьковое событие MouseDown
|
Green
| - Window: Наблюдаю туннельное событие PreviewMouseDown
- Grid: Наблюдаю туннельное событие PreviewMouseDown
- Grid: Наблюдаю пузырьковое событие MouseDown
- Window: Наблюдаю пузырьковое событие MouseDown
|
Blue
| - Window: Наблюдаю туннельное событие PreviewMouseDown
- Grid: Наблюдаю туннельное событие PreviewMouseDown
- UniformGrid: Наблюдаю туннельное событие PreviewMouseDown
- UniformGrid: Наблюдаю пузырьковое событие MouseDown
- Grid: Наблюдаю пузырьковое событие MouseDown
- Window: Наблюдаю пузырьковое событие MouseDown
|
Yellow
| - Window: Наблюдаю туннельное событие PreviewMouseDown
- Grid: Наблюдаю туннельное событие PreviewMouseDown
- UniformGrid: Наблюдаю туннельное событие PreviewMouseDown
- TextBlock: Наблюдаю туннельное событие PreviewMouseDown
- TextBlock: Наблюдаю пузырьковое событие MouseDown
- UniformGrid: Наблюдаю пузырьковое событие MouseDown
- Grid: Наблюдаю пузырьковое событие MouseDown
- Window: Наблюдаю пузырьковое событие MouseDown
|
Aqua
| - TextBlock: Возбуждаю прямое событие MouseEnter
- TextBlock: Возбуждаю прямое событие MouseLeave
|
Orange
| - Window: Наблюдаю туннельное событие PreviewMouseDown
- Grid: Наблюдаю туннельное событие PreviewMouseDown
- UniformGrid: Наблюдаю туннельное событие PreviewMouseDown
- Button: Наблюдаю туннельное событие PreviewMouseDown
- Button: Возбуждаю пузырьковое событие Click
- UniformGrid: Наблюдаю пузырьковое событие Click (как вложенное)
- Grid: Наблюдаю пузырьковое событие Click (как вложенное)
- Window: Наблюдаю пузырьковое событие Click (как вложенное)
|
Обратите внимание, что хотя мы и вложили в элементы TextBlock событие Click, но оно не проходит через них при щелчке на кнопке, поскольку сразу всплывает к родительскому элементу UniformGrid. И любой из элементов с вложенным событием Click, как и вообще с любым вложенным событием, способен его только слушать и обрабатывать, но никоим образом не может его возбуждать.
Может возникнуть вопрос, а как на самом деле в коде происходит регистрация обработчиков событий, если мы их прикрепили в разметке, ...и с помощью каких делегатов? Все это делает за нас оболочка благодаря тому, что класс поддержки разметки объявлен как partial (частичный). Чтобы увидеть это, надо зайти в конструктор кодовой части файла Window1.axml.cs для класса Window1, щелкнуть правой кнопкой мыши на вызове метода InitializeComponent() и выбрать команду Go To Definition (Перейти к определению) из контекстного меню. В редакторе отобразится созданный файл кода Window1.g.i.cs, где и будут полные определения прикрепленных к элементам обработчиков событий.
В нашем примере мы применили разные типы элементов для перехвата события. Когда применяются одинаковые типы элементов, то для правильной адресации следует присоединять к одному и тому же событию обработчики с уникальными именами. Но можно ко всем элементам присоединить и общий обработчик, тогда нужно присваивать уникальные имена уже элементам и анализировать в обработчике источник, возбудивший событие с присоединенным обработчиком. Сказанное можно проиллюстрировать примером из MSDN для трех именованных кнопок:
private void CommonClickHandler(object sender, RoutedEventArgs e){ FrameworkElement feSource = e.Source as FrameworkElement; switch (feSource.Name) { case "YesButton": // do something here ... break; case "NoButton": // do something ... break; case "CancelButton": // do something ... break; } e.Handled=true;} Это распространенный прием для распознавания и обыкновенных событий C#.
Дата добавления: 2015-04-15; просмотров: 1108;