2

From the MSDN documentation for VisualTreeHelper.GetDescendantBounds():

// Return the bounding rectangle of the parent visual object and all of its descendants.
Rect rectBounds = VisualTreeHelper.GetDescendantBounds(parentVisual);

I get this and it works, but I do not want to include the parent's bounds, the reason is that my parent is a page of an XPS document, and so calling this just returns the page boundaries, which is not what I want. I want the bounding box of everything on the page, i.e. just of the children of the page visual.

// snippet of my code
Visual visual = paginator.GetPage(0).Visual;
Rect contentBounds = VisualTreeHelper.GetDescendantBounds(visual);
// above call returns the page boundaries
// is there a way to get the bounding box of just the children of the page?

The reason I need this is that I'm saving the XPS page to a bitmap and need to include as little white space as possible, to limit the bitmap to only the 'used' area of the page.

Do I need to iterate over all the children of the visual myself and call VisualTreeHelper.GetContentBounds() on each one? I thought there would be a better way than doing this...

dodgy_coder
  • 12,407
  • 10
  • 54
  • 67

1 Answers1

2

I've come up with a workable solution by enumerating over all the child visuals of the parent (page) visual. A more efficient and/or library solution would be better, but this works for now.

// enumerate all the child visuals
List<Visual> children = new List<Visual>();
EnumVisual(visual, children);

// loop over each child and call GetContentBounds() on each one
Rect? contentBounds = null;
foreach (Visual child in children)
{
    Rect childBounds = VisualTreeHelper.GetContentBounds(child);
    if (childBounds != Rect.Empty)
    {
        if (contentBounds.HasValue)
        {
            contentBounds.Value.Union(childBounds);
        }
        else
        {
            contentBounds = childBounds;
        }
    }
}

/// <summary>
/// Enumerate all the descendants (children) of a visual object.
/// </summary>
/// <param name="parent">Starting visual (parent).</param>
/// <param name="collection">Collection, into which is placed all of the descendant visuals.</param>
public static void EnumVisual(Visual parent, List<Visual> collection)
{
    for (int i = 0; i < VisualTreeHelper.GetChildrenCount(parent); i++)
    {
        // Get the child visual at specified index value.
        Visual childVisual = (Visual)VisualTreeHelper.GetChild(parent, i);

        // Add the child visual object to the collection.
        collection.Add(childVisual);

        // Recursively enumerate children of the child visual object.
        EnumVisual(childVisual, collection);
    }
}           
dodgy_coder
  • 12,407
  • 10
  • 54
  • 67
  • You could try cracking open `VisualTreeHelper.GetContentBounds` in ILSpy (or the equivalent) and see what spills out. Also, the definition you provided in the OP for `VisualTreeHelper.GetContentBounds` makes me think you have duplicate recursion here. I don't think you don't need to make `EnumVisual` recursive, because `GetContentBounds` might already do this. – Merlyn Morgan-Graham Sep 06 '11 at 03:34
  • Thanks for the idea regarding reverse engineering. With the GetContentBounds, I initially thought that myself, but found out it only returns the 'immediate' children, and not the 'grandchildren' (children of children, etc.) ... more info [here](http://stackoverflow.com/questions/874380/how-do-i-loop-through-all-the-controls-in-a-window-in-wpf) – dodgy_coder Sep 06 '11 at 03:52
  • That article only talks about enumeration, but not bounds. I was hoping the bounds would naturally propagate up, as I would think the definition of a bound would implicitly be recursive. If this were true, when a grandchild is added outside the current bounds, the bounds would naturally expand, propagating up the tree. If this is the case, you could simply union the direct children of the current element to get the bounds of all the descendants, *excluding* the current element. Maybe an experiment is in order :) – Merlyn Morgan-Graham Sep 06 '11 at 03:57
  • Sorry I was getting a bit confused, I meant to say that VisualTreeHelper.GetChildrenCount() only returns the 'immediate' children, and not the 'grandchildren'. The other problem with GetContentBounds() is that it returns 'Rect.Empty' for container type visuals (like canvas), which is sort of useless for my visual tree case - a page containing a canvas, containing the children. So I think I just have to iterate over all the children, checking for Rect.Empty. – dodgy_coder Sep 06 '11 at 04:03
  • There goes my next suggestion ;) – Merlyn Morgan-Graham Sep 06 '11 at 04:06