Marcando Eventos Roteados como Manipulados e Manipulação de Classes

Handlers for a routed event can mark the event handled within the event data. Handling the event will effectively shorten the route. Class handling is a programming concept that is supported by routed events. A class handler has the opportunity to handle a particular routed event at a class level with a handler that is invoked before any instance handler on any instance of the class.

Este tópico contém as seguintes seções.

  • Prerequisites
  • When to Mark Events as Handled
  • "Visualização" (Encapsulamento) Eventos vs.Propagação de eventos e manipulação de eventos
  • Class Handlers and Instance Handlers
  • Class Handling of Routed Events by Control Base Classes
  • Adding Instance Handlers That Are Raised Even When Events Are Marked Handled
  • Deliberately Suppressing Input Events for Control Compositing
  • Tópicos relacionados

Prerequisites

This topic elaborates on concepts introduced in the Visão geral sobre eventos roteados.

When to Mark Events as Handled

When you set the value of the Handled property to true in the event data for a routed event, this is referred to as "marking the event handled". There is no absolute rule for when you should mark routed events as handled, either as an application author, or as a control author who responds to existing routed events or implements new routed events. For the most part, the concept of "handled" as carried in the routed event's event data should be used as a limited protocol for your own application's responses to the various routed events exposed in WPF APIs as well as for any custom routed events. Outra maneira de se considerar o "tratados" problema é que você geralmente deve marcar um evento roteado manipulado se seu código respondeu ao evento roteado de forma significativa e relativamente completa. Normalmente, não deve haver mais de uma resposta significativa que exige a implementações de manipulador separado para qualquer ocorrência de eventos roteados único. If more responses are needed, then the necessary code should be implemented through application logic that is chained within a single handler rather than by using the routed event system for forwarding. The concept of what is "significant" is also subjective, and depends on your application or code. Como diretriz geral, alguns "resposta significativa" Exemplos incluem: Definindo o foco, modificando o estado público, definindo as propriedades que afetam a representação visual e disparar outros novos eventos. Exemplos de respostas de nonsignificant: modificar um estado particular (com nenhum impacto visual ou representação programática), log de eventos, ou observando os argumentos de um evento e optando por não responder a ele.

The routed event system behavior reinforces this "significant response" model for using handled state of a routed event, because handlers added in XAML or the common signature of AddHandler are not invoked in response to a routed event where the event data is already marked handled. You must go through the extra effort of adding a handler with the handledEventsToo parameter version (AddHandler(RoutedEvent, Delegate, Boolean)) in order to handle routed events that are marked handled by earlier participants in the event route.

In some circumstances, controls themselves mark certain routed events as handled. Um evento roteado manipulado representa uma decisão por WPF controlar os autores que a ações do controle em resposta ao evento roteado são significativa ou completa como parte da implementação de controle e o evento precisa sem tratamento adicional. Usually this is done by adding a class handler for an event, or by overriding one of the class handler virtuals that exist on a base class. Você ainda pode contornar esse evento tratamento se necessário. consulte Trabalhando em torno de supressão de eventos por controles posteriormente neste tópico.

"Visualização" (Encapsulamento) Eventos vs.Propagação de eventos e manipulação de eventos

Preview routed events are events that follow a tunneling route through the element tree. The "Preview" expressed in the naming convention is indicative of the general principle for input events that preview (tunneling) routed events are raised prior to the equivalent bubbling routed event. Also, input routed events that have a tunneling and bubbling pair have a distinct handling logic. If the tunneling/preview routed event is marked as handled by an event listener, then the bubbling routed event will be marked handled even before any listeners of the bubbling routed event receive it. The tunneling and bubbling routed events are technically separate events, but they deliberately share the same instance of event data to enable this behavior.

The connection between the tunneling and bubbling routed events is accomplished by the internal implementation of how any given WPF class raises its own declared routed events, and this is true of the paired input routed events. Mas, a menos que essa implementação de nível de classe existe, não há nenhuma conexão entre um evento roteado de encapsulamento e um evento roteado bubbling que compartilham o esquema de nomeação: sem tal implementação, eles seriam dois os eventos roteados totalmente separados e não seriam gerados em seqüência ou compartilhamento de dados de evento.

For more information about how to implement tunnel/bubble input routed event pairs in a custom class, see Como: Criar um evento roteado personalizado.

Class Handlers and Instance Handlers

Eventos roteados considere os dois tipos diferentes de ouvintes de evento: os ouvintes de classe e ouvintes de instância. Class listeners exist because types have called a particular EventManager API ,RegisterClassHandler, in their static constructor, or have overridden a class handler virtual method from an element base class. Os ouvintes de instância são elementos/instâncias de determinada classe, onde um ou mais manipuladores foram anexadas para esse evento roteado por uma chamada para AddHandler. Existentes WPF roteadas eventos faça chamadas AddHandler como parte do common language runtime (CLR) wrapper de evento {} de adicionar e remover {} a implementações do evento, que também é como simples XAML o mecanismo de anexar manipuladores de eventos por meio de uma sintaxe de atributo está habilitado. Therefore even the simple XAML usage ultimately equates to an AddHandler call.

