3
votes

So I have a WPF window with a grid which has 2 columns. In the first column I have a canvas which I am rendering an SVG image to. This canvas is re-sized to be the same size as the image (so there are no transforms going on) and it is assumed that the image is a lot smaller than the screen so there is no need for a scroll viewer - I'll call this the left canvas.

In the second column I have another canvas which is inside a Viewbox, the same SVG is also being rendered to this canvas and it is assumed that the SVG image size is larger than the Viewbox fixed size. The Viewbox re-sizes the canvas to fit inside it - although doesn't appear to apply any transforms to the canvas and doesn't change its width or height, some other magic goes on here - but fine, it works.

The idea is that the user can draw a rectangle on the left canvas, which will represent a zoom area, and then the right canvas will zoom in so that rectangle will fit to the Viewbox containing the canvas - by fit I mean without cropping or stretching/squashing any of the zoom area , so if it is a landscape zoom area in a portrait Viewbox, the sides of the zoom area will meet the sides of the Viewbox leaving space at the top and bottom which is fine.

I though this would be straight forward as there are no transforms applied to either of the canvases, and they both have the same width and height (even though some magic from the Viewbox is making the right one smaller). This is how I'm doing it at the moment:

Find the centre point:

centreX = Canvas.GetLeft(zoomAreaRect) + (zoomAreaRect.Width / 2);
centreY = Canvas.GetTop(zoomAreaRect) + (zoomAreaRect.Height / 2);

Find the scale amount:

double scale;

if(zoomAreaRect.Width > zoomAreaRect.Height)
{
    scale = RightCanvas.Width / zoomAreaRect.Width;
}
else
{
    scale = RightCanvas.Height / zoomAreaRect.Height;
}

Then use a scale transform using centreX and centreY as the centre of the transform, and scale for both scaleX and scaleY on the transform.

Now this clearly doesn't work because I need to somehow take into account the Viewbox size when working out the scale amount, I'm just not sure how to do this. Can anyone help please?

Update:

I have scrapped the Viewbox as this complicates things.. so the right canvas is just normal size too but is contained inside a border with fixed width and height. The aim is to zoom in on the zoom area until it fits to the border.

Here is the XAML for the right side:

<Border Name="ContainingBorder" 
              Grid.Column="1"
              MaxWidth="295"
              MaxHeight="487"
              Height="487">
    <Border.Clip>
        <RectangleGeometry Rect="0.5, 0.5, 295, 487"/>
    </Border.Clip>

    <Canvas Name="RightCanvas"/>
</Border>

I have managed to zoom in the correct amount, it just doesn't zoom into the right centre. I just use the aspect ratio as the scale amount which appears to work:

double ratioX = ContainingBorder.AcctualWidth / zoomAreaRect.Width;
double ratioY = ContainingBorder.AcctualHeight / zoomAreaRect.Height;

double scale = ratioX < ratioY ? ratioX : ratioY;

Any ideas how I can figure out the centre x and y? The above centreX and centreY calculations don't appear to work properly.

2

2 Answers

0
votes

I'm not sure if I fully understood your question. maybe pasting the xaml code would help.

Potentially you can work with the "margin" property of the canvas:

<Grid>
    <Grid.ColumnDefinitions>
        <ColumnDefinition/>
        <ColumnDefinition/>
    </Grid.ColumnDefinitions>
    <!--clip to bounds for hide parts of the canvas-->
    <Viewbox ClipToBounds="True">
        <!--set margin negatively to zoom out of the viewbox size-->
        <Canvas Margin="0,0,-100,-100" Width="200" Background="Red" Height="200">
            <Rectangle Width="100" Height="100" Fill="Gray"/>
        </Canvas>
    </Viewbox>
</Grid>
0
votes

Maybe you can solve your problem with a visual brush:

<Window x:Class="ZoomViewBoxTest.MainWindow"
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
    Title="MainWindow" Height="300" Width="700">
<Grid>
    <Grid.ColumnDefinitions>
        <ColumnDefinition/>
        <ColumnDefinition/>
    </Grid.ColumnDefinitions>       
    <Viewbox Width="700" Height="300">
        <Canvas x:Name="LeftCanvas" Width="600" Background="Red" Height="600">
            <!--drawing Rectangle simulates your zoom area-->
            <Rectangle x:Name="DrawingRectange" Width="100" Height="150" Fill="Green" Canvas.Left="200" Canvas.Top="300"></Rectangle>
            <Rectangle Width="200" Height="200" Fill="Gray" Canvas.Left="250" Canvas.Top="350"/>
        </Canvas>
    </Viewbox>
    <Viewbox Grid.Column="1">
        <Rectangle Width="{Binding ElementName=DrawingRectange,Path=Width}" Height="{Binding ElementName=DrawingRectange,Path=Height}">
            <Rectangle.Fill>
                <VisualBrush Visual="{Binding ElementName=LeftCanvas}" Stretch="None" >
                    <VisualBrush.Viewbox>
                        <!-- X = DrawingRectangle.X / LeftCanvas.Width 
                             Y = DrawingRectangle.X / LeftCanvas.Height
                             Width = DrawingRectangle.Width / LeftCanvas.Width
                             Height = DrawingRectangle.Height / LeftCanvas.Height-->
                        <Rect X="0.333" Y="0.5" Width="0.1666" Height="0.25"></Rect>
                    </VisualBrush.Viewbox>
                </VisualBrush> 
                </Rectangle.Fill>
        </Rectangle>
    </Viewbox>
</Grid>

Of coarse the Viewbox of the VisualBrush must be binded to the actual values from the DrawingRectangle (in code behind or via converters).