سؤال

I want a control's bounds to resize to JUST fit all of its dynamic content, which changes every frame. In other words, if I show a bounding box on the control via any means (canvas, border, etc.), that box should contain all of the visible content, where the top left corner of the visible content is the top left corner of the box. Everything should be JUST contained within the box.

The contents are special in that they change every frame, and are drawn all over the place based on parameters from an external source. I have the current height and width of the control set to 'auto' - which works well in ensuring that all content is displayed - but unfortunately the left-top corner value of the control will NOT correspond to the left-top of the all the visible content.

Is there an easy way to make the control's bounds change to contain all its content? As I said above, if I showed a bounding box, that box should JUST contain all of the visible content.

هل كانت مفيدة؟

المحلول

You don't say how you position the child elements of the control but the child element are relative to the control so you need the control to shrink its blank left and top regions and then move the control itself so that everything stays where you put it. One way to do this is to write a custom panel similar to a Canvas.

Here is a prototype BoundingCanvas that tries to bound its position and size to the bounds of its children:

public class BoundingCanvas : Panel
{
    public BoundingCanvas()
    {
        HorizontalAlignment = HorizontalAlignment.Left;
        VerticalAlignment = VerticalAlignment.Top;
    }

    private Vector minTopLeft;

    protected override Size MeasureOverride(Size availableSize)
    {
        var resultSize = new Size();
        minTopLeft = new Vector(double.PositiveInfinity, double.PositiveInfinity);
        var unlimitedSize = new Size(double.PositiveInfinity, double.PositiveInfinity);
        foreach (UIElement child in Children)
        {
            child.Measure(unlimitedSize);
            var topLeft = GetTopLeft(child);
            minTopLeft.X = Math.Min(minTopLeft.X, topLeft.X);
            minTopLeft.Y = Math.Min(minTopLeft.Y, topLeft.Y);
            resultSize.Width = Math.Max(resultSize.Width, topLeft.X + child.DesiredSize.Width);
            resultSize.Height = Math.Max(resultSize.Height, topLeft.Y + child.DesiredSize.Height);
        }
        resultSize.Width -= minTopLeft.X;
        resultSize.Height -= minTopLeft.Y;
        return resultSize;
    }

    protected override Size ArrangeOverride(Size finalSize)
    {
        Margin = new Thickness(minTopLeft.X, minTopLeft.Y, 0, 0);
        foreach (UIElement child in Children)
            child.Arrange(new Rect(GetTopLeft(child) - minTopLeft, child.DesiredSize));
        return finalSize;
    }

    private Point GetTopLeft(UIElement element)
    {
        return new Point(Canvas.GetLeft(element), Canvas.GetTop(element));
    }
}

and you can use it like this:

<Grid>
    <local:BoundingCanvas Background="Pink">
        <Rectangle Canvas.Top="10" Canvas.Left="10" Width="10" Height="10" Fill="DarkRed"/>
        <Rectangle Canvas.Top="20" Canvas.Left="20" Width="10" Height="10" Fill="DarkRed"/>
    </local:BoundingCanvas>
</Grid>

The prototype needs more error checking and special case handling but it demonstrates the concept. In this example, the panel compensates for the adjusted size with an extra margin but you could also manipulate Canvas.Top and Canvas.Left if the intended parent was a Canvas.

مرخصة بموجب: CC-BY-SA مع الإسناد
لا تنتمي إلى StackOverflow
scroll top