Elements within the visual tree are checked for registered handler implementations. Handlers are potentially invoked throughout the route, in the order that is inherent in the type of the routing strategy for that routed event. For instance, bubbling routed events will first invoke those handlers that are attached to the same element that raised the routed event. Then the routed event "bubbles" to the next parent element and so on until the application root element is reached.

From the perspective of the root element in a bubbling route, if class handling or any element closer to the source of the routed event invoke handlers that mark the event arguments as being handled, then handlers on the root elements are not invoked, and the event route is effectively shortened before reaching that root element. However, the route is not completely halted, because handlers can be added using a special conditional that they should still be invoked, even if a class handler or instance handler has marked the routed event as handled. This is explained in Adding Instance Handlers That Are Raised Even When Events Are Marked Handled, later in this topic.

At a deeper level than the event route, there are also potentially multiple class handlers acting on any given instance of a class. This is because the class handling model for routed events enables all possible classes in a class hierarchy to each register its own class handler for each routed event. Each class handler is added to an internal store, and when the event route for an application is constructed, the class handlers are all added to the event route. Class handlers are added to the route such that the most-derived class handler is invoked first, and class handlers from each successive base class are invoked next. Generally, class handlers are not registered such that they also respond to routed events that were already marked handled. Therefore, this class handling mechanism enables one of two choices:

  • Derived classes can supplement the class handling that is inherited from the base class by adding a handler that does not mark the routed event handled, because the base class handler will be invoked sometime after the derived class handler.

  • Derived classes can replace the class handling from the base class by adding a class handler that marks the routed event handled. You should be cautious with this approach, because it will potentially change the intended base control design in areas such as visual appearance, state logic, input handling, and command handling.

Class Handling of Routed Events by Control Base Classes

On each given element node in an event route, class listeners have the opportunity to respond to the routed event before any instance listener on the element can. For this reason, class handlers are sometimes used to suppress routed events that a particular control class implementation does not wish to propagate further, or to provide special handling of that routed event that is a feature of the class. For instance, a class might raise its own class-specific event that contains more specifics about what some user input condition means in the context of that particular class. The class implementation might then mark the more general routed event as handled. Class handlers are typically added such that they are not invoked for routed events where shared event data was already marked handled, but for atypical cases there is also a RegisterClassHandler(Type, RoutedEvent, Delegate, Boolean) signature that registers class handlers to invoke even when routed events are marked handled.

Class Handler Virtuals

Some elements, particularly the base elements such as UIElement, expose empty "On*Event" and "OnPreview*Event" virtual methods that correspond to their list of public routed events. These virtual methods can be overridden to implement a class handler for that routed event. The base element classes register these virtual methods as their class handler for each such routed event using RegisterClassHandler(Type, RoutedEvent, Delegate, Boolean) as described earlier. The On*Event virtual methods make it much simpler to implement class handling for the relevant routed events, without requiring special initialization in static constructors for each type. For instance, you can add class handling for the DragEnter event in any UIElement derived class by overriding the OnDragEnter virtual method. Within the override, you could handle the routed event, raise other events, initiate class-specific logic that might change element properties on instances, or any combination of those actions. You should generally call the base implementation in such overrides even if you mark the event handled. Calling the base implementation is strongly recommended because the virtual method is on the base class. The standard protected virtual pattern of calling the base implementations from each virtual essentially replaces and parallels a similar mechanism that is native to routed event class handling, whereby class handlers for all classes in a class hierarchy are called on any given instance, starting with the most-derived class' handler and continuing to the base class handler. You should only omit the base implementation call if your class has a deliberate requirement to change the base class handling logic. Whether you call the base implementation before or after your overriding code will depend on the nature of your implementation.

Input Event Class Handling

The class handler virtual methods are all registered such that they are only invoked in cases where any shared event data are not already marked handled. Also, for the input events uniquely, the tunneling and bubbling versions typically are raised in sequence and share event data. This entails that for a given pair of class handlers of input events where one is the tunneling version and the other is the bubbling version, you may not want to mark the event handled immediately. If you implement the tunneling class handling virtual method to mark the event handled, that will prevent the bubbling class handler from being invoked (as well as preventing any normally registered instance handlers for either the tunneling or bubbling event from being invoked).

Once class handling on a node is complete, the instance listeners are considered.

Adding Instance Handlers That Are Raised Even When Events Are Marked Handled

