ArtfulBits
COMPANY SERVICES PRACTICES TECHNOLOGIES CAREER CONTACTS Home Contact us Site map
OVERVIEW
WPF
Adorners in WPF

Downloads

Project archive #1
Project archive #2
Project archive #3
Project archive #4


What is an Adorner?

In WPF, an Adorner is special FrameworkElement that can be bounded to UIElement to allow user manipulate that element. Under manipulating we mean:

  • Adding functional handles to a UIElement that enable user to manipulate the element in some way (resize, rotate, reposition, etc.).
  • Provide visual feedback to indicate various states, or in response to various events.
  • Overlay visual decorations on a UIElement.
  • Visually mask or override part or whole UIElement.

WPF does not provide concrete adorners but it does provide the basic infrastructure. That means that you need to write your own special Adorner class.
A base infrastructure consists of next classes:

  • Adorner
    This is a base Adorner from which you will need to subclass.
  • AdornerLayer
    The AdornerLayer can be considered as a plane in which the Adorners are drawn.
  • AdornerDecorator
    Defines the location in visual tree of an AdornerLayer. Window class already has it in the visual tree. It adds the default adorner layer above all other controls in the window.

Adorning a Single UIElement

To bind an adorner to a particular UIElement, follow these steps:

  • Call the static method GetAdornerLayer of the AdornerLayer class to get an AdornerLayer object for the UIElement to be adorned. GetAdornerLayer walks up the visual tree, starting at the specified UIElement, and returns the first adorner layer it found. (If no adorner layers are found, the method returns null.)
  • Create new instance of the adorner you need and pass it the instance of the UIElement you need to adorn.
  • Call the Add method of the AdornerLayer, and pass it your new-ly created adorner to bind it to the target UIElement.

The following example binds  SomeAdorner (our own Adorner) to a TextBox named myTextBox.


myAdornerLayer = AdornerLayer.GetAdornerLayer(myTextBox); myAdornerLayer.Add(new SomeAdorner(myTextBox));

Adorning the Children of a Panel

To bind an adorner to the children of a Panel, follow these steps:

1. Call the static method GetAdornerLayer to find an adorner layer for the element children of which are to be adorned.
2. Enumerate the children of the parent element and call the Add method to bind an adorner to each child element.

The following example binds a SomeAdorner (our own Adorne) to the children of a StackPanel named myStackPanel.


foreach (UIElement toAdorn in myStackPanel.Children) myAdornerLayer.Add(new SomeAdorner(toAdorn));

Rendering

Adorners are rendered in an AdornerLayer, which is a rendering surface that is always on top of the adorned element or a collection of adorned elements. Rendering of adorner is independent of rendering  UIElement the adorner is bound to. An adorner is typically positioned relatively to the element to which it is bound, using the standard 2-D coordinate origin located at the upper-left of the adorned element. Anything placed in the adorner layer is rendered on top of the rest visual elements. In other words, adorners are always visually on top and cannot be overridden using z-order.

It is important to note that adorners do not include any inherent rendering behavior (by default Adorner class does not render anything), ensuring that an adorner's rendering is the responsibility of the adorner's implementer.

This means that the adorner's rendering should be implemented by:

  1. Overriding the OnRenderSizeChanged method and drawing adorner's visual content using the DrawingContext object methods.
  2. Providing a visual children collection. (In this case you should override GetVisualChild method and VisualChildrenCount property)

The following examples accordingly show these approaches.

The first example adorner simply adorns the corners of a UIElement with circles.

The following image shows the SimpleCircleAdorner applied to a TextBox.

Sources of example above.

Second approach:

An example above is simplified. Complete version is here.

Purpose of the AdornerDecorator

As I mentioned above, there is another class that plays an important role in rendering, called AdornerDecorator.
AdornerDecorator determines a placement of AdornerLayer in the visual tree. That divides a visual tree in two parallel branches one of which is the child of the AdornerDecorator with it's visual subtree, and the other one is AdornerLayer.

For better understanding we will consider the next example. Let us create a new project.
First of all lets implement our own Adorner class.

As you have already seen we used HyperContol class as a child of our Adorner. This class is also used to represent Adorner.
Here its implementation:

After above implementations we must create generic.xaml resource file where ControlTemplate and Style for HyperControl should be created.

Create GUI of our project in Window1.xaml.

Implement code-behind file.

Run that project and start dragging TextBlock with Text property set to "Test". You can see that adorner can be draged above the entire Window.

You can get that project here.

After that lets change Window1.xaml by adding AdornerDecorator in the Grid. And sets its ClipToBounds property to true:

Run that, and you will notice that adorner can be dragged only inside the grid element.

Source files of the project can be found here.

So, I hope that after that experience you will understand the purpose of AdornerDecorator class.

Note: If you need to switch adorner layer and adorned element in z-order, you should derrive from AdornerDecorator class and override GetVisualChild method (it's default implementation can be discovered using the awesome tool by Lutz Roeder's, named Reflector).

Events and Hit Testing

Adorners receive input events just like any other FrameworkElement. As the adorner always has a higher z-order than the element it adorns, the adorner receives input events that may be intended for the underlying adorned element. An adorner can listen for certain input events and pass these on to the underlying adorned element by re-raising the event.

To enable pass-through hit testing of elements under an adorner, set the hit test IsHitTestVisible property to false on the adorner.


« Previous | Home | Next »

 

Author: 2007 Dima Zaharov, Department Manager/ArtfulBits

Company | Services | Practices | Technologies | Career | Contacts
© 2005-2008 ArtfulBits. All rights reserved.