Search code examples
wpfxamlcanvasresizescrollviewer

Control size of Canvas in ScrollViewer


I am writing a custom control for image display and interaction. Some of the features it needs is zooming and panning as well as drawing a custom region of interest, i.e. drawing a rectangle on top of the displayed frame with arbitrary position and size. Zooming and panning features are done and work nicely, I'm using a ScrollViewer, a scale transform and some event handlers for mouse clicking and moving in the C# code.

For the custom selection feature I want to add a canvas on top of the frame since that's the only way I know of that allows arbitrary positioning of a rectangle. In order for any shapes on the canvas to be mappable to image coordinates I need of course the canvas to exactly line up with the image so size and position must be the same. I also would like the image to keep adjusting to window resizes as it does without any canvas. Since Canvas doesn't automatically resize itself to anything I want to bind its dimensions to the size of the image like so (this is from the control template for the custom control):

<ScrollViewer x:Name="scrollViewer" Grid.Column="0" Grid.Row="1" CanContentScroll="True" PanningMode="Both" HorizontalScrollBarVisibility="Visible" VerticalScrollBarVisibility="Visible">
  <Grid>
    <Image x:Name="screen" Panel.ZIndex="0" Cursor="Cross" Source="{TemplateBinding ColoredFrame}"/
    <Canvas x:Name="testCanvas" Panel.ZIndex="1" Background="Transparent" Width="{Binding ActualWidth, ElementName=screen}" Height="{Binding ActualHeight, ElementName=screen}"/>
    <Grid.LayoutTransform>
      <TransformGroup>
        <RotateTransform Angle="{Binding RotationAngle, RelativeSource={RelativeSource TemplatedParent}}"/>
        <ScaleTransform x:Name="scaleTransform" ScaleX="{Binding ZoomFactor, RelativeSource {RelativeSource TemplatedParent}}" ScaleY="{Binding ZoomFactor, RelativeSource={RelativeSource TemplatedParent}}"/>
      </TransformGroup>
    </Grid.LayoutTransform>
  </Grid>
</ScrollViewer>

However, I am getting weird behaviour with this - when I use bindings for both Width and Height of the canvas to ActualWidth and ActualHeight of the image, like in the above code, it will automatically resize itself only when the size increases (when the user increases the size of the window) but not when it decreases. Any size increase is permanent and when decreasing the window again, one would have to scroll as the size of the image and canvas never decrease. This does not happen when I bind only one of the dimensions (width or height) and set the other one to a constant value. Binding modes (one way / two way) have no effect either.

I'm probably missing something really obvious but I've spent hours trying to figure this out and I am at a loss. Any help would be appreciated.


Solution

  • as far as I know Canvas does not have good sizing options. I think an Adorner will do what you try to achieve. For an example and additional information please have a look here: https://learn.microsoft.com/de-de/dotnet/framework/wpf/controls/adorners-overview

    Happy coding Tim