Skip to content
Cole Campbell edited this page Mar 3, 2018 · 2 revisions

Ultraviolet Framework 1.3 added support for adorners, which are a special type of element that visually decorate other elements.

Creating an Adorner

New adorners must inherit from the Adorner abstract class found in the Ultraviolet.Presentation.Documents namespace.

For the most part, adorners are defined like any other UPF element, as they ultimately derive from the FrameworkElement class. The example below is an adorner which draws four colored boxes at the corners of the adorned element.

public class ExampleBoxesAdorner : Adorner
{
    const Double FullBoxSize = 16;
    const Double HalfBoxSize = FullBoxSize / 2;

    public ExampleBoxesAdorner(UIElement adornedElement)
        : base(adornedElement)
    {

    }

    protected override void OnDrawing(UltravioletTime time, DrawingContext dc)
    {
        DrawBlank(dc, new RectangleD(-HalfBoxSize, -HalfBoxSize, FullBoxSize, FullBoxSize), Color.Red);
        DrawBlank(dc, new RectangleD(AdornedElement.RenderSize.Width - HalfBoxSize, -HalfBoxSize, FullBoxSize, FullBoxSize), Color.Lime);
        DrawBlank(dc, new RectangleD(-8, AdornedElement.RenderSize.Height - HalfBoxSize, FullBoxSize, FullBoxSize), Color.Blue);
        DrawBlank(dc, new RectangleD(AdornedElement.RenderSize.Width - HalfBoxSize, AdornedElement.RenderSize.Height - HalfBoxSize, FullBoxSize, FullBoxSize), Color.Magenta);

        base.OnDrawing(time, dc);
    }

    protected override RectangleD CalculateVisualBounds()
    {
        var bounds = AdornedElement.VisualBounds;
        RectangleD.Inflate(ref bounds, HalfBoxSize, HalfBoxSize, out bounds);
        return bounds;
    }
}

The AdornedElement property provides a reference to the element being adorned by this adorner instance. Here, we use it to calculate the positioning of our four boxes so that they are centered on the corners of the adorned element's visual bounding box.

Also, take note of the CalculateVisualBounds() method. Our example adorner renders outside of the usual bounds of the adorned element, so we need to inform UPF of this fact in order to avoid being improperly clipped. If the adorner does not render outside of the adorned element's visual bounds, it is not necessary to override this method.

Adorning an Element

Adorners are added programatically by attaching instances of Adorner to adorner layers.

Adorner layers are used to specify where adorners appear to reside within the visual tree; adorners associated with a particular layer will be rendered above any elements below that layer in the tree. Every view has an adorner layer at its root, and additional adorner layers can be added to a view by wrapping an element with an instance of the AdornerDecorator container element.

<AdornerDecorator>
    <Grid>
        <!-- etc -->
    </Grid>
</AdornerDecorator>

The static GetAdornerLayer() method defined on the AdornerLayer class will walk up the visual tree starting at the specified element and return the first adorner layer that it finds. Once you have that, you can use the Add() method to add an adorner to the layer.

// Given that "btn" is a reference to an element...
var adornerLayer = AdornerLayer.GetAdornerLayer(btn);
adornerLayer.Add(new ExampleBoxesAdorner(btn));
Clone this wiki locally