0

In my WPF app I dynamically load a XAML drawing from XML at runtime. This drawing is a complex series of nested canvas and geometry 'Path's (for example):

<?xml version="1.0" encoding="utf-8"?>
<Canvas Width="1593" Height="1515">
    <Canvas.Resources />
    <Canvas>
        <Path Fill="…" Data="…"/>
        <Path Fill="…" Data="…"/>
        <Path Fill="…" Data="…"/>
        <Canvas>
            <Canvas>
                <Path Stroke="…" StrokeThickness="…" StrokeMiterLimit="…" StrokeLineJoin="…" StrokeEndLineCap="…" Data="…"/>
                <Path Stroke="…" StrokeThickness="…" StrokeMiterLimit="…" StrokeLineJoin="…" StrokeEndLineCap="…" Data="…"/>
            </Canvas>
        </Canvas>
        <Path Fill="…" Data="…"/>
        <Path Fill="…" Data="…"/>
        <Path Fill="…" Data="…"/>
    </Canvas>
</Canvas>

The outer canvas' Height/Width are incorrectly set, as many of the Path expressions exceeds these dimensions. I don't have any control over this source XML, so I'm required to fix it up at runtime after the drawing is loaded. To load the drawing I use code similar to the following:

public static Canvas LoadDrawing(string xaml)
{
    Canvas drawing = null;
    using (var stringreader = new StringReader(xaml))
    {
        using (var xmlReader = new XmlTextReader(stringreader))
        {
            drawing = (Canvas)XamlReader.Load(xmlReader);
        }
    }
    return drawing;
}

Then, I attempt to reset the canvas size, using the following code:

    var canvas = LoadDrawing(…);
    someContentControOnTheExistingPage.Content = canvas;
    var bounds = VisualTreeHelper.GetDescendantBounds(canvas); // << 'bounds' is empty here.
    canvas.Width = bounds.Width;
    canvas.Height = bounds.Height;

Except, at the point where I create the canvas element, the bounds is empty. However, if I just wire a simple button and invoke GetDescendantBounds() interactively on the same canvas, then I receive expected height/width.

My takeaway is that GetDescendantBounds() does not work unless the layout with the new control has completed. So my questions are:

  1. Is there a way I can force a layout computation prior to running GetDescendantBounds()? Or…
  2. Is there another way I can get the bounds/extents of a visual tree, prior adding it to its parent?

Thanks

-John

JohnKoz
  • 908
  • 11
  • 21

2 Answers2

1

Is there a way I can force a layout computation prior to running GetDescendantBounds?

Yes, call the Arrange and Measure methods of the Canvas:

var canvas = LoadDrawing("...");
someContentControOnTheExistingPage.Content = canvas;
canvas.Arrange(new Rect(someContentControOnTheExistingPage.RenderSize));
canvas.Measure(someContentControOnTheExistingPage.RenderSize);
var bounds = VisualTreeHelper.GetDescendantBounds(canvas);
canvas.Width = bounds.Width;
canvas.Height = bounds.Height;
mm8
  • 163,881
  • 10
  • 57
  • 88
  • You're welcome but please remember to upvote helpful answers: https://stackoverflow.com/help/privileges/vote-up – mm8 May 17 '17 at 09:49
0

First you need to add this line in your xaml string.

xmlns='http://schemas.microsoft.com/winfx/2006/xaml/presentation'

This is C# code example to find control and properties.

 public void LoadXaml()
    {
        string canvasString = @"<Canvas Name='canvas1' xmlns='http://schemas.microsoft.com/winfx/2006/xaml/presentation' Width = '1593' Height = '1515'> </Canvas>";
        var canvas = LoadDrawing(canvasString);

        //Use this line you will find height and width.
        Canvas canvasCtrl = (Canvas)LogicalTreeHelper.FindLogicalNode(canvas, "canvas1");

        // var bounds = VisualTreeHelper.GetDescendantBounds(canvas); // << 'bounds' is empty here.

        canvas.Width = canvasCtrl.Width; //bounds.Width;
        canvas.Height = canvasCtrl.Height; //bounds.Height;
    }
manoj
  • 150
  • 12