Implementing Multiscale Images in Silverlight Applications

In Chapter 5, we discussed how to use Deep Zoom Composer to encode and generate a multiscale image. This section discusses how to actually implement a multiscale image in a Silverlight application. Silverlight provides the MultiScalelmage control to implement multiscale images in Silverlight applications.

The MultiScalelmage control allows you to use the XML and image output from Deep Zoom Composer to implement high-resolution images in your Silverlight applications that users can zoom in and out on.

Multiscale images are implemented in XAML by designing a MultiScaleImage control, for example:

<MultiScaleImage x:Name="msi"

UseSprings = "False"

Source="GeneratedImages/dzc_output.xml" height="4 80" Width="640"/>

The Source attribute of the MultiScaleImage control points to the location of the XML file that describes the image files that make up the multiscale image. This file is automatically generated by Deep Zoom Composer when you create the multiscale image. The UseSprings property tells Silverlight whether to use animations when rendering changes to size or position of the image.

The MultiScaleImage control is similar to an Image control with a few important additions. The MultiScaleImage provides the ViewportHeight and ViewportWidth properties that set the height and width of the rendered multiscale image. The ViewPortOrigin property specifies Point object that determines the center coordinates used when rendering the multiscale image. For example, the following code sets the center of the ViewPortOrigin property to the center of the multiscale image:

msImage.ViewportOrigin = new Point(0, 0);

The MultiScaleImage also provides the function ZoomAboutLogicalPoint() that adjusts the zoom factor and ViewPortOrigin and re-renders the MultiScaleImage. The function accepts a zoom factor, where 1 is actual size, as the first argument, a double specifying the x offset from center as the second, and a double specifying the y offset from center as the third. For example, the following code sets the zoom factor to 50 percent and renders the multiscale image with a ViewPortOrigin 100 pixels to the right and 50 pixels down from the center of the multi-scale image:

msImage.ZoomAboutLogicalPoint(.5, 100, -50);

The code in Listings 8.22, 8.23, and 8.24 shows an example of implementing a MultiScaleImage control in a Silverlight application. Most of the code in Listings 8.22 and 8.23, as well as all of the code in Listing 8.24, was generated using the Export Images and Silverlight Project option in Deep Zoom Composer. The GeneratedImages folder and contents need to be placed in the ClientBin directory of the Visual Studio project for the image to render when you are testing and will also need to be deployed with the project.

The code in Listing 8.22 implements a MultiScaleImage control named msi, a TextBlock control named loadText, and three Button controls named inBtn, outBtn, and allBtn.

XAML Code Defining a MultiScalelmage Control

<UserControl x:Class=MmultiScale.PageM

xmlns="http://schemas.microsoft.com/winfx/2 00 6/xaml/presentation" xmlns:x=Mhttp://schemas.microsoft.com/winfx/2 00 6/xamlM Width=M66 0M Height=M5 80M>

<Grid x:Name="LayoutRoot" Background=M#FFFFFFFFM> <Border BorderThickness=M1,1<1<1M Margin=M10,10<10<100M BorderBrush="#FF9F9F9F"> <MultiScaleImage x:Name=MmsiM UseSprings=MFalseM MinHeight=M4 80M MinWidth="64 0" Height=M480M Width=M640M/>

<TextBlock x:Name=MloadTextM Text=MLoading . . ." HorizontalAlignment="Center" VerticalAlignment=MBottomM Margin=M0, 0, 0, 60"/> <Button x:Name=MinBtnM Content=MZoom In" HorizontalAlignment="Center" VerticalAlignment="Bottom" Margin="0, 0, 2 00, 10" Height="3 0" Width=M10 0M/> <Button x:Name="allBtn" Content=MShow All" HorizontalAlignment="Center" VerticalAlignment="Bottom" Margin=M0, 0, 0, 10" Height="3 0" Width=M10 0M/> <Button x:Name=MoutBtnM Content=MZoom Out" HorizontalAlignment="Center" VerticalAlignment="Bottom" Margin="0, 0, -200, 10" Height="3 0" Width=M10 0M/>

The code in Listing 8.23 provides the functionality to zoom in an out on the multiscale image. In the Page() constructor, the code sets the Source attribute of the msi object to the GeneratedImages/dzc_output.xml file that was created by Deep Zoom Composer. Next, the code attaches the msi_Loaded and msi_ImageOpenSucceeded event handlers to the Loaded and ImageOpenSucceeded events of the msi object using the following code:

this.msi.Loaded += new RoutedEventHandler(msi_Loaded); this.msi.ImageOpenSucceeded +=

new RoutedEventHandler(msi_ImageOpenSucceeded);

Then the code attaches the zoomInClick, zoomOutClick, and ShowAllClick event handlers to the inBtn, outBtn, and allBtn Click events.

