Introduction

This article explains a technique that I have used to define WPFadorners in XAML. The technique requires two custom classes.

These classes are used to define:

the control to be adorned; and

the UI elements that make up the adorner

The main class is AdornedControl which is derived from ContentControl. The content of AdornedControl is displayed in the normal way in the visual tree of the UI. The adorner is defined using the AdornerContent property of AdornedControl. The content of this property, in XAML, defines the UI elements that will be placed in the adorner that is attached to the AdornedControl.

I have included two sample projects to accompany this article. The simple sample shows a very simple use of AdornedControl that is described in this article. The advanced sample shows a more advanced usage that meets the needs described in the Background section below.

Assumed Knowledge

It is assumed that you already know C# and have a basic knowledge of using WPF and XAML. Knowledge of the WPF visual and logical tree will also be helpful.

Background

Recently, I found that I was in need of a way to display auxiliary controls when the user hovers the mouse over a particular control.

The particular control is a flow-chart node embedded in a Canvas that looks like this:

The purpose of the auxiliary controls was for the user to be able to drag the node around the Canvas and also to be able to remove the node from the Canvas.

This is what the node looks like with the auxiliary controls displayed:

Normally, the auxiliary controls will not be displayed. They will only be visible when the mouse has moved over the node. They will remain visible until the mouse has moved away from the node and a period of time has elapsed. I decided that adorners would fit this situation well because they are defined outside of the node's visual tree and therefore will not interfere with the normal layout and visuals of the node.

The usual way of creating adorners and adding them to the adorner layer is accomplished through procedural code. What I really wanted was to design both the adorned controls and their adorners in XAML. I created two custom classes that work together to allow this: AdornedControl and FrameworkElementAdorner.

But first let's review the usual way of working with adorners.

Using Adorners - The Usual Way

The normal method of using adorners is procedural. You need to create a class that derives from Adorner. In the derived class, you either have some custom rendering code or you attach UI elements and visuals as children to the adorner. Next, you create an instance of your derived adorner class. The UI element that is to be adorned is passed into the constructor of the adorner. Lastly, the adorner is added to the adorner layer. All this is achieved procedurally in C# code.

When you are ready to display the adorner and allow the user to interact with it must be added to the adorner layer. AdornerLayer.GetAdornerLayer is called and typically passed a reference to the UI element that is to be adorned. GetAdornerLayer searches back up the visual tree for an appropriate adorner layer to use.

The alternative to writing custom rendering code is to add children to the visual tree of the adorner. This means that any class derived from Visual can be instanced and added to the visual tree underneath the adorner. I have used this technique in FrameworkElementAdorner.

This has only been a brief discussion about using adorners procedurally. The main purpose of my article is to discuss how to use adorners defined in XAML.

Defining Adorners in XAML using AdornedControl

There are many advantages to defining the UI in XAML. Not only is it simpler than writing the equivalent procedural code, but it's also safer and less prone to errors. Unfortunately, as shown above, adorners are usually created procedurally and cannot, by default, be defined in XAML. This seems like an unnecessary omission from WPF and so I implemented a class that allows it.

AdornedControl allows adorners to be defined in XAML. It defines both the adorner and the control that is to be adorned.

IsAdornerVisible can also be set procedurally to show or hide the adorner. Alternatively the Show() and Hide() functions and Show and Hide commands can be used. In the advanced sample, I use animation to fade the adorner in and out when it is shown and hidden.

Finally, the other custom class which I haven't yet mentioned is FrameworkElementAdorner. This class is used internally by AdornedControl. It derives from Adorner and references a FrameworkElement as its child in the visual tree. This is what allows us to add any FrameworkElement into the visual tree of the adorner.

FrameworkElementAdorner is based on UIElementAdorner by Josh Smith which can be found here. I adapted it to work with FrameworkElement and made a few modifications that I needed. The code for FrameworkElementAdorner is a good example of how to create an adorner that has visual children.

Conclusion

This example has explained how to use AdornedControl to define, in XAML, an adorned control and its adorner.

History

7th February, 2010: Article updated

The SizeChanged of the adorned element is now monitored in order to update the placement of the adorner

Fixed issue with placement of the adorner on the outside top & left of the adorned element

27th February, 2010: Code updated

The update is simply to remove the copyright information from Assembly.cs that was automatically added when the project was generated. I tested that the code still compiles and runs.

18th June, 2010: Article updated

The 'Focusable' property for AdornedControl is now set to 'false' by default.

Added a new improved sample project. Functionally the 'improved sample' is the same as the 'advanced sample', however the animation code to fade in and fade out the adorner is now integrated into the AdornedControl class itself which makes it trivial to move this functionality to new projects. The new methods 'FadeInAdorner' and 'FadeOutAdorner' can be called to fade the adorner in and out. Alternately the 'FadeIn' and 'FadeOut' commands can trigger this behaviour. The new property 'IsMouseOverShowEnabled' can be set to 'true' to automatically fade in and show the adorner when the mouse cursor is hovered over the adorned control.

