Important new concepts of WPF

Source: Internet
Author: User

Important new concepts of WPF

Source: http://www.cnblogs.com/free722/archive/2011/11/12/2238654.html

Logical tree and visual tree

XAML is designed to present user interfaces, because it has hierarchical features. In WPF, the user interface is built by an object tree, which is called a logic tree.

The logic tree of the WPF user interface is not necessarily created using XAML. It may be implemented using procedural code.

The logic tree concept is intuitive, but why do you need to pay attention to it? Almost every aspect of WPF (attributes, resources, events, and so on) has behaviors associated with the logic tree. For example, attribute values are automatically transmitted to sub-elements along the tree, and triggered events can traverse the tree from bottom to top or down.

A concept similar to the logic tree is the visual tree. The visual tree is basically an extension of the logic tree. In the visual tree, nodes are scattered and distributed to the core visual components. The visual tree provides some detailed visualization implementation, rather than treating each element as a "black box ". For example, although ListBox is logically a separate control, its default visual rendering is composed of more original WPF elements: A Border object, two scrollbars, and other elements.

Not all logical Tree nodes appear in the Visual tree. Only elements derived from System. Windows. Media. Visual or System. Windows. Media. Visual3D are included. Other elements are not included because they are not inherently rendered.

Use System. Windows. LogicTreeHelper and System. Windows. Media. VisualTreeHelper to traverse the logical tree and visual tree.

Note: Do not write code based on the specific visual tree. The logic tree is static and will not be disturbed by programmers (such as dynamic addition/deletion) elements, but the visual tree will change as long as users switch to different Windows themes.

Sample Code for traversing and printing a logical tree and a visual tree:

using System;
using System.Diagnostics;
using System.Windows;
using System.Windows.Media;

public parital class AboutDialog : Window
{
public AboutDialog()
{
IntializeComponent();
PrintLogicalTree(0, this);
}
    protected override void OnContentRendered(EventArgs e)
    {
        base.OnContentRendered(e);
        PrintVisualTree(0, this);
}

Void PrintLogicalTree (int depth, object obj)
{
// Print the object and use a leading space to indicate the depth
Debug. WriteLine (new string ('', depth) + obj );

// Sometimes, the leaf node is not DependencyObject, such as string
If (! (Obj is DependencyObject) return;

// Call each logical subnode recursively
Foreach (object child in LogicalTreeHelper. GetChildren (obj as DependencyObject ))
PrintLogicalTree (depth + 1, child );
}

Void PrintVisualTree (int depth, DependencyObject obj)
{
// Print the object and use a leading space to indicate the depth
Debug. WriteLine (new string ('', depth) + obj );
// Recursively call each visible subnode
For (int I = 0; I <VisualTreeHelper. GetChildrenCount (obj); I ++)
PrintVisualTree (depth + 1, VisualTreeHelper. GetChild (obj, I ));
}
}

Although the logic tree can be traversed in the Window constructor, the visible tree will only have nodes after the Window layout is completed at least once, otherwise it will be empty. This is why PrintVisualTree is called in On-ContentRendered, because OnContentRendered is called only after the layout is complete.

Dependency attribute

WPF introduces a new property type called Dependency Property, which is used throughout the WPF platform for style, automatic data binding, and animation.

The dependency attribute depends on multiple providers to determine its value at any time. These providers can be an animation that keeps changing values, or the attribute values of a parent element are slowly passed to the child element. The biggest feature of dependency properties is its built-in ability to transmit change notifications.

The power of adding such intelligence to attributes is the ability to directly enable the rich feature in the declaration mark. The key to the WPF friendly declaration design is that it uses many attributes. For example, the Button control has 96 common attributes. Attributes can be conveniently set in XAML without program code. However, if the Dependency Property does not have additional vertical transmission, it is difficult to obtain the desired result in a simple action such as setting the property without writing additional code.

Implementation of dependency attributes

In fact, the dependency attribute is only a common. NET attribute, but it has been integrated into the WPF architecture. It is fully implemented by WPF APIs. No. NET language can understand dependency attributes by nature.

The following example shows how a Button can effectively implement a dependency attribute called IsDefault:

Public class Button: ButtonBase
{
// Dependency attributes
Public static readonly DependencyProperty IsDefaultProperty;

Static Button ()
{
// Register attributes
Button. IsDefaultProperty = DependencyProperty. Register ("IsDefault", typeof (bool ),
Typeof (Button ),
New FrameworkPropertyMetadata (false, new PropertyChangedCallback (OnIsDefaultChanged )));
...
}

//. NET Property wrapper (optional)
Public bool IsDefault
{
Get {return (bool) GetValue (Button. IsDefaultProperty );}
Set {SetValue (Button. IsDefaultProperty, value );}
}

// Callback for attribute change (optional)
Private static void OnIsDefaultChanged (DependencyObject o, DependencyPropertyChangedEventArgs e)
{...}
}

The IsDefaultProperty static member is a real Dependency Property of the System. Windows. DependencyProperty type. According to the rules, all DependencyProperty members must be public and static, and a Property is used as the suffix. Dependency properties are usually created by calling the DependencyProperty. Register static method. Such a method requires a name (IsDefault), a property type (bool), and a class (Button class) that owns this property ). Using different Register method overloading, you can pass in metadata (metadata) to tell WPF how to handle this attribute, how to handle callback of attribute value change, how to handle forced value conversion, and how to verify the value. The Button will call the reload of Register in its static constructor, assign a default value of false to the Dependency Property, and add a delegate to the change notification.