The rest of the Page() constructor attaches delegate functions for mouse events that allow multiscaled images to be dragged and resized using the mouse and mouse wheel. This code is generated by Deep Zoom Composer; however, you can easily modify any of it to adjust behavior for your application.

The msi_ImageOpenSucceeded() event handler uses the SubImages property of the MultiScaleImage to iterate through the subimages in the collection. If you specified the Export as Collection option, then the subimages of the multiscale image can be accessed in using the SubImages property. The code simply sets the Text attribute of the loadText control when the multiscale image has been opened successfully.

The msi_Loaded() event handler simply sets the zoom factor to .5 for the image and the ViewPointOrigin to the center when the image is loaded using the Zoom() function. The Zoom() function accepts a double that specifies the zoom factor and a Point that specifies the coordinate in the MultiScaleImage object to use for the ViewPointOrigin. The actual coordinate needs to be translated to a logical coordinate inside the multiscale image. This is done using the ElementToLogicalPoint() function as shown below:

Point logicalPoint = this.msi.ElementToLogicalPoint(pointToZoom);

The code renders the desired zoom on the multiscale image using the

ZoomAboutLogicalPoint() function of the MultiScaleImage object as shown below:

this.msi.ZoomAboutLogicalPoint(zoom, logicalPoint.X, logicalPoint.Y);

The code in the ShowAllClick() handler sets the ViewPointOrigin to the center of the multiscale image. The ViewPortWidth is set to 1 to reset the view port to the full size of the MultiScaleImage control. Then the ZoomFactor is set to 1, which resets the image view to the full multiscale image.

The zoomInClick() handler uses the Zoom() function to set the zoom factor to 1.2 of the current size and the x and y offsets to half the width and height of the current size. The zoomOutClick() handler uses the Zoom() function to set the zoom factor to .8 of the current size and the x and y offsets to half the width and height of the current size.

C# Code That Implements MultiScaleImage Zooming Functionality using System;

using System.Collections.Generic;

using System.Linq;

using System.Net;

using System.Windows;

using System.Windows.Controls;

using System.Windows.Documents;

using System.Windows.Input;

using System.Windows.Media;

using System.Windows.Media.Animation;

using System.Windows.Shapes;

