views:

649

answers:

1

Hi,

I am trying to position an Adorner depending on the dimensions of the parent of the adorned element. For example, I have a textbox. I want to adorn this textbox so it looks something like this:

how the adorner needs to be placed

A textbox is placed in a canvas object and if there is enough space available then place the adorner (semi transparent rounded square) in line with the bottom edge of the textbox. The adorner is initiated when the user clicks on the textbox.

Currently the canvas and its contents (the textbox) is hosted in a WinForms form - so the WPF is handled by the ElementHost control.

But when I run my code, when the textbox is clicked for the first time it displays the adorner aligned to the top edge of the textbox (see figure below). After that it positions itself correctly (like the figure above) Does anyone know why this might be?

how adorner is positions

I have pasted the code for this below:

TextBoxAdorner.cs - this the adorner logic

public class TextBoxAdorner : Adorner
{
    private TextBox _adornedElement;
    private VisualCollection _visualChildren;
    private Rectangle _shape;
    private Canvas _container;
    private Canvas _parentCanvas;

    public TextBoxAdorner(UIElement adornedElement, Canvas parentCanvas)
        : base(adornedElement)
    {
        _adornedElement = (TextBox)adornedElement;
        _parentCanvas = parentCanvas;
        _visualChildren = new VisualCollection(this);

        _container = new Canvas();

        _shape = new Rectangle();
        _shape.Width = 100;
        _shape.Height = 80;
        _shape.Fill = Brushes.Blue;
        _shape.Opacity = 0.5;

        _container.Children.Add(_shape);

        _visualChildren.Add(_container);
    }

    protected override Size ArrangeOverride(Size finalSize)
    {
        Point location = GetLocation();
        _container.Arrange(new Rect(location, finalSize));

        return finalSize;
    }

    private Point GetLocation()
    {
        if (_parentCanvas == null)
            return new Point(0, 0);

        Point translate;
        double xloc = 0, yloc = _shape.Height - _adornedElement.ActualHeight;

        if (yloc < 0) // textbox is bigger than the shape
            yloc = 0;
        else
        {
            translate = this.TranslatePoint(new Point(0, -yloc), _parentCanvas);

            // coordinate is beyond the position of the parent canvas
            if (translate.Y < 0)  // this is true the first time it's run
                yloc = 0;
            else
                yloc = -yloc;
        }

        translate = this.TranslatePoint(new Point(_shape.Width, 0), _parentCanvas);

        // textbox is in right edge of the canvas
        if (translate.X > _parentCanvas.ActualWidth) 
        {
            double pos = translate.X - _parentCanvas.ActualWidth;

            translate = this.TranslatePoint(new Point(-pos,0), _parentCanvas);

            if (translate.X < 0)
                xloc = 0;
            else
                xloc = translate.X;
        }

        return new Point(xloc, yloc);
    }

    protected override Size MeasureOverride(Size constraint)
    {
        Size myConstraint = new Size(_shape.Width, _shape.Height);
        _container.Measure(myConstraint);

        return _container.DesiredSize;
    }

    protected override Visual GetVisualChild(int index)
    {
        return _visualChildren[index];
    }

    protected override int VisualChildrenCount
    {
        get
        {
            return _visualChildren.Count;
        }
    }
}
A: 

The position of an Adorner is relative to the adorned element. If you want it to be to the top of your object, the value of yloc should be negative. However, the code you have also regards the boundaries of the Canvas. If there's not enough place for the rectangle above, it would put it below. Trying placing the TextBox a little lower in the Canvas.

Aelij Arbel