5 WPF routing events

Recommended for you: Get network issues from WhatsUp Gold. Not end users.

Routed events along the element tree tunnel down or bubble to tourism, and the event handler. Routed events can be handled in an element (such as a label) even though it by another element (such as a launch image of the inside of the tag). As the dependency property, routing events can understand how they work with the traditional method with the correct signature connection consumer - an event handler - but you need to understand the characteristics of them all,.

Understanding of routing events

.NET developers are familiar with the events of the thought of object (such as WPF elements) to send a message to notify you of code something happens. WPF event routing enhance the concept of.NET event model. Event routing allows the event sponsored by an element, but by another element processing. For example, clicking on the toolbar button, start event, rise to the toolbar, and then to include a window, until the incident is the code to process your.

Event routing allows you to easily write compact structure, good code, in the local event processing the most convenient. For the WPF content model, event routing is necessary, it allows a simple elements (such as button) contains a dozen different components, each with its own independent event collection.

The definition, registration, and the packaging of routing events

Routing events are read-only static field, registered in the static constructor, defined as the standard.Net event package.

For example, the Button class provides the Click event, it inherits from the abstract ButtonBase class. This is the definition and registration code event:

public abstract class ButtonBase : ContentControl, ...
{
    // Define the event
    public static readonly RoutedEvent ClickEvent;

    // The registered event
    static ButtonBase()
    {
        ButtonBase.ClickEvent = EventManager.RegisterRoutedEvent(
          "Click", RoutingStrategy.Bubble,
          typeof(RoutedEventHandler), typeof(ButtonBase));
        ...
    }

    // Packaging event
    public event RoutedEventHandler Click
    {
        add
        {
            base.AddHandler(ButtonBase.ClickEvent, value);
        }
        remove
        {
            base.RemoveHandler(ButtonBase.ClickEvent, value);
        }
    }

    ...
}

Routing EventManager.RegisterRoutedEvent events by () method of register. You must specify the name of the event, protocol type, delegate defines the event processor Grammar (RoutedEventHandler in this example,), and has the event class (in this case, ButtonBase).

Usually, the routing events packaging for general.NET events,.NET language can all access route events. Event wrapper using AddHandler () and RemoveHandler () method to add and remove registered the caller, two are defined in the FrameworkElement base class, all WPF elements inherit the two method.

Sharing routing events

Routing events can be shared between classes. For example, two classes UIElement and ContentElement use the MouseUp event, MouseUp event defined in the System.Windows.Input.Mouse class. UIElement and ContentElement simply use the RoutedEvent.AddOwner () method to reuse the event:

UIElement.MouseUpEvent = Mouse.MouseUpEvent.AddOwner(typeof(UIElement));

Event triggered routing

Of course, as with any event, define a class need to cause the routing events at some point, exactly where they occur is an implementation detail. However, the important factor is your event is not caused by the traditional.NET wrapper. But the use of RaiseEvent () method, all the elements of this method from the UIElement class inheritance. This is the corresponding code in the ButtonBase class:

var e = new RoutedEventArgs(ButtonBase.ClickEvent, this);
base.RaiseEvent(e);

RaiseEvent()Method is responsible for initiating event to each caller. The caller can directly call AddHandler () registered themselves, or they can use the event wrapper.

All of the WPF event to comply with the.NET agreement. The first parameter is the object that raised the event (sender). The second parameter is the EventArgs object, additional details may be used. For example, the MouseUp event to provide MouseEventArgs object, indicating when the event occurs when the mouse pressing a button which:

private void img_MouseUp(object sender, MouseButtonEventArgs e)
{
}

If an event does not need to transmit extra details, use the RoutedEventArgs class, which contains some of the events to be routed details. If the event need to transmit additional information, it uses a special RoutedEventArgs derived objects (such as MouseButtonEventArgs). Because each WPF event argument classes derived from RoutedEventArgs, each WPF event handlers are able to access information about the event routing.

Processing route events

Can be in the XAML tag attached event:

<Image Source="happyface.jpg" Stretch="None"
 Name="img" MouseUp="img_MouseUp" />

The naming convention for event handling method is: ElementName_EventName

