Search code examples
c#wpfgraphicsxps

How to get the bounding box of the children of a WPF visual, excluding the parent


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...


Solution

  • 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);
        }
    }