События и команды в WPF
В WPF используются элементы управления двух видов:
- С одиночным содержимым - производные от ContentControl. Такой элемент может содержать в себе только один ближайший вложенный дочерний элемент.
- С множественным содержимым (списковые) - производные от ItemsControl. Такие элементы являются элементами контейнерного типа и могут содержать любое количество ближайших вложенных дочерних элементов.
В WPF структуру приложения можно рассматривать как дерево элементов. Если приложение формируется в XAML, то дерево создается на основе отношений вложенности элементов в разметке. При создании приложения в коде C# дерево создается с учетом того, как строятся коллекции для контейнеров, производных от ItemsControl, и задается содержимое элементов-потомков ContentControl.
В WPF существуют три взгляда на дерево элементов:
- Логическое дерево
- Визуальное дерево (дерево отображения)
- Гибридное дерево
Различия между логическими и визуальными деревьями не всегда важны до тех пор, пока не потребуется более тонкое управление подсистемами WPF. Эти две концепции позволяют понять, как могут повлиять на конечный результат те или иные изменения, вносимые в разметку или код.
Логическое дерево представляет собой модель структуры зависимостей родитель-потомок, определяемую вложенностью содержимого крупных элементов при формировании разметки XAML. Это то, что мы можем представить себе на этапе проектирования иерархии имеющихся элементов. На этапе компиляции часть элементов детализируется в более мелкие.
Визуальное дерево представляет модель отображаемых элементов интерфейса, порожденных классом Visual. При компиляции приложения логическое дерево расширяется до визуального, поскольку часть укрупненных элементов разбивается на атомарные.
Введение перенаправленных событий в WPF связано с принятой моделью содержимого. Простого события для элемента стало недостаточно, поскольку он может содержать в себе еще несколько визуальных элементов. Тогда для полного контроля над событием контейнерного элемента пришлось бы привязывать один и тот же обработчик еще и к событиям каждого из дочерних элементов. Всплывающее событие решает эту проблему, оно восходит и ищет элемент с подписавшимся на него обработчиком.
Например, если кнопка в качестве содержимого имеет несколько элементов: надпись, рисунок или другую кнопку, то щелчок курсором мыши на дочернем элементе еще не означает щелчок на самой кнопке. Событие щелчка возбуждается дочерним для кнопки элементом и его нужно преобразовать в событие щелчка для самой кнопки. В этом случае срабатывает механизм маршрутизации событий ( RoutedEvent ). Здесь ярко проявляются различия в логических и визуальных деревьях, поскольку события на низком уровне маршрутизируются на основе визуального дерева.
Разработчикам приложения не всегда необходимо знать, реализуется ли обрабатываемое событие как перенаправленное. Перенаправленные события имеют особое поведение, но оно остается невидимым, если событие обрабатывается на элементе, где оно возникает. Поэтому для традиционного использования различия между старым и новым механизмами работы событий несущественны.
Маршрутизированные события поддерживают следующие стратегии маршрутизации ( RoutingStrategy ):
- прямую Direct - обрабатывается на источнике, возбудившем событие
- туннельную Tunnel (тоже самое - тоннельную!) - нисходящая маршрутизация событий
- пузырьковую Bubble - восходящая маршрутизация событий
Прямые события ведут себя как обычные события в .NET Framework. У нас есть три инградиента при рассмотрении этого рода событий: элемент, событие и прикрепленный обработчик (или несколько обработчиков для делегата события). При регистрации обработчика все три инградиента жестко связываются. Событие элемента возбуждается только на нем и может быть обработано только собственными обработчиками.
Туннельные события могут быть возбуждены любым элементом. Но они всегда начинаются на корневом элементе ( Window, Page ) и передвигаются вниз по дереву элементов, пока не будут обработаны и прерваны ( остановлены ) каким-нибудь из них или не достигнут исходного элемента-источника для события. Это позволяет находящимся выше элементам перехватить событие и обработать его прежде, чем оно достигнет возбудившего его элемента. К именам всех туннельных событий добавлена приставка Preview (например, PreviewMouseDown ), поэтому их иногда еще называют событиями предварительного просмотра.
Пузырьковые события будут всплывать вверх (распространяться) по визуальному дереву от исходного элемента, пока не будут обработаны или не достигнут корневого элемента. Эти события распространяются в противоположном к туннельным направлении, что позволяет обработать их любому родительскому элементу, стоящему в логическом дереве выше исходного. Например, обработчик события MouseDown можно прикрепить к охватывающему элементу сетки, а не к самому элементу внутри сетки, или к корневому окну. Пузырьковые события имеют имена, просто указывающие их действие (например, MouseDown ).
Перенаправленные события можно еще называть векторными, в отличие от обычных скалярных. Перенаправленные события могут использоваться для связи элементов дерева, так как данные событий можно менять и они сохраняются для каждого элемента в маршруте. Один элемент может изменить что-либо в данных события и это изменение будет доступно для следующего элемента в маршруте. Поэтому передача вверх перенаправляемых событий обычно используется для получения отчета об изменении входных данных или о состоянии различных элементов управления пользовательского интерфейса.
Прослушиватели перенаправленных событий и источники перенаправленных событий не требуют для совместного использования общего события в их иерархии. Любой UIElement или ContentElement может являться прослушивателем перенаправленных событий. Поэтому перенаправленные события можно рассматривать как "концептуальный интерфейс", посредством которого разнородные элементы в приложении могут обмениваться данными. Эта концепция для перенаправленных событий особенно применима для событий ввода.
Существует разновидность пузырьковых событий, которые называются вложенными. Большинство контейнерных элементов (например, Grid ) не имеет события Click, потому что оно для них неконкретизировано. Однако к ним все равно можно прикрепить (присоединить) обработчик наподобие свойства зависимости, позаимствовав событие у дочернего элемента, который способен это событие возбудить. Это не значит, что элемент с несвойственным ему вложенным событием будет способен его возбуждать, но возбужденное другими и движущееся через него событие он будет способен перехватить и обработать.
Обработчик события рекомендуется прикреплять в разметке XAML, поскольку такой способ поддерживает и обычные и маршрутизируемые события. Синтаксис прикрепления аналогичен определению свойств. В дескрипторе элемента мы пишем имя события и через знак присваивания - имя обработчика, а оболочка в ответ сама сгенерирует заготовку метода с соответствующей событию сигнатурой. Синтаксис вложенных событий аналогичен, но имеет префикс элемента зависимости, например
<Grid ButtonBase.Click="Grid_Click"> <Button Click="Button_Click"> Генератор Click </Button> </Grid>В такой конструкции при щелчке на кнопке Button возбудится событие Click. Это событие начнет всплывать к корню визуального дерева. Прежде всего оно будет обработано самой кнопкой, потому что мы прикрепили обработчик Button_Click. Затем оно будет обработано обработчиком Grid_Click, поскольку мы его тоже предусмотрели. И так далее, пока не достигнет корня визуального дерева. Даже если у кнопки Button не предусмотреть обработчик, то событие все равно будет ею возбуждено при щелчке на ней и начнет всплывать по дереву элементов.
Под маршрутизацией событий подразумевается движение событий (event bubbling) по дереву элементов в поиске своих обработчиков. Маршрутитизацию событий еще называют перенаправлением событий. Механизм этого движения можно рассмотреть на примере событий PreviewMouseDown (туннельное) и MouseDown (пузырьковое). При щелчке курсором мыши на каком-нибудь элементе окна WPF (назовем его целевым - target) вначале возбуждается туннельное событие PreviewMouseDown, которое движется от корня дерева к целевому элементу. Когда оно достигнет корня дерева, на смену ему возбуждается парное событие MouseDown, которое начинает движение в противоположном направлении: от целевого элемента к корню дерева.
В любом элементе по маршруту движения этих событий мы можем прикрепить соответствующий обработчик, перехватить событие и обработать его (а затем, если нужно, остановить). Прикрепленному обработчику вместе с событием передается объект аргументов (обычно вторым параметром). Для событий PreviewMouseDown и MouseDown это будет объект e типа MouseButtonEventArgs, для события Click это будет объект e типа RoutedEventArgs, для события KeyDown - объект e типа KeyEventArgs, и так далее. Каждый из этих объектов имеет булево свойство Handled, с помощью которого в любом из обработчиков можно прервать (остановить) дальнейшую маршрутизацию события, присвоив значение e.Handled=true.
Если по пути к целевому элементу мы останавливаем туннельное событие, то не будет возбуждаться и парное к нему пузырьковое событие. Дальнейшая маршрутизация событий может автоматически прерываться и другими событиями. Так например, при щелчке на кнопке вначале возбуждается туннельное событие PreviewMouseDown, которое движется от корня к кнопке. По достижении кнопки парное событие MouseDown не возбуждается, а вместо него начинает всплывать сгенерированное кнопкой событие Click.
Более того, если кнопка внутри себя содержит дочерние элементы и щелчок выполнен по одному из них, то событие PreviewMouseDown доходит до этого целевого элемента. Затем возбуждается парное пузырьковое событие MouseDown, но поднявшись до элемента кнопки оно подавляется и заменяется всплывающим событием Click. Кнопка сама устанавливает для события MouseDown флаг Handled=true и возбуждает собственное событие щелчка. Это действие соответствует наиболее естественному поведению кнопки.
Дата добавления: 2015-04-15; просмотров: 2273;