Finally, the tradition called IsDefault.. NET properties will be called inherited from System. windows. dependencyObject's GetValue and SetValue methods to implement their own accessors, System. -Windows. dependencyObject is the underlying base class, which must be inherited by classes with dependency attributes. GetValue returns the last value set by SetValue. If SetValue has never been called, it is the default value when this property is registered. The IsDefault. NET attribute is not required. The Button user may directly call the GetValue/SetValue method because they are public.

Note: At runtime, the. NET property package is bypassed to set dependency properties in XAML.

Although the XAML compiler relies on this property package during compilation, during runtime, WPF directly calls GetValue and SetValue. Therefore, in order to ensure that the property set using XAML is consistent with the property set using procedural code, the property package should not contain any other logic except GetValue/SetValue calls, this is crucial. To add custom logic, add it to the registered callback function.

On the surface, the code above is like a lengthy way to present simple boolean attributes. However, because GetValue and SetValue use an efficient sparse storage system internally, and IsDefaultProperty is a static member (rather than an instance Member. the implementation of dependency properties saves the memory required for each implementation.

The benefit of dependency attributes is far more than saving memory. It collects a considerable amount of code and standardizes it.

Change Notification

Whenever the value of the dependency attribute changes, WPF automatically triggers a series of actions based on the metadata of the attribute. One of the most interesting features of built-in change notifications is the property trigger, which can execute custom actions when the property value changes without changing any procedural code.

For example, you want to make the Button blue when you move the mouse up. If there is no property trigger, you need to add two Event Handlers for each Button, one for the MouseEvent event and the other for the MouseLeave event.

<Button MouseEnter="Button_MouseEnter" MouseLeave="Button_MouseLeave" 
MinWidth="75" Margin="10">
Help</Button>
<Button MouseEnter="Button_MouseEnter" MouseLeave="Button_MouseLeave"
MinWidth="75" Margin="10">
OK</Button>

The following code implements these two Event Handlers:

void Button_MouseEnter(object sender, MouseEventArgs e)
{
Button b = sender as Button;
if(b != null) b.Foreground = Brushed.Blue;
}

void Mouse_MouseLeave(object sender, MouseEventArgs e)
{
Button b = sender as Button;
if(b != null) b.Foreground = Brushed.Black;
}

However, with an attribute trigger, you can perform the same behavior in XAML:

<Button MinWidth="75" Margin="10">
<Button.Style>
<Style TargetType="{x:Type Button}">
<Style.Triggers>
<Trigger Property="IsMouseOver" Value="True">
<Setter Property="Foreground" Value="Blue" />
</Trigger>
</Style.Triggers>
</Style>
</Button.Style>
OK
</Button>

Property triggers are only one of the three supported by WPF. Data triggers are another form of property triggers, which can work in any. NET attribute (not just dependency attributes ). The event trigger specifies an action by declaring it. This action takes effect when a routing event is triggered.

Property Value inheritance

The term "property value inheritance" does not refer to the traditional object-oriented class inheritance, but refers to the transmission of property values along the element tree from top to bottom.

The inheritance of attribute values is determined by the following two factors:

Support for multiple providers

WPF has many powerful mechanisms to independently set the value of dependency properties. If there is no well-designed mechanism to process these completely different property value providers, the system will become chaotic and the property values will become unstable. Of course, as their names Express, dependency properties are designed to rely on these providers in a consistent and orderly manner.

This shows the five steps. Through this process, WPF runs each dependency attribute and finally calculates its value. The Change Notification embedded in the dependency attribute can automatically occur.

Basic Value Judgment

Most property value providers consider the calculation of basic values. The following list shows eight providers that can set the values of most dependency attributes. The priority order is from high to low:

Local value-> style trigger-> template trigger-> style setting Program-> theme style trigger-> theme style setting Program-> property value inheritance-> Default Value

The technical meaning of the local value is any call to DependencyObject. SetValue, but it usually has a simple attribute value, which is completed using XAML or procedural code.

The default value refers to the initial value used when the dependency property is registered.

Computing

If the value in the first step is an Expression (an object derived from System. Windows. Expression), WPF performs a special algorithm step to convert the Expression to a specific result. In WPF 3.0, expressions only work when dynamic resources or data are used.

Application Animation

If one or more animations are running, they have the ability to change the current attribute value or completely replace the current attribute value.

Restrictions

After all the property value providers process it, WPF will get an attribute value that is almost the final value. If the Dependency Property has registered the CoerceValueCallback, it will pass the property value to the Coerce-ValueCallback delegate. The callback function returns a new value, which is implemented based on the custom logic.

Verify

Finally, if the Dependency Property has registered ValidateValueCallback, the value in the previous restriction will be passed into the ValidateValueCallback delegate. If the input value is valid, the callback function returns true; otherwise, false. If false is returned, an exception is thrown and the entire process is canceled.

If you cannot determine where the Dependency Property obtains the current value, you can obtain the static method DependencyPropertyHelper. GetValueSource as the debugging assistant. This method returns a ValueSource structure, which contains the following data: A BaseValueSource enumerated value, which reflects where the basic value comes from (the first step in the process ); isExpression, IsAnimated, and IsCoerced Boolean attributes, which reflect the information from step 2 to step 4.

Do not use this method in program code. In later versions of WPF, the assumption of value calculation will be broken, and attribute values will be processed in different ways based on its source type, instead of processing it based on the method in the WPF application.

You may need to clear the local value and ask WPF to get the value from the next highest priority provider, and then use this value to set the final attribute value. DependencyObject provides such a mechanism that can be implemented by calling the ClearValue method.

// B is a Button instance.
B. ClearValue (Button. ForegroundProperty );

Button. ForegroundProperty is a DependencyProperty static member. After ClearValue is called, the basic value is recalculated and the local value is deleted from the equation.

Additional attributes

An additional property is a special form of dependency property, which can be effectively added to any object. This may sound strange, but this mechanism has multiple applications in WPF.

Similar to WinForm technology, many WPF Classes define a Tag attribute (type: System. Object) to store custom data for each instance. However, to add custom data to any object derived from DependencyObject, additional attributes are more powerful and flexible. We usually ignore this, that is, we can use additional attributes to efficiently add custom data to sealed class instances.

In addition, you may misinterpret the additional attributes. Although you set them to depend on the SetXXX static method in XAML, you can bypass this method in Procedural Code and directly call DependencyObject -. setValue method. This means that any dependency attribute can be used as an additional attribute in Procedural Code. For example, the following code adds the IsTextSearchEnabled attribute of ListBox to the Button control and assigns the attribute a value:

// Add an irrelevant property to the Button and set its value to true.
OkButton. SetValue (ListBox. IsTextSearchEnabledProperty, true );

Although this does not seem to make any sense, you can use this attribute value in a way that makes sense to the application or component.

Routing event

Just as WPF adds many basic things to the simple. NET attribute concept, it also adds many basic things for. NET events. Routing events are events specially designed for use in the element tree. When a routing event is triggered, it can traverse the visual tree and logic tree up or down and trigger each element in a simple and persistent way without any custom code.

Event Routing allows many programs to ignore the details of the visual tree (which is good for style resetting) and is critical to successful WPF element creation.

In the previous chapter, for the Stop button in the VCR style, a user may directly press the left mouse button on the Rectangle logical child element. Because the event traverses the logic tree, the Button element will still find the event and process it. Therefore, you can embed any complex content in an element (such as a Button) or set a complex visual tree, with the left mouse clicking on any of the internal elements, the Click event of the Button of the parent element is still triggered. If no event is routed, the creator of the internal content or the user of the button has to write code to concatenate the event.

There are many similarities between the implementation and behavior of routing events and dependency attributes.

Routing event implementation

Like dependency attributes, No. NET Language (except XAML) is inherently capable of understanding Route Assignment.

Just as the dependency attribute is composed of a public static DependencyProperty member and an agreed Property suffix name, the routing Event is also composed of a public static RoutedEvent member and an agreed Event suffix name. The registration of routing events is similar to registering the dependency attribute in the static builder. It defines a common.. NET event or an event wrapper. This ensures that you are more familiar with the use of procedural code, and you can add an event handler in XAML using the event feature syntax. Like the property wrapper, the event Wrapper can only call AddHandler and RemoveHandler in the accesser, rather than other events.

Public class Button: ButtonBase
{
// Route events
Public static readonly RoutedEvent ClickEvent;

Static Button ()
{
// Register an event
Button. ClickEvent = EventManager. RegisterRoutedEvent ("Click", RoutingStrategy. Bubble,
Typeof (RoutedEventHandler), typeof (Button ));
...
}

//. NET event wrappers (optional)
Public event RoutedEventHandler Click
{
Add {AddHandler (Button. ClickEvent, value );}
Remove {RemoveHandler (Button. ClickEvent, value );}
}

Protected override void OnMouseLeftButtonDown (MouseButtonEventArgs e)
{
...
// Trigger the event
RaiseEvent (new RoutedEventArgs (Button. ClickEvent, this ));
...
}
...
}

These AddHandler and RemoveHandler methods do not inherit from DependencyObject, but are inherited from System. Windows. UIElement. UIElement is a higher level base class that can be inherited by elements (such as Button elements. These methods can add a delegate to an appropriate routing event or remove a delegate from a routing event. In OnMouseLeftButtonDown, it uses an appropriate RoutedEvent member to call RaiseEvent to trigger the Click event. The current Button instance (this) is passed to the source element of the event ). It is not listed in the code list, but as a response to the KeyDown event, the Click Event of the Button will be triggered, so that the Click action can be completed by the Space key or the Enter key.

Routing policies and event handlers

After the registration is complete, each routing event selects one of the three routing policies. The so-called routing policy is the way the event triggers to traverse the entire element tree. These policies are provided by the RoutingStategy enumeration value.

Tunneling (pipeline transfer)-events are triggered on the root element first, and then transmitted down the tree from each element, until the source element is reached (or until the handler marks the event as handled ).

Bubbling (bubble)-events are triggered on the Source Element first, and then transmitted from each element up along the tree, until the root element is reached (or until the handler marks the event as handled ).

Direct (Direct) ---- events are only triggered on the source element. This is the same as the behavior of common. NET events. Different from this, such events will still participate in some routing event-specific mechanisms, such as event triggers.

The event handler for routing events has a signature, which is consistent with the common. NET event handler pattern match: the first parameter is a System. the Object Name Is sender, and the second parameter (usually named e) is a derived from System. eventArgs class. The sender parameter passed to the event handler is the element added to the handler. Parameter e is an instance of RoutedEventArgs (or derived from RoutedEventArgs). RoutedEventArgs is a subclass of EventArgs and provides four useful attributes:

Source ---- elements that start to trigger this event in the logic tree.

OriginalSource-elements in the visual tree that start triggering the event (for example, TextBlock or the ButtonChrome sub-element of the standard Button element ).

Handled ---- Boolean value. If it is set to true, the event is marked as Handled. This is the tag used to stop Tunneling or Bubbling.

RoutedEvent-a real routing event object (such as Button. ClickEvent). When an event handler is used for multiple routing events at the same time, it can effectively identify the triggered events.

The existence of Source and OriginalSource allows more advanced logic trees or lower-level visual trees. However, this difference is only valid for physical events such as mouse events. For more abstract events, there is no need to establish a direct relationship with an element in the visual tree (just like the Click supported by the keyboard). WPF will pass the same object to Source and OriginalSource.

Routing event implementation

The UIElement class defines many routing events for keyboard, mouse, and pointer input. Most routing events are bubble events, but many events are paired with pipeline events. Pipeline events are easily identified because, by convention, their names all have a Preview prefix, which is immediately triggered before their paired bubble events occur. For example, PreviewMouseMove is a pipe event that is triggered before the MouseMove bubble event.

An event is provided for many different actions to give the element an opportunity to effectively cancel the event or modify the event before the event is about to happen. By convention, the embedded elements of WPF will only take action in response to a bubble event (after the events of bubbles and pipelines are defined, this ensures that the event in the MPs queue can be "previewed ". For example, in the Preview event of the TextBox Control, the entered text is verified to filter out non-conforming text.

Where can I process the Middle-click events?

If you browse all the mouse events provided by the UIElement or ConentElement, you can find the MouseLeftButtonDown, MouseLeftButtonUp, MouseRightButtonDown, and MouseRightButtonUp events. What do you do if there are additional buttons on the mouse?

This information can be obtained through more common MouseDown and MouseUp events. Parameters passed in such an event handler include a MouseButton enumeration value, which indicates the mouse status Left, Right, Midle, XButton1, XButton2, and a MouseButtonState enumeration value, indicates whether the button is Pressed or Released.

Suspending a route event is an illusion

Although setting the Handled attribute of RoutedEventArgs In the event handler to true can terminate pipeline transfer or bubbling, each handler along the tree can still receive these events. This can only be done in the code. At any time, you should avoid handling handled events as much as possible, because the events should be handled immediately.

In short, terminating Management Transfer or bubbling is just a hypothetical phenomenon. More accurately, when a routing event is marked as handled, pipeline transmission and bubbling continue, but by default, the event handler only processes events that have not been processed.

Additional event

By attaching an event, WPF can complete the pipe transfer and bubbling of the routing event through an element that does not define the event.

Additional events are similar to additional attribute operations. Each routing event can be used as an additional event.

Because you need to pass a lot of information to the routing event, you can use the "megahandler" on the upper layer to process each pipe or bubble event. This handler analyzes the RoutedEvent object to determine which event is triggered, converts the RoutedEventArgs parameter to an appropriate subclass, and then continues.

Command

WPF provides built-in command support, which is a more abstract and loosely coupled event version. Although events are associated with a user action, commands represent actions that are separated from the user interface. The most standard command example is Cut and Copy) and Paste (Paste ). Applications can always provide these actions through many synchronous mechanisms: MenuItem in the Menu control, MenuItem in the ContextMenu control, Button in the ToolBar Control, and keyboard shortcut.

Built-in commands

A command is any object that implements the ICommand interface (located in the System. Windows. Input namespace). Each object defines three simple members:

Execute: the logical method for executing a specific command.

CanExecute: If the command is allowed to be executed, true is returned; otherwise, false is returned.

CanExecuteChanged: this event is triggered whenever the value of CanExecute changes.

If you need to create cut, copy, and Paste commands, you can define three classes that implement the ICommand interface and find a place to store these three classes (such as static members in the main window ), call Execute from the relevant event handler (when CanExecute returns true) to process the CanExecuteChanged event and change the IsEnabled attribute in the relevant user interface.

Controls such as Button, CheckBox, and MenuItem have related logic to interact with any command. They have a simple Command attribute (type: ICommand). When the Command attribute is set, the Click event is triggered at any time, these controls automatically call the Execute method of the command (as long as CanExecute returns true ). In addition, they automatically synchronize the IsEnabled value with the CanExecute value, which is implemented through the CanExecuteChanged event. By assigning values to attributes, any logic can be implemented in XAML.

At the same time, WPF has defined a series of commands, so you do not need to implement ICommand objects for the Cut, Copy, and Paste commands, or worry about where to save these commands. WPF has five class static attributes that implement built-in WPF commands:

ApplicationCommands ---- Close, Copy, Cut, Delete, Find, Help, New, Open, Paste, Print, PrintPreview, Properties, Redo, Replace, Save, SaveAs, SelectAll, Stop, Undo, etc..

The other four classes are ComponentCommands, MediaCommands, NavigationCommands, and EditCommands.

Each attribute returns the instance of RoutedUICommand. The RoutedUIElement class not only implements the ICommand interface, but also supports bubbling like a routing event.

Related Article

Contact Us

The content source of this page is from Internet, which doesn't represent Alibaba Cloud's opinion; products and services mentioned on that page don't have any relationship with Alibaba Cloud. If the content of the page makes you feel confusing, please write us an email, we will handle the problem within 5 days after receiving your email.

If you find any instances of plagiarism from the community, please send an email to: info-contact@alibabacloud.com and provide relevant evidence. A staff member will contact you within 5 working days.

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.