The event handler code.:

img.MouseUp += new MouseButtonEventHandler(img_MouseUp);

This code in accordance with the requirements of the signature for the event structure of the delegate object (MouseButtonEventHandler), will be entrusted to point to the img_MouseUp () method, and then the delegate to register for the img.MouseUp event handler list.

You can omit the delegate object structure of MouseButtonEventHandler, the code above can also be abbreviated as:

img.MouseUp += img_MouseUp;

Can also use the UIElement.AddHandler event handler (connected):

img.AddHandler(Image.MouseUpEvent,new MouseButtonEventHandler(img_MouseUp));

This method requires a display structure delegate object MouseButtonEventHandler, because the UIElement.AddHandler () method to support all WPF events, the delegate type you want to add is not know.

Some developers more like using event was defined the class name, rather than the events are raised the name of the class, this is equivalent to the code, the MouseUp event is defined in the UIElement:

img.AddHandler(UIElement.MouseUpEvent,new MouseButtonEventHandler(img_MouseUp));

Remove event handlers can only through the code, you can use the decrement operator:

img.MouseUp -= img_MouseUp;

You can also use the UIElement.RemoveHandler () method:

img.RemoveHandler(Image.MouseUpEvent,new MouseButtonEventHandler(img_MouseUp));

Don't repeat to add the event handler.

Event routing

There are three schools of routing events:

The RoutedEventArgs class

For the bubble event, sender parameter points to the last ring chain. For example, if an event to tag images from the bubble, is pointed to by the sender parameter label.

The properties of the RoutedEventArgs class:

Name Description Source indicating what the object that raises the event. When the event occurs, the keyboard event case, refers to the control that has focus. In the case of mouse events, refers to the mouse pointer top element. OriginalSource refers to the first object that raises the event. Usually, OriginalSource is equivalent to Source. However, in some cases, OrignialSource refers to the object tree deeper elements. For example, if you click the window boundary, Source is a Window object, however, OriginalSource is the Border object. RoutedEvent provides a RoutedEvent object, used to trigger events processor, such as a static UIElement.MouseUpEvent object. This information is used in the use of the same event handler different events. Handled is used to suspend the bubble or tunneling process. When Handled is true, the event is no longer travel, not for any other elements triggered events.

Bubbling events

The following is a simple window used to demonstrate event bubbling. When a part of you click label, event sequences are displayed in a list box. The MouseUp event to travel through 5 layers, ended up in the BubbledLabelClick window.

bubblelabelclick

To create this test pane, image and each of its elders elements are connected to the same event processor - SomethingClicked () method.

<Window x:Class="RoutedEvents.BubbledLabelClick"
 xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
 xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
 Title="BubbledLabelClick" Height="359" Width="329"
 MouseUp="SomethingClicked">
  <Grid Margin="3" MouseUp="SomethingClicked">
    <Grid.RowDefinitions>
      <RowDefinition Height="Auto"></RowDefinition>
      <RowDefinition Height="*"></RowDefinition>
      <RowDefinition Height="Auto"></RowDefinition>
      <RowDefinition Height="Auto"></RowDefinition>
    </Grid.RowDefinitions>
      
    <Label Margin="5" Grid.Row="0" HorizontalAlignment="Left" 
     Background="AliceBlue" BorderBrush="Black" BorderThickness="1"
     MouseUp="SomethingClicked">
      <StackPanel MouseUp="SomethingClicked">
        <TextBlock Margin="3"
         MouseUp="SomethingClicked">
         Image and text label</TextBlock>
        <Image Source="happyface.jpg" Stretch="None" 
         MouseUp="SomethingClicked" />
        <TextBlock Margin="3" 
         MouseUp="SomethingClicked">
         Courtesy of the StackPanel</TextBlock>
      </StackPanel>
    </Label>
     
    <ListBox Grid.Row="1" Margin="5" Name="lstMessages"></ListBox>
    <CheckBox Grid.Row="2"  Margin="5" Name="chkHandle">
     Handle first event</CheckBox>
    <Button Grid.Row="3" Margin="5" Padding="3" HorizontalAlignment="Right"
     Name="cmdClear" Click="cmdClear_Click">Clear List</Button>
  </Grid>
