ZoomableApplication1: Scale and Offset

For the first entry in the series of examples using the ZoomableCanvas, I’ll start off with just the basics: Scale and Offset.  I’ll start by creating a brand new app, renaming it from WpfApplication1 to ZoomableApplication1, and adding a reference to a class library that I created after downloading the ZoomableCanvas source code and its dependencies that were listed at the bottom of this post: https://blogs.msdn.com/b/kaelr/archive/2010/07/29/zoomablecanvas.aspx.

The next thing I’ll do is get rid of the default <Grid> and replace it with a <ZoomableCanvas> with some children, like this:

 <Window x:Class="ZoomableApplication1.MainWindow"
        xmlns="https://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="https://schemas.microsoft.com/winfx/2006/xaml"
        Title="MainWindow" Height="350" Width="525">
    <ZoomableCanvas>
        <Rectangle Canvas.Top="0" Canvas.Left="0"
                   Width="30" Height="40" Fill="LightBlue"/>

        <Button    Canvas.Top="10" Canvas.Left="40"
                   Content="Hello, ZoomableCanvas!"/>

        <Calendar  Canvas.Top="40" Canvas.Left="10"
                   DisplayDate="6/18/2008"/>
    </ZoomableCanvas>
</Window>

And as you’d expect with a regular <Canvas>, this is the result:

(I’ve embedded the sample as an XBAP in an <iframe> above this line, so you should see a live sample and be able to interact with it if you’re using Internet Explorer and have .NET 4.0 installed.)

So far this is no different from a regular <Canvas>, so lets start off by setting the Scale and Offset directly in the XAML, like this:

     <ZoomableCanvas Scale=".5" Offset="-100,0">

You’ll notice that the elements have shrunk by half and moved to the right by 100 pixels.  But this is not normally how you’d set Scale and Offset.  Normally you’d change them dynamically; for example you might want to set Scale with a zoom slider, like this:

     <DockPanel>
        <Slider Minimum=".1" Maximum="2" Orientation="Vertical"
                Value="{Binding ElementName=MyCanvas, Path=Scale}"/>

        <ZoomableCanvas x:Name="MyCanvas">
            <Rectangle Canvas.Top="0" Canvas.Left="0"
                Width="30" Height="40" Fill="LightBlue"/>

            <Button    Canvas.Top="10" Canvas.Left="40"
                Content="Hello, ZoomableCanvas!"/>

            <Calendar  Canvas.Top="40" Canvas.Left="10"
                DisplayDate="6/18/2008"/>
        </ZoomableCanvas>
    </DockPanel>

But of course it’s much more natural to use the mouse wheel to zoom and click and drag to pan.  So let’s override the OnMouseWheel and OnMouseMove events on our Window to control the Scale and Offset as well:

 protected override void OnMouseWheel(MouseWheelEventArgs e)
{
    var x = Math.Pow(2, e.Delta / 3.0 / Mouse.MouseWheelDeltaForOneLine);
    MyCanvas.Scale *= x;
}

private Point LastMousePosition;

protected override void OnMouseMove(MouseEventArgs e)
{
    var position = e.GetPosition(this);
    if (e.LeftButton == MouseButtonState.Pressed)
    {
        MyCanvas.Offset -= position - LastMousePosition;
    }
    LastMousePosition = position;
}

The OnMouseWheel method takes the distance that the mouse wheel was turned (which is given by e.Delta), divides it by 3 since by default Windows expects to scroll 3 “lines” per tick, and then divides that amount by the amount of Delta that represents each “line”.  That value could be negative since e.Delta is negative when the mouse wheel is rotated backwards, so raising 2 to that power gives us a nice value above or below 1.0, but never below 0.  Then we simply adjust the scale by multiplying it by that amount.

The OnMouseMove method is about equally as simple.  It just keeps track of the last mouse position, and if the mouse button is held down when you drag the mouse then it adjusts Offset by the amount you dragged it.  Of course, we aren’t setting e.Handled = true or doing any mouse capturing in this tiny example, so you get some interesting interactions with other elements on the canvas that try to handle the mouse as well.  Try dragging the zoom slider remaining from the previous example for an interesting effect!

That’s about it for Scale and Offset.  In the upcoming posts I’ll show some more examples of mouse handling, including having the canvas zoom in and out “around the mouse”, which means that the point under the mouse cursor will stay “fixed” while the other points on the canvas move in and out relative to it as you zoom.  You can take a look at Bing maps to see what I mean.

In the mean time, I’ve attached the ZoomableApplication1 project to this post, and it includes the class library that I mentioned in the beginning.  The class library simply contains the source code files listed at bottom of this post: https://blogs.msdn.com/b/kaelr/archive/2010/07/29/zoomablecanvas.aspx, but if you don’t want to go download all the source files yourself then you can just grab the ZoomableCanvas.dll assembly from here since I’ve already done the work for you.  Enjoy!

ZoomableApplication1.zip