namespace multiScale {

public partial class Page : UserControl {

// Based on prior work done by

// Lutz Gerhard, Peter Blois, and Scott Hanselman

Point lastMousePos = new Point();

double _zoom = 1; bool mouseButtonPressed = false; bool mouseIsDragging = false; Point dragOffset; Point currentPosition;

public double ZoomFactor {

InitializeComponent();

//Sets the MultiScale Image Source this.msi.Source =

new DeepZoomImageTileSource(

new Uri("GeneratedImages/dzc_output.xmlM, UriKind.Relative));

this.msi.Loaded += new RoutedEventHandler(msi_Loaded);

LISTING 8.23

(continued)

this.msi.ImageOpenSucceeded +=

new RoutedEventHandler(msi_ImageOpenSucceeded);

inBtn.Click += new RoutedEventHandler(zoomInClick); allBtn.Click += new RoutedEventHandler(ShowAllClick); outBtn.Click += new RoutedEventHandler(zoomOutClick);

//Events implemented by DeepZoomComposer this.MouseMove += delegate(object sender, MouseEventArgs e) {

if (mouseButtonPressed) {

mouseIsDragging = true;

this.lastMousePos = e.GetPosition(this.msi);

this.MouseLeftButtonDown +=

delegate(object sender, MouseButtonEventArgs e)

mouseButtonPressed = true; mouseIsDragging = false; dragOffset = e.GetPosition(this); currentPosition = msi.ViewportOrigin;

this.msi.MouseLeave +=

delegate(object sender, MouseEventArgs e)

mouseIsDragging = false;

this.MouseLeftButtonUp +=

delegate(object sender, MouseButtonEventArgs e)

mouseButtonPressed = false;

if (mouseIsDragging == false) {

bool shiftDown =

(Keyboard.Modifiers & ModifierKeys.Shift) ModifierKeys.Shift;

ZoomFactor = 2.0;

if (shiftDown) ZoomFactor = 0.5;

Zoom(ZoomFactor, this.lastMousePos);

mouseIsDragging = false;

this.MouseMove += delegate(object sender, MouseEventArgs e) {

if (mouselsDragging) {

Point newOrigin = new Point(); newOrigin.X = currentPosition.X -

(((e.GetPosition(msi).X - dragOffset.X) / msi.ActualWidth) * msi.ViewportWidth); newOrigin.Y = currentPosition.Y -

(((e.GetPosition(msi).Y - dragOffset.Y) / msi.ActualHeight) * msi.ViewportWidth); msi.ViewportOrigin = newOrigin;

new MouseWheelHelper(this).Moved +=

delegate(object sender, MouseWheelEventArgs e)

ZoomFactor = 1.2;

else

ZoomFactor = .80; Zoom(ZoomFactor, this.lastMousePos);

void msi_ImageOpenSucceeded(object sender, RoutedEventArgs e) {

//If collection, SubImages list of all MultiScaleSubImages int x=0;

foreach (MultiScaleSubImage subImage in msi.SubImages) {

loadText.Text = "Loaded " + x + " sub-images.";

void msi_Loaded(object sender, RoutedEventArgs e) {

public void Zoom(double zoom, Point pointToZoom) {

Point logicalPoint =

this.msi.ElementToLogicalPoint(pointToZoom); this.msi.ZoomAboutLogicalPoint(zoom, logicalPoint.X,

LISTING 8.23

(continued)

logicalPoint.Y);

private void ShowAllClick(object sender, RoutedEventArgs e) {

this.msi.ViewportOrigin = new Point(0, 0); this.msi.ViewportWidth = 1; ZoomFactor = 1;

private void zoomInClick(object sender, RoutedEventArgs e) {

Zoom(1.2, new Point(this.ActualWidth / 2, this.ActualHeight / 2));

private void zoomOutClick(object sender, RoutedEventArgs e) {

Zoom(.8, new Point(this.ActualWidth / 2, this.ActualHeight / 2));

The code in Listing 8.24 is a C# helper file that is generated by Deep Zoom Composer to provide mouse wheel event support that is used in Listing 8.23.

LISTING 8.24

C# File That Implements Mouse Wheel Event Handling Functionality using System;

using System.Net;

using System.Windows;

using System.Windows.Controls;

using System.Windows.Documents;

using System.Windows.Ink;

using System.Windows.Input;

using System.Windows.Media;

using System.Windows.Media.Animation;

using System.Windows.Shapes;

using System.Windows.Browser;

namespace multiScale {

// Courtesy of Pete Blois public class MouseWheelEventArgs : EventArgs {

private double delta; private bool handled = false;

public MouseWheelEventArgs(double delta) {

this.delta = delta;

public double Delta {

// Use handled to prevent the default browser behavior!

public bool Handled {

get { return this.handled; } set { this.handled = value; }

public class MouseWheelHelper {

public event EventHandler<MouseWheelEventArgs> Moved; private static Worker worker; private bool isMouseOver = false;

public MouseWheelHelper(FrameworkElement element) {

if (MouseWheelHelper.worker == null)

MouseWheelHelper.worker = new Worker();

MouseWheelHelper.worker.Moved += this.HandleMouseWheel;

element.MouseEnter += this.HandleMouseEnter; element.MouseLeave += this.HandleMouseLeave; element.MouseMove += this.HandleMouseMove;

continued

LISTING 8.24

(continued)

private void HandleMouseWheel(object sender,

MouseWheelEventArgs args)

if (this.isMouseOver)

this.Moved(this, args)

private void HandleMouseEnter(object sender, EventArgs e) this.isMouseOver = true;

private void HandleMouseLeave(object sender, EventArgs e) this.isMouseOver = false;

private void HandleMouseMove(object sender, EventArgs e) this.isMouseOver = true;

private class Worker public event EventHandler<MouseWheelEventArgs> Moved;

public Worker() {

(HtmlPage.IsEnabled)

HtmlPage.Window.AttachEvent("DOMMouseScrollM, this.HandleMouseWheel) HtmlPage.Window.AttachEvent("onmousewheel", this.HandleMouseWheel) HtmlPage.Document.AttachEvent("onmousewheel", this.HandleMouseWheel)

private void HandleMouseWheel(object sender, HtmlEventArgs args)

double delta = 0;

ScriptObject eventObj = args.EventObject;

if (eventObj.GetProperty("wheelDelta") != null) {

delta =

((double)eventObj.GetProperty("wheelDelta"))/12 0;

if (HtmlPage.Window.GetProperty("operaM) != null) delta = -delta;

else if (eventObj.GetProperty("detailM) != null) {

delta = -((double)eventObj.GetProperty("detail")) / 3;

if (HtmlPage.BrowserInformation.UserAgent.IndexOf(

MouseWheelEventArgs wheelArgs =

new MouseWheelEventArgs(delta); this.Moved(this, wheelArgs);

if (wheelArgs.Handled)

args.PreventDefault();

The results of the application defined in Listings 8.22, 8.23, and 8.24 are shown in Figure 8.12. When the application starts, the multiscale image is loaded, the text of the loadText control is loaded, and the image can be resized using the button controls and the mouse.

FIGURE 8.12

Silverlight application that implements a MultiScalelmage control to dynamically resize a high resolution image

FIGURE 8.12

+1 0

Average user rating: 5 stars out of 1 votes

Post a comment

  • Receive news updates via email from this site