</Window>

SomethingClicked()Simple method to check the properties of the RoutedEventArgs object and add a message to the list box:

protected int eventCounter = 0;

private void SomethingClicked(object sender, RoutedEventArgs e)
{            
    eventCounter++;
    string message = "#" + eventCounter.ToString() + ":\r\n" + 
      " Sender: " + sender.ToString() + "\r\n" +
      " Source: " + e.Source + "\r\n" +
      " Original Source: " + e.OriginalSource;
    lstMessages.Items.Add(message);
    e.Handled = (bool)chkHandle.IsChecked;
}

If you have selected the chkHandle check box, the SomethingClicked () method to set the RoutedEventArgs.Handled property to true, in the first event occurs, stop the event sequence. The results, you will see only the first event in the list.

Because the SomethingClicked () MouseUp event method treatment caused by Window, you will be able to intercept the window surface list box and click on the blank. But, when you click the Clear button (it removes all the contents of the list boxes), MouseUp event is not triggered. That is because the button compressed MouseUp events, and cause a higher level of Click events. At the same time, the Handled flag is set to true, the MouseUp event to stop.

The compression processing events

Use the AddHandler () method, third sets of parameters for true, even if the Handled flag is true, will also receive events:

cmdClear.AddHander(UIElement.MouseUpEvent, 
  new MouseButtonEventHandler(cmdClear_MouseUp), true);

From the design point of view, this is not a good decision.

Additional events

A panel, there is a set of buttons. As everyone knows, the panel does not support the button's Click event, how to define the button's Click event on the panel?

The answer is to use the additional events. Grammatical form additional events such as: ClassName.EventName, this is the code example:

<StackPanel Button.Click="DoSomething" Margin="5">
  <Button Name="cmd1">Command 1</Button>
  <Button Name="cmd2">Command 2</Button>
  <Button Name="cmd3">Command 3</Button>
  ...
</StackPanel> 

In the use of the code bind additional events, can only use the UIElement.AddHandler () method, can not be used since the addition operator.

pnlButtons.AddHandler(Button.Click, new RoutedEventHandler(DoSomething));

In the DoSomething () method, there are several methods that can know which button trigger events:

Object comparison test button:

private void DoSomething(object sender, RoutedEventArgs e)
{            
    if (e.Source == cmd1)
    { ... }
    else if (e.Source == cmd2)
    { ... }
    else if (e.Source == cmd3)
    { ... }
}

Use the Tag tag test button:

private void DoSomething(object sender, RoutedEventArgs e)
{
    object tag = ((FrameworkElement)sender).Tag;
    MessageBox.Show((string)tag);
}

Tunnel events

Tunnel events begin with the word Preview, tunnel events and bubbling events usually appear in pairs, always first trigger event initiated tunnel, bubbling event.

Tunnel events and bubbling events work the same, in the opposite direction. For example, in this case, if the definition is tunnel event PreviewMouseUp, click on the image in the label, first by the window of the PreviewMouseUp event, then Grid, then StackPanel and so on, until the arrival of the actual source, is the image in the label.

image

If you marked tunnel event for the Handled, bubble event will not occur. Because the two event to share the same RoutedEventArgs.

Tunnel events mainly used to perform some preprocessing, for example, take action or filter out specific mouse actions to specific keystroke.

The WPF event

The event is divided into five categories:

The mouse, keyboard, Stylus, Multitouch events together referred to as the input event.

The Lifetime event

The FrameworkElement class defines the Lifetime event

Initialized

Occurs in elements is instantiated, and its properties in accordance with the XAML tag set. At this point, the elements are initialized, however, has not used the style and data binding. At this point, the IsInitialized attribute is true. Initialized is the direct events, not routed event.

Loaded

In the window has been initialized, and the style and data binding has been used. This is the last stop before elements show. At this point, the IsLoaded attribute is true.

Unloaded

In the following situations, element release, container window is closed, the specified element is removed from the window.

FrameworkElement implements the ISupportInitialize interface, there are two methods: BeginInit (), EndInit (). The rendering process:

  1. Element is instantiated
  2. BeginInit()
  3. All property elements
  4. Trigger the Initialized event
  5. EndInit()

