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.
Binding the Height/Width to the actual size of the control within the canvas worked for me:
Essentially it requires a complete rewrite of Canvas. Previous proposed solutions that override MeasureOverride fail because the default Canvas.Left/.Top &c properties invalidate Arrangment, but ALSO need to invalidate measure. (You get the right size the first time, but the size doesn't change if you move elements after the initial layout).
The Grid solution is more-or-less reasonable but binding to Margins in order to get x-y displacement can wreak havoc on other code (particalary in MVVM). I struggled with the Grid view solution for a while, but complications with View/ViewModel interactions and scrolling behaviour finally drove me to this. Which is simple and to the point, and Just Works.
It's not THAT complicated to re-implement ArrangeOverride and MeasureOverride. And you're bound to write at least as much code elsewhere dealing with Grid/Margin stupidity. So there you are.
Here's a more complete solution. non-zero Margin behaviour is untested. If you need anything other than Left and Top, then this provides a starting point, at least.
WARNING: You must use AutoResizeCanvas.Left and AutoResizeCanvas.Top attached properties instead of Canvas.Left and Canvas.Top. Remaining Canvas properties have not been implemented.
As an improvement to @MikeKulls's answer, here's a version which does not throw an exception when there are no UI elements in the canvas or when there are UI elements without Canvas.Top or Canvas.Left properties:
In the function, i'm trying to find the maximum X position and Y position, where controls in the canvas can reside.
Use the function only in Loaded event or later and not in constructor. The window has to be measured before loading..
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:
Hope this helps