WPF Series: Creating Generic Adorner

by XAML Team | Comments 4

As I have mentioned at the end of the previous post today I will show you how to create a generic adorner, which will allow you to decorate a single element  multiple times at different positions. At the end we will be able to create something that looks like this:

TopicWithAdorners

To achieve this we will need a way to say that we want to position the adorner at the different corners of the element as well as to specify whether we want to be inside or outside (vertically or horizontally) of the element. Let's create some enumeration types that will serve for this purpose. Introducing AdornerPosition and AdornerPlacement enums.

AdornerPosition

public enum AdornerPosition

{

    Unspecified = 0,

    TopLeft = 1,

    TopCenter = 2,

    TopRight = 3,

    MiddleLeft = 4,

    MiddleCenter = 5,

    MiddleRight = 6,

    BottomLeft = 7,

    BottomCenter = 8,

    BottomRight = 9

}

AdornerPlacement

public enum AdornerPlacement

{

    Unspecified = 0,

    In = 1,

    OutHorizontally = 2,

    OutVertically = 3

}

Each of the enums' values speak for it self, but for clarity here is a diagram that illustrates them:

  AdornerPlacementAndPosition

The final piece is to implement the ArrangeOverride() method of the Adorner class. It looks like this:

protected override Size ArrangeOverride( Size finalSize )

{

    if ( adorningElement != null )

    {

        Point adorningPoint = new Point();

        int pos = (int) position - 1;

 

        Rect adorneredElementRect = VisualTreeHelper.GetContentBounds( AdornedElement );

        Rect adorningElementRect = VisualTreeHelper.GetContentBounds( adorningElement );

 

        adorningPoint.X = ( ( ( pos % 3 ) / 2d ) * adorneredElementRect.Width )

                          - ( ( ( pos % 3 ) / 2d ) * adorningElementRect.Width );

        adorningPoint.Y = ( ( ( pos / 3 ) / 2d ) * adorneredElementRect.Height )

                          - ( ( ( pos / 3 ) / 2d ) * adorningElementRect.Height );

        if ( placement == AdornerPlacement.OutHorizontally )

        {

            switch ( pos % 3 )

            {

                default :

                case 1 :

                {

                    //center column do nothing

                    break;

                }

                case 0 :

                {

                    adorningPoint.X -= adorningElementRect.Width;

                    break;

                }

                case 2 :

                {

                    adorningPoint.X += adorningElementRect.Width;

                    break;

                }

            }

        }

        else if ( placement == AdornerPlacement.OutVertically )

        {

            switch ( pos / 3 )

            {

                default :

                case 1 :

                {

                    //center row do nothing

                    break;

                }

                case 0 :

                {

                    adorningPoint.Y -= adorningElementRect.Height;

                    break;

                }

                case 2 :

                {

                    adorningPoint.Y += adorningElementRect.Height;

                    break;

                }

            }

        }

 

        adorningElement.Arrange( new Rect( adorningPoint, adorningElement.DesiredSize ) );

    }

    return finalSize;

}

That's it. You can now adorn your elements passing different positions and placements. Sample project can be downloaded from here.

Have fun adorning your elements.

4 Comments

Vaibhav
hey it's just good one
I need one help i am trying to apply adorner to line control in wpf c#
I have Created a class LineResizingAdorner in which i have taken two thumbs Right Center and Left Center
as i have to resize the line by moving this two thumbs,but i am facing some problems in increasing the length of the line as X and Y coordinate of the line changes ,
So can you help me out to apply the adorner to line control with respective to x1,y1,x2,y2 so that it should work perfectly

Waiting for your reply 

Regards 
Vaibhav Jagtap


Ciantic
For reason unknown this doesn't work for all elements. E.g. button or textbox.

I tried like this:

            Ellipse el = new Ellipse { Width = 30, Height = 30, Fill = Brushes.Red };
            Ellipse el2 = new Ellipse { Width = 30, Height = 30, Fill = Brushes.Red };
            Ellipse el3 = new Ellipse { Width = 30, Height = 30, Fill = Brushes.Red };
            AdornerLayer.GetAdornerLayer(textBox1).Add(new GenericAdorner(
                textBox1,
                el,
                AdornerPosition.TopLeft,
                AdornerPlacement.In));
            AdornerLayer.GetAdornerLayer(buttonSample).Add(new GenericAdorner(
                buttonSample,
                el2,
                AdornerPosition.TopLeft,
                AdornerPlacement.In));
            AdornerLayer.GetAdornerLayer(rectangle).Add(new GenericAdorner(
                rectangle,
                el3,
                AdornerPosition.TopLeft,
                AdornerPlacement.In));

It doesn't work for buttonSample or textBox1. Huh?
Winnie
Can I apply this to Silvelright?

Comments

  1.    
      
      
       
  2. (optional, emails won't be shown on public pages)
  3. (optional)
Read more articles by XAML Team - or - read latest articles in Developer Tools