When creating a window, each branch elements in bottom-up order has been initialized. This means that, before the container is nested elements initializing them. When the initial event occurs, sub elements must have the elements have been fully initialized. However, contains the elements might not have been initialized, the other part and also not sure the window has been initialized.

In each element is initialized, each element being in its own container, is style, is bound to a data source, if necessary. In the Initialized window after the incident, is to enter the next step.

The Loaded event and Initialized event according to path instead of loading, in other words, the container window is first loaded, then it contains elements. When all the elements of the Loaded event to complete, the window is displayed, the elements are presented.

Relative to the FrameworkElement class, window class to add the Lifetime event:

SourceInitialized

Occurrence is obtained in the HwndSource attribute in the window before. The HwndSource property is the legacy code in the function.

ContentRendered

After the window appears. In this case, do not affect the visual appearance of the window code may, also do not enforce the second presentation. However, ContentRendered does the instruction window completely visible, and ready for input.

Activated

Happened to the window in the user switches, probably from another window of the application, may also be from another application. The window is first loaded, also raises the Activated event. Equivalent to the control's GotFocus event.

Deactivated

In the user from the window when you leave, may be to another window of the application, it may be another application. When the window is closed, after the Closing event, Closed event, Deactivated event is raised. Equivalent to the control's LostFocus event.

Closing

When the window was closed. Or the user operation, or use Window.Close in code () method, or Application.Shutdown () method. By setting the CancelEventArgs.Cancel property to true, Closing events can be lifted off the operation, keep the window open. However, if the computer shutdown or log off the Closing event does not occur. In order to deal with this possibility, you need to handle the Application.SessionEnding event.

Closed

Occurs in the window closed. However, the element object is accessible, and the Unloaded event has not occurred. Here, you can perform the cleanup, write to the configuration file into the registry, etc.

If you only want to first initializes the control, the best in the Loaded event is triggered. Usually, all initialization can be performed in the Window.Loaded event.

Also in the Window constructor (InitializeComponent) statement after adding the initialization code. However, if the initialization code throws an exception, the best move that initialization code to the Loaded event.

The input event

The input event custom event parameters derived from the InputEventArgs class.

Add the InputEventArgs class two properties: Timestamp and Device.

Timestamp provides an integer, when to use MS indicating events. Time stamp does not represent the actual time. The time stamp value large said after the events.

Device returns the triggering event equipment information object. Can be a mouse, keyboard, or a stylus. Each is a class, are derived from the abstract System.Windows.Input.InputDevice class.

The next few sections, will detail how to handle the mouse, keyboard, and multi touch behavior.

Keyboard entry

All the elements of the sequence of events by keyboard:

PreviewKeyDown

The event occurred in the tunnel, and press the button

KeyDown

Bubbling events, occurring in the key is pressed

PreviewTextInput

The event occurred in the tunnel, keystrokes, and receives a text input element. When the button is not text input, this event will not occur. Such as: Ctrl, Shift, Backspace, the arrow keys, function keys and so on.

TextInput

Bubbling events, occurred in keystroke, and receives a text input element. When the button is not text input, this event will not occur.

PreviewKeyUp

Tunnel incident occurred, was released on bond.

KeyUp

Bubbling events occurred, was released on bond.

Some controls compression events, they can execute more special keyboard yourself. For example, a text box control compression of the TextInput event, compressed the function key KeyDown events. However, there is no compression tunnel events PreviewTextInput and PreviewKeyDown, they can still use.

The text box controls also add new events, TextChanged events. In the next keystroke caused text box text after the change. At this time, the new text is displayed in the text box. So too can not stop the keystroke undesirable.

The button

Example: monitoring a text box all possible key events, and the report when events occur. Figure 5-6 shows the result type a capital letter S.

Each button will cause PreviewKeyDown and KeyDown events. However, the TextInput event only when a character is entered to the element was triggered. In fact, type A behavior may involve a combination key. For example, when you type a capital S, you use the Shift+S key, the results, you will see the two KeyDown and KeyUp events, but only a TextInput event.

