Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

WPF: How to get the true size (Bounding Box) of shapes

Tags:

c#

.net

wpf

I'm having trouble with getting the actual size (bounding box) of Shapes.

I tried to use both RenderSize and ActualSize but they return values that don't make sense. However, using these methods for UIElements works great.

if you can help me with this I will be greateful.

like image 330
Horse Pen Avatar asked Feb 24 '11 12:02

Horse Pen


2 Answers

You can get the Bounding Box for any Visual using TransformToVisual

So if you have a Polygon defined like this

<Canvas Name="canvas">
    <Polygon Name="polygon" Canvas.Left="12" Canvas.Top="12"
             Points="0,75 100,0 100,150 0,75"
             Stroke="Purple"
             Fill="#9999ff"
             StrokeThickness="1"/>
</Canvas>

Then you can add a Border around its Bounding Box with the following code

private void AddBorderForFrameworkElement(FrameworkElement element)
{
    Rect bounds = element.TransformToVisual(canvas).TransformBounds(new Rect(element.RenderSize));
    Border boundingBox = new Border { BorderBrush = Brushes.Red, BorderThickness = new Thickness(1) };
    Canvas.SetLeft(boundingBox, bounds.Left);
    Canvas.SetTop(boundingBox, bounds.Top);
    boundingBox.Width = bounds.Width;
    boundingBox.Height = bounds.Height;
    canvas.Children.Add(boundingBox);            
}

However, you may not always get the desired results using this since the Bounding Box will not always be the Bounds for what is actually drawn. If you instead define your Polygon like below where you start to draw where x=100 then the Bounding Box will be much larger then what is drawn

<Polygon Name="polygon" Canvas.Left="140" Canvas.Top="12"
         Points="100,75 200,0 200,150 100,75"
         Stroke="Purple"
         Fill="#9999ff"
         StrokeThickness="1"/>

enter image description here
Bounding Box comparison

like image 135
Fredrik Hedblad Avatar answered Sep 24 '22 16:09

Fredrik Hedblad


I've run into this problem too and found that a good way to get the exact bounding box for shapes, one that includes the stroke too if there's any, and works for virtually any path I've thrown at it, is the VisualContentBounds property from the WPF Visual class.

The problem with this is that it's internal (found it with Reflector), so you can only use it for built-in WPF shapes (since you can't override it outside the assembly) and you need to get it through reflection:

    Rect visualContentBounds = (Rect)GetPrivatePropertyValue(myShape, "VisualContentBounds");

    /*...*/

    private static object GetPrivatePropertyValue(object obj, string propName)
    {
        if (obj == null)
            throw new ArgumentNullException("obj");

        Type t = obj.GetType();
        PropertyInfo pi = t.GetProperty(propName, BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Instance);

        if (pi == null)
            throw new ArgumentOutOfRangeException("propName", string.Format("Field {0} was not found in Type {1}", propName, obj.GetType().FullName));

        return pi.GetValue(obj, null);
    }
like image 32
Gabi Ghita Avatar answered Sep 25 '22 16:09

Gabi Ghita