The AddHandler method supplies a particular overload that allows you to add handlers that will be invoked by the event system whenever an event reaches the handling element in the route, even if some other handler has already adjusted the event data to mark that event as handled. This is not typically done. Generally, handlers can be written to adjust all areas of application code that might be influenced by an event, regardless of where it was handled in an element tree, even if multiple end results are desired. Also, typically there is really only one element that needs to respond to that event, and the appropriate application logic had already happened. But the handledEventsToo overload is available for the exceptional cases where some other element in an element tree or control compositing has already marked an event as handled, but other elements either higher or lower in the element tree (depending on route) still wish to have their own handlers invoked.

When to Mark Handled Events as Unhandled

Generally, routed events that are marked handled should not be marked unhandled (Handled set back to false) even by handlers that act on handledEventsToo. However, some input events have high-level and lower-level event representations that can overlap when the high-level event is seen at one position in the tree and the low-level event at another position. For instance, consider the case where a child element listens to a high-level key event such as TextInput while a parent element listens to a low-level event such as KeyDown. If the parent element handles the low-level event, the higher-level event can be suppressed even in the child element that intuitively should have first opportunity to handle the event.

In these situations it may be necessary to add handlers to both parent elements and child elements for the low-level event. The child element handler implementation can mark the low-level event as handled, but the parent element handler implementation would set it unhandled again so that further elements up the tree (as well as the high-level event) can have the opportunity to respond. This situation is should be fairly rare.

Deliberately Suppressing Input Events for Control Compositing

The main scenario where class handling of routed events is used is for input events and composited controls. A composited control is by definition composed of multiple practical controls or control base classes. Often the author of the control wishes to amalgamate all of the possible input events that each of the subcomponents might raise, in order to report the entire control as the singular event source. In some cases the control author might wish to suppress the events from components entirely, or substitute a component-defined event that carries more information or implies a more specific behavior. O exemplo canônico que está imediatamente visível para qualquer autor do componente é como um Windows Presentation Foundation (WPF) Button trata qualquer evento de mouse que eventualmente resolverá para o evento intuitivo que todos os botões têm: um Click de evento.

The Button base class (ButtonBase) derives from Control which in turn derives from FrameworkElement and UIElement, and much of the event infrastructure needed for control input processing is available at the UIElement level. Em particular, UIElement gerais de processos Mouse eventos que lidar com o teste de visitas para o cursor do mouse dentro de seus limites e oferece eventos distintos para as ações de botão mais comuns, como MouseLeftButtonDown. UIElementtambém fornece um vazio virtual OnMouseLeftButtonDown como o manipulador de classe pré-registrados de MouseLeftButtonDown, e ButtonBase substitui o proprietário. Similarly, ButtonBase uses class handlers for MouseLeftButtonUp. In the overrides, which are passed the event data, the implementations mark that RoutedEventArgs instance as handled by setting Handled to true, and that same event data is what continues along the remainder of the route to other class handlers and also to instance handlers or event setters. Also, the OnMouseLeftButtonUp override will next raise the Click event. O resultado final para a maioria dos ouvintes que será o MouseLeftButtonDown e MouseLeftButtonUp eventos "desapareçam" e são substituídos, em vez disso, por Click, um evento que contém o que significa mais como se sabe que este evento foi originado a partir de um botão verdadeiro e não alguns composto parte do botão ou algum outro elemento inteiramente.

Working Around Event Suppression by Controls

Sometimes this event suppression behavior within individual controls can interfere with some more general intentions of event handling logic for your application. For instance, if for some reason your application had a handler for MouseLeftButtonDown located at the application root element, you would notice that any mouse click on a button would not invoke MouseLeftButtonDown or MouseLeftButtonUp handlers at the root level. The event itself actually did bubble up (again, event routes are not truly ended, but the routed event system changes their handler invocation behavior after being marked handled). When the routed event reached the button, the ButtonBase class handling marked the MouseLeftButtonDown handled because it wished to substitute the Click event with more meaning. Therefore, any standard MouseLeftButtonDown handler further up the route would not be invoked. There are two techniques you can use to ensure that your handlers would be invoked in this circumstance.

A primeira técnica é deliberadamente, adicionar o manipulador usando a handledEventsToo assinatura do AddHandler(RoutedEvent, Delegate, Boolean). A limitation of this approach is that this technique for attaching an event handler is only possible from code, not from markup. The simple syntax of specifying the event handler name as an event attribute value via Extensible Application Markup Language (XAML) does not enable that behavior.

The second technique works only for input events, where the tunneling and bubbling versions of the routed event are paired. For these routed events, you can add handlers to the preview/tunneling equivalent routed event instead. That routed event will tunnel through the route starting from the root, so the button class handling code would not intercept it, presuming that you attached the Preview handler at some ancestor element level in the application's element tree. If you use this approach, be cautious about marking any Preview event handled. For the example given with PreviewMouseLeftButtonDown being handled at the root element, if you marked the event as Handled in the handler implementation, you would actually suppress the Click event. That is typically not desirable behavior.

Consulte também

Tarefas

Como: Criar um evento roteado personalizado

Referência

EventManager

Conceitos

Visualização de Eventos

Visão geral sobre eventos roteados