PreviewKeyDown,KeyDown,PreviewKeyUp,And KeyUp events using the KeyEventArgs object provides information. The most important is the Key attribute, return to the System.Windows.Input.Key enumeration value. Recognition is pressed or released key. This is the key event handler:

private void KeyEvent(object sender, KeyEventArgs e)
{
    string message = "Event: " + e.RoutedEvent + " " +
      " Key: " + e.Key;
    lstMessages.Items.Add(message);            
}

The Key value does not consider other key state. For example, when you press the S key, regardless of whether or not to press the Shift key, will receive the same key(Key.S).

By default, hold down a key for a period of time, will duplicate keys. So, when the input Shift+S, may have multiple Shift, if you want to ignore repeated Shift bond, rely on check the KeyEventArgs.IsRepeat property, you can determine whether a keystroke is key has been pressing the results. As shown below:

if ((bool)chkIgnoreRepeat.IsChecked && e.IsRepeat) return;

The TextInput event for TextCompositionEventArgs objects. This object contains a Text property, process text controls incoming. See the code:

private void TextInput(object sender, TextCompositionEventArgs e)
{
    var message = "Event: " + e.RoutedEvent + " " +
      " Text: " + e.Text;
    lstMessages.Items.Add(message);
}

KeyConverter for the Key enumeration value is converted to a string of more useful:

KeyConverter converter = new KeyConverter();
string key = converter.ConvertToString(e.Key);

Consider a accepts only numeric text box, in the PreviewTextInput, are verified and the PreviewKeyDown event, because some input will be bypassed the PreviewTextInput event, but, PreviewKeyDown is too low level, providing more information for the same input. Best practice is to verify, most input in PreviewTextInput, verify the PreviewTextInput missing input in PreviewKeyDown.

private void pnl_PreviewTextInput(object sender, TextCompositionEventArgs e)
{
    short val;
    if (!Int16.TryParse(e.Text, out val))
    {
        // Rejection of non digital key input
        e.Handled = true;
    } 
}

private void pnl_PreviewKeyDown(object sender, KeyEventArgs e)
{
    if (e.Key == Key.Space)
    {
        // Refuse to space, it will not cause the PreviewTextInput event. 
        e.Handled = true;
    }
}

 

The text you can connect the two event handler to a single frame, can connect them to include several input only container numbers in a text box, in order to improve the coding efficiency.

Focus

In order to control will receive focus, its Focusable property must be true, which is all the default values for controls. The Focusable property is defined in the UIElement class, which means that the non control element can also get focus. Usually, than control class Focusable default to false. However, you can set it to true.

If you do not explicitly set the Tab sequence, when you press the Tab key to switch the focus, the focus will move to the first element of the current element, if the current element has no child elements, to the next sub elements of the same layer.

If you set the TabIndex property for each control, TabIndex 0 won the first focus control, then jump to the next highest TabIndex value (such as 1, then 2, then 3, etc.). If more than one element with the same TabIndex value, the tab series automatic use, jump to the then recent elements.

The default TabIndex attributes all control values for the Int32.MaxValue.

In addition to the TabIndex attribute, the Control class also defines the IsTabStop property, set to false, the Tab key is pressed, the control will not receive the focus. However, with the Focusable false, controls can still through other means (such as a mouse click to call Fucus () method, in the code) focus.

When the control is not visible or not available, no matter how to set the TabIndex, IsTabStop, and Focusable properties, these controls are not included in the Tab sequence. Control the visible property is Visibility, the control of available attribute is IsEnabled.

Key state

The key events of KeyEventArgs object contains a KeyStates property, the property trigger event key. The KeyboardDevice attribute for any key on the keyboard of the same information.

The KeyboardDevice attribute provides an instance of the KeyboardDevice class. It has two attributes: FocusedElement, Modifiers. Focus element refers to the events with focal elements. The modifier is when the event occurs and what modifier key is pressed, including Shift, Ctrl, and Alt. And you can use the bitwise logical check their status, like this:

if ((e.KeyboardDevice.Modifiers & ModifierKeys.Control) == ModifierKeys.Control)
{
    lblInfo.Text = "You held the Control key.";
}

 

