Translate

Wednesday, 30 October 2013

WPF Routed Events

WPF Routed Events


The .NET Framework defines a standard mechanism for managing events. A class
may expose several events, and each event may have any number of subscribers.
WPF augments this standard mechanism to overcome a limitation: if a normal .NET
event has no registered handlers, it is effectively ignored.
Consider what this would mean for a typical WPF control. Most controls are made
up of multiple visual components. For example, suppose you give a button a very
plain appearance consisting of a single Rectangle, and provide a simple piece of text
as the content. (Chapter 9 describes how to customize a control’s appearance.) Even
with such basic visuals, there are still two elements present: the text and the rectangle.
The button should respond to a mouse click whether the mouse is over the text
or the rectangle. In the standard .NET event handling model, this would mean registering
a MouseLeftButtonUp event handler for both elements.
This problem would get much worse when taking advantage of WPF’s content
model. A Button is not restricted to having plain text as a caption—it can contain any
object as content. The example is not especially ambitious, but even
this has six visible elements: the yellow outlined circle, the two dots for the eyes, the
curve for the mouth, the text, and the button background itself. Attaching event
handlers for every single element would be tedious and inefficient. Fortunately, it’s
not necessary.
WPF uses routedevents , which are rather more thorough than normal events. Instead
of just calling handlers attached to the element that raised the event, WPF walks the
tree of user interface elements, calling all handlers for the routed event attached to
any node from the originating element right up to the root of the user interface tree.
This behavior is the defining feature of routed events, and is at the heart of event
handling in WPF.
Example 4-1 shows markup for the button in . If one of the Ellipse elements
inside the Canvas were to receive input, event routing would enable the Button,
Grid, Canvas, and Ellipse to receive the event, as shows.

Example. Handling events in a user interface tree
<Button PreviewMouseDown="PreviewMouseDownButton"
MouseDown="MouseDownButton">
<Grid PreviewMouseDown="PreviewMouseDownGrid"
MouseDown="MouseDownGrid">
<Grid.ColumnDefinitions>
<ColumnDefinition />
<ColumnDefinition />
</Grid.ColumnDefinitions>
<Canvas PreviewMouseDown="PreviewMouseDownCanvas"
MouseDown="MouseDownCanvas"
Width="20" Height="18" VerticalAlignment="Center">
<Ellipse PreviewMouseDown="PreviewMouseDownEllipse"
MouseDown="MouseDownEllipse"
x:Name="myEllipse"
Canvas.Left="1" Canvas.Top="1" Width="16" Height="16"
Fill="Yellow" Stroke="Black" />

A routed event can either be bubbling, tunneling, or direct. A bubbling event starts by
looking for event handlers attached to the target element that raised the event, and
then looks at its parent and then its parent’s parent, and so on until it reaches the
root of the tree; this order is indicated by the numbers in . A tunneling
event works in reverse—it looks for handlers at the root of the tree first and works its
way down, finishing with the originating element.
Direct events work like normal .NET events: only handlers attached directly to the
originating element are notified—no real routing occurs. This is typically used for
events that make sense only in the context of their target element. For example, it
would be unhelpful if mouse enter and leave events were bubbled or tunneled—the
parent element is unlikely to care about when the mouse moves from one child element
to another. At the parent element, you would expect “mouse leave” to mean “the
mouse has left the parent element,” and because direct event routing is used, that’s
exactly what it does mean. If bubbling were used, the event would effectively mean
“the mouse has left an element that is inside the parent, and is now inside another element
that may or may not be inside the parent,” which would be less useful.

Friday, 18 October 2013

WPF GRID

WPF GRID

Consider the document Properties dialog from Internet Explorer .
Notice how the main area of the form is arranged as two columns. The column on the
left contains labels, and the column in the middle contains information.

Achieving this kind of layout with any of the panels we’ve looked at so far is difficult,
because they are not designed with two-dimensional alignment in mind. We
could try to use nesting—Example 3-6 shows a vertical StackPanel with three rows,
each with a horizontal StackPanel.

The result, is not what we want at all. Each row has been
arranged independently, so we don’t get the two columns we were hoping for.
The Grid panel solves this problem. Rather than working a single row or a single column
at a time, it aligns all elements into a grid that covers the whole area of the
panel. This allows consistent positioning from one row to the next. Example 3-7
shows the same elements as Example 3-6, but arranged with a Grid rather than
nested StackPanel elements.
Example . Ineffective use of StackPanel
<StackPanel Orientation="Vertical" Background="Beige">
<StackPanel Orientation="Horizontal">
<TextBlock>Protocol:</TextBlock>
<TextBlock>HyperText Transfer Protocol</TextBlock>
</StackPanel>
<StackPanel Orientation="Horizontal">
<TextBlock>Type:</TextBlock>
<TextBlock>HTML Document</TextBlock>
</StackPanel>
<StackPanel Orientation="Horizontal">
<TextBlock>Connection:</TextBlock>
<TextBlock>Not Encrypted</TextBlock>
</StackPanel>
</StackPanel>


The Grid needs to know how many columns and rows we require, and we indicate
this by specifying a series of ColumnDefinition and RowDefinition elements at the
start. This may seem rather verbose—a simple pair of properties on the Grid itself
might seem like a simpler solution. However, you will often need to control the characteristics
of each column and row independently, so in practice, it makes sense to
have elements representing them.
Notice that each element in the grid has its column and row specified explicitly using
attached properties. This is mandatory—without these, everything ends up in column
0, row 0. (Grid uses a zero-based numbering scheme, so 0,0 corresponds to the
top-left corner.)

This default “one size fits all” behavior is useful when you want all the items in the
grid to be the same size, but it’s not what we want here. It would make more sense
for the column on the left to be wide enough to contain the labels, and for the column
on the right to be allocated the remaining space. Fortunately, the Grid provides
a variety of options for managing column width and row height.


Grid, Element Order, and Z Order

You might be wondering why the Grid doesn’t simply put items into the grid in the order
in which they appear; this would remove the need for the Grid.Row and Grid.Column
attached properties. However, grids do not necessarily have exactly one element per cell.
Grid cells can be empty. If the grid’s children simply filled the cells in order, you would
need to provide placeholders of some kind to indicate blank cells. But because elements
indicate their grid position, you can leave cells empty simply by providing no
content for those cells.
Elements may span multiple cells, by using the Grid.RowSpan and Grid.ColumnSpan
attached properties.
Cells can also contain multiple elements. In this case, the order in which the relevant
elements are listed in the markup determines which appears “on top.” Elements that
appear later in the document are drawn over those that appear earlier. The order in
which overlapping elements are drawn is usually referred to as the Z order. This is
because the x- and y-axes are traditionally the ones used for drawing on-screen, so the
z-axis, representing the third dimension, “sticks out” of the screen. This makes it the
logical axis to represent how overlapping elements stack up on top of one another.
In general, panels that allow their children to overlap (e.g., Grid and Canvas) rely on the
order in which elements appear in the XAML to determine the Z order. However, you
can override this: the attached Panel.ZIndex property allows the Z order to be specified

explicitly.