Silverlight Zooming and Panning a Canvas

12. May 2009

Zooming and Panning seems to be in great demand for Silverlight projects. I myself searched around through numerous articles trying to figure out good ways to do it. I came up with a solution that hopefully can satisfy some of the demand here on the internets.

First thing I did was make a control that is the zooming and panning canvas. I called it DesignRender as it applied to the particular project I was developing.

<UserControl
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
    xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
    xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
    mc:Ignorable="d"
    x:Class="SilverlightApplication2.DesignRender">
    
    <ScrollViewer x:Name="scrollRoot" VerticalScrollBarVisibility="Disabled" 
                  HorizontalScrollBarVisibility="Disabled">
        <Canvas x:Name="canvasRoot">
            <Canvas Background="#FF00FBF2" x:Name="canvasContainer"
                    Width="1000" Height="800">
                <!-- This is dummy stuff for zooming/panning -->
                <Rectangle Width="90" Height="90" Fill="#FFCA1F1F"
                           Canvas.Left="0" Canvas.Top="0" 
                           RadiusX="2" RadiusY="2" />
                <Canvas.RenderTransform>
                    <ScaleTransform x:Name="scaler" 
                                    CenterX="0" CenterY="0" 
                                    ScaleX="1" ScaleY="1" />
                </Canvas.RenderTransform>
            </Canvas>
        </Canvas>
    </ScrollViewer>
</UserControl>

I am using a scrollviewer so I can place the UserControl effectively in a parent control. You can change this as desired. The canvasRoot acts as your background and the canvasContainer is the actual content residing in your background. If this was photoshop, the canvasRoot would be the greyed out background and the canvasContainer would be your actual document. Hence the static sizing of the canvasContainer. Now for the code behind.

public partial class DesignRender : UserControl
{
    private bool isDragging = false;
    private Point offset;
    
    public DesignRender()
    {
        InitializeComponent();

        // Handle the mouse click events within the control
        // for panning.
        this.canvasContainer.MouseLeftButtonUp += 
            new MouseButtonEventHandler(canvasContainer_MouseLeftButtonUp);
        this.canvasContainer.MouseLeftButtonDown +=
            new MouseButtonEventHandler(canvasContainer_MouseLeftButtonDown);
        this.canvasContainer.MouseMove += 
            new MouseEventHandler(canvasContainer_MouseMove);
        
        // Handle the scroll wheel events. They are different
        // per browser so we have to attach to a couple of events.     
        HtmlPage.Window.AttachEvent("DOMMouseScroll", OnMouseWheel);
        HtmlPage.Window.AttachEvent("onmousewheel", OnMouseWheel);
        HtmlPage.Document.AttachEvent("onmousewheel", OnMouseWheel);
    }

    /// <summary>
    /// Called when the mouse wheel is used. Zooms
    /// in our out depending.
    /// </summary>
    private void OnMouseWheel(object sender, HtmlEventArgs e)
    {
        double mouseDelta = 0;
        ScriptObject obj = e.EventObject;
        
        // Get what the mouseDelta was in
        // the particular browser being used
        if (obj.GetProperty("detail") != null)
            mouseDelta = ((double)obj.GetProperty("detail"));
        else if (obj.GetProperty("wheelDelta") != null)
            mouseDelta = ((double)obj.GetProperty("wheelDelta"));
        
        // Quick mouse translation
        mouseDelta = Math.Sign(mouseDelta);

        // calculate the new scale for the canvas
        double newScale = this.scaler.ScaleX + (mouseDelta * .25);

        // Don't allow scrolling too big or small
        if (newScale < 0.25)
            return;
        if (newScale > 10)
            return;

        // Set the zoom!
        this.scaler.ScaleX = newScale;
        this.scaler.ScaleY = newScale;
    }
    
    /// <summary>
    /// Called when user presses mouse down. Start
    /// panning.
    /// </summary>
    private void canvasContainer_MouseLeftButtonDown(
        object sender, MouseButtonEventArgs e)
    {
        // Say we are dragging
        this.isDragging = true;
        this.canvasContainer.CaptureMouse();
        // Calculate the place where they clicked
        offset = e.GetPosition(this.canvasContainer);
        offset.X *= this.scaler.ScaleX;offset.Y *= this.scaler.ScaleY;
    }
    
    /// <summary>
    /// Called when user releases mouse. End panning
    /// </summary>
    private void canvasContainer_MouseLeftButtonUp(
        object sender, MouseButtonEventArgs e)
    {
        if (this.isDragging)
        {
            // Say we are done dragging
            this.isDragging = false;
            this.canvasContainer.ReleaseMouseCapture();
        }
    }
    
    /// <summary>
    /// Called when the user moves there mouse. If
    /// they have the mouse button pressed then we
    /// pan.
    /// </summary>
    private void canvasContainer_MouseMove(
        object sender, MouseEventArgs e)
    {
        if (this.isDragging)
        {
            // Calculate the new drag distance
            Point newPosition = e.GetPosition(this.canvasRoot);
            Point newPoint = 
                new Point(newPosition.X - offset.X,newPosition.Y - offset.Y);
            
            // Set the values
            this.canvasContainer.SetValue(Canvas.LeftProperty,newPoint.X);
            this.canvasContainer.SetValue(Canvas.TopProperty,newPoint.Y);
        }
    }
}

The code itself should be pretty self explanatory. I use scaling to do the zooming and mouse down events for panning. Now just host this control in your Page.xaml whatever way you would like. This should get everyone started in the right direction. Hope this helps.



Currently rated 5.0 by 1 people

  • Currently 5/5 Stars.
  • 1
  • 2
  • 3
  • 4
  • 5

C#, Silverlight , , ,