The KeyboardDevice property also provides several convenient, listed in table 5-5. For each method, you pass in a Key enumeration value.

IsKeyDown(), IsKeyUp(): Test key is pressed or is not pressed.

IsKeyToggled(): Whether a key is in an open state a. This can only be applied to a switching state bonds, such as Caps Lock, Scroll Lock, and Num Lock.

GetKeyStates(): Return to one or more of the KeyStates enumeration value, to tell you the key is not pressed, press, or open. This method is essentially equivalent to also call IsKeyDown () and IsKeyToggled().

When you use the KeyEventArgs.KeyboardDevice property, your code to obtain a key state virtual. This means it gets keyboard at the time of the incident state. This is not necessarily equal to the current keyboard state. For example, consider if the user typing code performs faster than what happens. Every time your KeyPress event, you will visit the keystroke trigger events, rather than just typed character. This is almost always the behavior you want.

However, you are not limited to obtain the key information in the key events. You can get keyboard state at any time. The trick is to use the keyboard to type Keyboard, which is very similar to KeyboardDevice, except that it is a member of the static. This is an example of the use of Keyboard detection, left Shift key current state:

if (Keyboard.IsKeyDown(Key.LeftShift))
{
    lblInfo.Text = "The left Shift is held down.";
}

Mouse input

The most fundamental mouse events allow you to respond to move the mouse over an element. These events are MouseEnter (when the mouse pointer on the element) and MouseLeave (when the mouse pointer leaves elements). These two events are events.

For example, if you have a StackPanel, which contains a button. You move the mouse pointer to the button, the MouseEnter event will be the first initiated by StackPanel (as you enter it, and then the boundary) button (because you move directly on it). When you move the mouse, the MouseLeave event will be the first to button trigger, and then on the StackPanel.

The mouse moves raises two events: PreviewMouseMove (tunnel event) and MouseMove (bubble event). All these events provide a MouseEventArgs object. The MouseEventArgs object contains attributes some mouse button state. It contains a GetPosition () method, obtaining relative to a coordinate of an element. This is an example of a pixel, and device independent display the mouse pointer relative to the position of the window:

private void MouseMoved(object sender, MouseEventArgs e)
{
    Point pt = e.GetPosition(this);
    lblInfo.Text = 
      String.Format("You are at ({0},{1}) in window coordinates",
      pt.X, pt.Y);
}

In this case, coordinates from the upper left corner of the window's client area measurement (in the title bar below).

The UIElement class has two attributes:

IsMouseOver, To determine whether the mouse in an element, or a sub element of it.

IsMouseDirectlyOver, To determine whether the mouse in an element, but not in its child elements.

Mouse click

All elements of the mouse click event

Name type description of PreviewMouseLeftButtonDown routing

PreviewMouseRightButtonDown tunnel occurred at MouseLeftButtonDown when mouse button is pressed

MouseRightButtonDown bubble occurred at PreviewMouseLeftButtonUp when mouse button is pressed

PreviewMouseRightButtonUp tunnel MouseLeftButtonUp in the mouse button is released

MouseRightButtonUp bubble occurs in the mouse button is released

The mouse button events all provide a MouseButtonEventArgs object. The MouseButtonEventArgs class derived from the MouseEventArgs class (which means that it contains the same coordinates and the button state information), and it added several members. MouseButton (you trigger the event to tell which button) and ButtonState (tell you when the event occurs when the button is pressed or not pressed). ClickCount, This tells you how much time the button is clicked, can be used to distinguish between click (ClickCount is 1), click (ClickCount 2).

Usually, Windows application in the mouse click reaction (up events rather than the down event).

Some elements of higher levels of mouse events. For example, the Control class to add the PreviewMouseDoubleClick and MouseDoubleClick events instead of MouseLeftButtonUp events. Similarly, the Button class has a Click event, can be a mouse or keyboard to trigger.

In order to obtain the current position of the mouse and the mouse button state, static member you can use the Mouse class, the corresponding member is similar to the MouseButtonEventArgs.

Recommended from our users: Dynamic Network Monitoring from WhatsUp Gold from IPSwitch. Free Download

Posted by Austin at December 06, 2013 - 5:02 AM