25th September, 2010: Article updated

The animation state of the adorner is now set when showing/hiding the adorner by setting the IsAdornerVisible property (thanks to Tri Q Tran for pointing this out)

Made some changes to make sure the animation state of the adorner is always correct

Opacity is now animated on the adorner rather than the adorner content

11th October, 2010: Article updated

Added a new dependency property to AdornedControl. AdornedTemplatePartName is used to specify the part name of an element in the visual-tree that is to be adorned. By default, the property is set to null which causes the AdornedControl itself to be the UI element that is adorned (the original behaviour). When the property is set to a valid part name, AdornedControl searches the visual-tree below itself to find the named UI element that is to be adorned. For example, when AdornerContent is an editable ComboBox setting AdornedTemplatePartName to PART_EditableTextBox will cause the TextBox part of the ComboBox to be adorned. This feature was requested by Richard Deeming.

15th March, 2011: Article updated

Fixed an issue in ImprovedAdornedControlSample. This issue was reported by Member 2477019 (see article messages for details) and the fix was proposed by Louis-Philippe Lauzier.

I removed the code in HideAdornerInternal that was setting IsAdornerVisible to false. This line of code appears to be interfering with data-binding, and after analysis, this line of code appears totally unnecessary, so removing it should cause no issues.

This is such a great project, can't tell you how much I have learned from it.

My question: When the drag handler is clicked, an overlay appears (with four arrowheads pointing up/down/right/left and a dot in the center). I have carefully dissected the XAML code to bits and have understood everything, but cannot locate how this overlay is generated.

Thanks to author for so handy control! Working in XAML is better place to put such things.
One improvement could be to hide adorner by timeout (when no mouse is involved). I used adorner to show some hint to user and all I can do is to show adorner, wait by timer and close adorner. Can you do it automatically? Say, some method ShowAutoHide(double timeoutInSeconds);

If I set IsAdornerVisible to true in the XAML (and hide it programatically), the adorned shows up with the text from the binding. If I however set IsAdornerVisible to false (which I'd prefer, makes most sense to me) I get a data binding error at runtime and no text in the TextBlock.

You don't handle the scenario where the control could get Unloaded() by WPF. For example, a typical usage of an adorner is to display a progress bar. Suppose the progress bar is keeping track of some work done in a tab. The currently loaded TabItem gets unloaded whenever TabControl.OnSelectionChanged() gets called, so that WPF can load the newly selected TabItem. If the user then clicks back to the old TabItem, the adorner will disappear, even if the background worker thread is still doing its job and the ViewModel's IsInProgress property is true.

When I Snoop your control and Delve the AdornerContent property, I can see the StackPanel I have and its IsLoaded property is false.

This could also be why some people in the comments are reporting e.OldValue can be null, and potentially be a memory leak. Have not verified there is a memory leak, but any time Unloaded is not handled properly, there is a good chance for one!

Why is the AdornerContent DataContext set to the AdornedControl DataContext every time the context is changed? In my particular case, I needed the two to have different DataContexts. It was an easy rig, I was just wondering what the design decision behind this was.

I think you are actually wrong about your comment in the code about OldValue always being null. This isn't the case as you'll find out if you change AdornedControl.AdornerContent to reference other content then you will find that OldValue is non null and that the event is then unsubscribed.

As for memory leak, I don't really understand why it would be a memory leak. If you discard the reference to the AdornedControl and it's content .Net should easily be able to resolve the circular reference between them via the event. Otherwise if you are holding onto the AdornedControl and want to discard the content you need to set the AdornerContent property to null to make the event be unsubscribed.

I'm afraid you are going to have to give me some more context about how you are using the control before I accept that there is any kind of general memory leak.

All I can think of at the moment is that you might want to make use of AdornerDecorator in your visual tree. I thought of this because it sounds similar to a problem raised in an eariler comment by Jeremy Hutchinson. You should read those comments and see if it helps you.

After further research, i think that it is a "cliptobound" problem. But for the moment i can't acheive to solve the problem.

Aurore

-- 19/12/2011
In the visualtreeview, i've seen that the listbox is rendered through a Scrollviewer, and the content of the ScrollViewer is rendered by a ScrollContentPresenter instance. ScrollContentPresenter has its own adorner layer. Well, i do not want using this one, i'd like to find the window's adornerlayer instead.

Adding multiple adorners to a single AdornedControl isn't possible with the current code for this article. You could probably add that as a feature but this isn't something that I have tried to implement.

What you could do and something that I have tried is to nest multiple AdornedControls within one another, although this is an inelegant way of achieving what you want.