WPF: How to make canvas auto-resize?

Viewed 92407

I would like my Canvas to automatically resize to the size of its items, so that the ScrollViewer scroll bars have the correct range. Can this be done in XAML?

<ScrollViewer HorizontalScrollBarVisibility="Auto" x:Name="_scrollViewer">
    <Grid x:Name ="_canvasGrid" Background="Yellow">
        <Canvas x:Name="_canvas" HorizontalAlignment="Left" VerticalAlignment="Top" Background="Green"></Canvas>
        <Line IsHitTestVisible="False" .../>
    </Grid>
</ScrollViewer>

In the above code the canvas always has size 0, though it doesn't clip its children.

14 Answers

No this is not possible (see snippet from MSDN below). However, if you want to have scrollbars and auto-resizing, consider using a Grid instead, and use the Margin property to position your items on this Grid.. Grid will tell the ScrollViewer how big he wants to be, and you will get the scrollbars.. Canvas will always tells the ScrollViewer he doesn't need any size.. :)

Grid lets you enjoy both worlds - As long as you're putting all elements into a single cell, you get both: Arbitrary positioning and auto-sizing. In general it is good to remember that most panel controls (DockPanel, StackPanel, etc) can be implemented via a Grid control.

From MSDN:

Canvas is the only panel element that has no inherent layout characteristics. A Canvas has default Height and Width properties of zero, unless it is the child of an element that automatically sizes its child elements. Child elements of a Canvas are never resized, they are just positioned at their designated coordinates. This provides flexibility for situations in which inherent sizing constraints or alignment are not needed or wanted. For cases in which you want child content to be automatically resized and aligned, it is usually best to use a Grid element.

Hope this helps

I was able to achieve the result you are looking for by simply adding a new size changed event to the control which contained the data that was causing the canvas to grow. After the canvas reaches the extent of the scroll viewer it will cause the scroll bars to appear. I just assigned the following lambda expression to the size changed event of the control:

text2.SizeChanged += (s, e) => { DrawingCanvas.Height = e.NewSize.Height; 
                                 DrawingCanvas.Width = e.NewSize.Width; };

What worked for me is the following: Like the original poster's example in their question, I nested a canvas in a grid. The grid is within a scrollviewer. Instead of attempting to change the canvas size, I changed the grid size, both height and width in my case, and the canvas followed the size of the grid minus any margins. I set the grid size programmatically, although I would think binding would work as well. I got the desired size of the grid programmatically as well.

Using Grid will auto size to content without setting extra parameters, but just the VerticalAlignment and HorizontalAlignment. For example:

<Grid VerticalAlignment="Top" HorizontalAlignment="Center">
    <Polygon Points="0, 0, 0, 100, 80, 100" Panel.ZIndex="6" Fill="Black" Stroke="Black" StrokeStartLineCap="Round" StrokeDashCap="Round" StrokeLineJoin="Round" StrokeThickness="10"/>
</Grid>

As I understood the canvas does not inform the scrollviewer when controls are added to the canvas that increase the size of the canvas. The scrollviewer does not know that the canvas size increased. What worked for me was just to change the width of the canvas programmatically after adding the control.

mycanvas.Children.Add(myTextBlock);
mycanvas.Width += 10.0

;

<viewbox>
    <canvas>
        <uielements /> 
    </canvas>
</viewbox>
Related