Microsoft’s Silverlight 3 and the DeepZoom composer allow users to display high-resolution images interactively. This provides several benefits for either sets of images in a gallery or for smoothly viewing very large image files.
This brief tutorial will demonstrate how to set up a Silverlight 3 project with interactive zoom and panning/scrolling with a DeepZoom image.
The first thing we will do is add the MultiScaleImage control to our automatically generated UserControl. This control contains the event handlers that we will need to interact with the deep zoom image.
1: <MultiScaleImage x:Name="_deepZoomImage" Margin="7" MouseWheel="_deepZoomImage_MouseWheel" MouseLeftButtonDown="_deepZoomImage_MouseLeftButtonDown" MouseLeftButtonUp="_deepZoomImage_MouseLeftButtonUp" MouseMove="_deepZoomImage_MouseMove" MouseLeave="_deepZoomImage_MouseLeave">
3: <DropShadowEffect ShadowDepth="8" BlurRadius="10" Color="#FF605D5D"/>
We’ve already added several event handlers to the control:
- MouseWheel: Event fires when the scroll wheel moves forward/backward on the mouse.
- MouseLeftButtonDown/Up: These events fire when the user clicks. Both will be necessary to determine if the user is clicking or dragging the image.
- MouseMove: Will also be used for dragging/panning the image within the control.
- MouseLeave: Indicates when the mouse cursor leaves the control area. Needed in case the user passes the edge of the control when panning.
Now that we have our control created within our XAML file, we can begin loading our images and adding the code to interact with the control. First, we need to prepare a DeepZoom image using the DeepZoom composer. There is a short video to learn how to import images and create a Silverlight DeepZoom collection. Once created, the “GeneratedImages” folder can be uploaded to a webserver of your choice, or used locally depending on the setup of your Silverlight project (be careful for cross-zone policy issues if debugging Silverlight from the ASP webserver and using local image files).
We will need three global variables to keep the status of our mouse:
1: bool _dragging = false;
2: Point lastMousePosition;
3: Point lastMouseViewport;
We can then add in our basic information into the main constructor:
1: public MainPage()
4: string baseUrl = "http://pathToImages/"
5: _deepZoomImage.Source = new DeepZoomImageTileSource(new Uri(baseUrl + "/dzc_output.xml"));
The above URL can point either to the collage “dzc_output.xml” file, or to an individual deep zoom image XML file (found in the “dzc_output_images” folder). This set of code will load the image into our control as soon as the form loads. Depending on how your application functions, this can be done at any point or bound to other controls (buttons, list of albums, etc).
The application should compile at this point and be able to display the images. Now we can start to add some interactivity to the images.
First, we will add the code to tell our control when the user is dragging the image:
1: // Set dragging to true when user clicks their mouse within the control
2: private void _deepZoomImage_MouseLeftButtonDown(object sender, MouseButtonEventArgs e)
4: _dragging = true;
7: // Set dragging to false when user stops click
8: // If the mouse position hasn't changed, zoom in on the current location
9: private void _deepZoomImage_MouseLeftButtonUp(object sender, MouseButtonEventArgs e)
11: _dragging = false;
12: if (e.GetPosition(_deepZoomImage).Equals(lastMousePosition))
14: Point mouse = e.GetPosition(_deepZoomImage);
15: _deepZoomImage.ZoomAboutLogicalPoint(1.6, (_deepZoomImage.ElementToLogicalPoint(mouse)).X, (_deepZoomImage.ElementToLogicalPoint(mouse)).Y);
The first value within “ZoomAboutLogicalPoint” can be adjusted based on how far in or out you would like the image to zoom. The additional values indicate the “LogicalPoint” within the image that was clicked (a value of 0 to 1 indicating the far left/top to far right/bottom). To prevent some general bugginess within the application, we’ll also add a “stop dragging” command for when the user leaves the control:
1: // Set dragging to false when mouse leaves the control area
2: private void _deepZoomImage_MouseLeave(object sender, MouseEventArgs e)
4: _dragging = false;
Now to actually handle the dragging. These statements get placed in the MouseMove event handler. If our user has clicked (_dragging == true), we can move the image. If our user hasn’t clicked, set the last position for use with dragging:
1: // Code to handle panning our image
2: private void _deepZoomImage_MouseMove(object sender, MouseEventArgs e)
4: if (_dragging)
6: Point newOrigin = new Point();
7: newOrigin = lastMouseViewport; // Set the last mouse position
9: // Add the X and Y values of our current mouse position
10: newOrigin.X += ((lastMousePosition.X - e.GetPosition(_deepZoomImage).X) / _deepZoomImage.ActualWidth * _deepZoomImage.ViewportWidth);
11: newOrigin.Y += ((lastMousePosition.Y - e.GetPosition(_deepZoomImage).Y) / _deepZoomImage.ActualWidth * _deepZoomImage.ViewportWidth);
13: // Set the Viewport origin to the new mouse location
14: _deepZoomImage.ViewportOrigin = newOrigin;
18: lastMousePosition = e.GetPosition(_deepZoomImage);
19: lastMouseViewport = _deepZoomImage.ViewportOrigin;
In the above code, the X and Y scroll distance is calculated by subtracting the current mouse position from the last recorded location. This value is then divided by the image’s ActualWidth and multiplied by the ViewportWidth to compensate for any zooming within the image (otherwise the image will scroll very fast or very slow if the user has zoomed in or out).
Almost done! We’re also going to add an event so that the use can scroll in and out using their mouse wheel:
1: private void _deepZoomImage_MouseWheel(object sender, MouseWheelEventArgs e)
3: Point mouse = e.GetPosition(_deepZoomImage);
4: if (e.Delta > 0) // If scrolling in
6: _deepZoomImage.ZoomAboutLogicalPoint(1.2, (_deepZoomImage.ElementToLogicalPoint(mouse)).X, (_deepZoomImage.ElementToLogicalPoint(mouse)).Y);
8: else // If scrolling out
10: _deepZoomImage.ZoomAboutLogicalPoint(0.8, (_deepZoomImage.ElementToLogicalPoint(mouse)).X, (_deepZoomImage.ElementToLogicalPoint(mouse)).Y);
We use our Delta value to determine if the user was scrolling in or out, and then zoom using ZoomAboutLogicalPoint accordingly. Again, the first value can be changed to alter the “speed” of zooming. This works great for desktop users, but adding additional functionality for people without scroll wheels (particularly laptop users) would be necessary for a full-featured application.
Finally, we’re going to add one piece of XAML and an event handler in case the image gets lost so that the user can reset the zoom and position of the original image:
1: <Button x:Name="resetZoom" Height="27" HorizontalAlignment="Right" Margin="0,47,128,0" VerticalAlignment="Top" Width="29" Content="Reset
Zoom" Opacity="0.25" FontSize="8" Click="resetZoom_Click">
3: <SolidColorBrush Opacity="0.5" Color="#FF1F3B53"/>
1: private void resetZoom_Click(object sender, RoutedEventArgs e)
3: _deepZoomImage.ZoomAboutLogicalPoint(_deepZoomImage.ViewportWidth, 0.5, 0.5);
4: _deepZoomImage.ViewportOrigin = new Point(0, 0);
Since the ViewportWidth indicates how far the image is zoomed, we simply tell our control to ZoomAboutLogicalPoint with ViewportWidth as the first argument. We then reset the position to the center of the image.
To view the final project: http://www.agilemedicine.com/resources/DeepZoomExample
We’ll get the full source code posted soon. If you have any questions or comments, feel free to leave them below or e-mail us at firstname.lastname@example.org.