Skip Navigation LinksHome / Articles / View Article

The MultiscaleImage control and the SubImages collection

+ Add to SilverlightShow Favorites
7 comments   /   posted by Martin Mihaylov on Jul 21, 2008
(1 votes)

Update: The demo, the source code and the code in the article are compatible with Silverlight 2 RTW. The only changes I made were to the object element in the host page.

Introduction

The MultiscaleImage is a really great control that allows us to do amazing things in Silverlight 2. That is why I decided to write a series of articles about the nice things that can be done using it. This is the first one and it is focused on the SubImages collection. It explains how to select an image from the collection and then fit it to the size of the control. If you're new to this control read my previous article about it - Using the MultiscaleImage control. Before going ahead you should also be familiar with the DeepZoom Composer and its latest changes. Note that in this article I've replaced the "Double-Click" zoom with “One-Click” zoom.

Live demo | Source code

The SubImage collection

The MultiscaleImage control has a property that contains a collection of the Images that are used in the composition. In order to use it you should export your DeepZoom Composer project as a collection. Now let's see how we can get an image from the collection when it's clicked with the mouse for example:

   1: private int GetSelectedImage( Point p )
   2: {
   3:     for( int i = 0; i < MyMultiscaleImage.SubImages.Count; i++ )
   4:     {
   5:         MultiScaleSubImage subImage = MyMultiscaleImage.SubImages[ i ];
   6:  
   7:         double scaleBy = 1 / subImage.ViewportWidth;
   8:         Rect rect = new Rect( -subImage.ViewportOrigin.X * scaleBy, 
   9:                                       -subImage.ViewportOrigin.Y * scaleBy, 
  10:                                       1 * scaleBy, 
  11:                                     ( 1 / subImage.AspectRatio ) * scaleBy );
  12:  
  13:         if( rect.Contains( p ) )
  14:             return i;
  15:     }
  16:  
  17:     return -1;
  18: }

We have clicked on one of the images in the collection. In order to find the clicked image we have to iterate throughout the whole collection, of images calculate scale factor, which is reciprocal to the ViewportWidth of the SubImage. Than we create a rectangle that has the same coordinates as the SubImage in the MultiscaleImage control. The Rectangle class has a method Contains that takes a Point as an argument (in this case it's a logical point) and checks if that point is contained within the bounds of the rectangle. If the rectangle contains the point, then the SubImage also contains it. We return the found index, but if no image is clicked we return -1 as default value.

Fitting the image to the control

Since we can get the selected image out of the collection, let's now try to fit it to the MultiscaleImage control. Here is the method:

   1: private void FitImageToScreen( int index )
   2: {
   3:     if( index != -1 )
   4:     {
   5:         MultiScaleSubImage subImage = MyMultiscaleImage.SubImages[ index ];
   6:  
   7:         this.parentViewportWidth = 1 / subImage.ViewportWidth;
   8:         MyMultiscaleImage.ViewportWidth = this.parentViewportWidth;
   9:         double scaleBy = 1 / subImage.ViewportWidth;
  10:         MyMultiscaleImage.ViewportOrigin = new Point( -subImage.ViewportOrigin.X * scaleBy,
  11:                                                       -subImage.ViewportOrigin.Y * scaleBy );
  12:     }
  13: }

If the index is different then -1, than we have something that can be fitted. We get the image out of the SubImages using the index. The ViewportWidth property of the SubImage returns the ViewportWidth of the image when the VieweportWidth of the control is 1. So it's easy to calculate how much the ViewportWidth of the control must be so the SubImage has a ViewportWidth of 1. Than we calculate the new ViewportOrigin in dependence of the ViewportOrigin of the SubImage and the scale factor. Finally we save the new ViewportWidth of the control, in order to use it later.

Putting these methods in use

I do the zooming and the fitting in the handler for the MouseLeftButtonUp event of the MultiscaleImage control:

   1: private void MyMultiscaleImage_MouseLeftButtonUp( object sender, MouseButtonEventArgs e )
   2: {
   3:     if( !dragInProgress )
   4:     {
   5:         Point p = e.GetPosition( MyMultiscaleImage );
   6:         Point zoomPosition = MyMultiscaleImage.ElementToLogicalPoint( new Point( p.X, p.Y ) );
   7:  
   8:         if( ( Keyboard.Modifiers & ModifierKeys.Alt ) == ModifierKeys.Alt )
   9:         {
  10:             this.Zoom( 0.9, zoomPosition );
  11:         }
  12:         else
  13:         {
  14:             this.Zoom( 1.1, zoomPosition );
  15:  
  16:             int index = this.GetSelectedImage( MyMultiscaleImage.ElementToLogicalPoint(
                                                         e.GetPosition( MyMultiscaleImage ) ) );
  17:             if( index == selectedImageIndex )
  18:             {
  19:                 if( ( float )this.parentViewportWidth < ( float )MyMultiscaleImage.ViewportWidth 
                          || MyMultiscaleImage.ViewportWIdth == 1)
  20:                 {
  21:                     this.FitImageToScreen( index );
  22:                 }
  23:  
  24:             }
  25:             else
  26:             {
  27:                 this.FitImageToScreen( index );
  28:             }
  29:  
  30:             selectedImageIndex = index;
  31:         }
  32:     }
  33:  
  34:     dragInProgress = false;
  35:     mouseLeftButtonClicked = false;
  36: }

The logic here is the following - if the image is not fitted and is smaller we fit it, if it's not fitted, but is bigger we don't fit it, so we can zoom it further. If another image is clicked it's fitted to the screen.

Summary

That is all for now and as you can see it isn’t complicated at all. Hope you weren’t scared by the spiders. Here is a link to the live demo and a link to the source code of this example. Stay tuned for the next article, because the things are going to become more and more impressive step by step.

Share


Comments

Comments RSS RSS
  • RE: The MultiscaleImage control and the SubImages collection  

    posted by blackmonday on Sep 05, 2008 04:45

    When trying to follow along and recreate this I found many holes and items  left out but I've managed to fill them all in apart from the 'DeepZoomOutput' namespace. I can't find this anywhere and a search online and in various blogs turns up absolutely nothing. Can you please advise how to fix this?

  • RE: The MultiscaleImage control and the SubImages collection  

    posted by Enrai on Sep 05, 2008 05:12

    Hi, blackmonday,

    Concretely for this sample the DeepZoomOutput namespace can be found in the MousWheelHelper.cs file. It's confusing, so I'll be sure to change it. I'll also be glad if you share the other holes with me, so I can improve the code and take myself a note. You know, any feedback is welcome :)

  • RE: The MultiscaleImage control and the SubImages collection  

    posted by cammer on Jan 20, 2009 16:56

    How would I bring an image into focus based on a value I pass to it vs what the user clicks on?   I have multiple images that will take the whole space of the msi - I only want to show one at a time based on the value I pass it (with some conditional logic).

  • RE: The MultiscaleImage control and the SubImages collection  

    posted by Enrai on Jan 27, 2009 08:19

    Hi, cammer!

    Based on the value you have passed you must calculate the index of the needed Image, so you would be able to access it later in the SubImages collection. You have to build your logic around that index and bind the value, you passed, to it somehow.

    If you can be more concrete and describe your case, I could give you some more hints and ideas! ;)

  • RE: The MultiscaleImage control and the SubImages collection  

    posted by codegolem on Jul 24, 2009 17:12

    Hello there! Thank you for this useful info!

    I was wondering how to fit a SubImage in the MultiScaleImage if the SubImage's AspectRatio is different from the MultiScaleImage's one... (for example... a vertical SubImage to be fit in an horizontal MultiScaleImage)...

    Any ideas? Thank you!

  • RE: The MultiscaleImage control and the SubImages collection  

    posted by Daniel on Aug 31, 2009 17:43

    Excelent article ..

    I've managed to make al work perfectly ..  Thanks Enrai :)

  • RE: The MultiscaleImage control and the SubImages collection  

    posted by Nate on Feb 10, 2010 00:45
    Thank you.  This article was perfect

Add Comment

 
 

   
  
  
   
Please add 5 and 6 and type the answer here:

Join the free SilverlightShow webcast 'Running Silverlight Outside the Browser and with Elevated Trust'. Sept 7th, 8 am - 9 am PDT.
In this live session Chris Anderson will cover configuring and debugging OOB mode, toast notifications, elevated trust, direct file access and much more.
Learn more | Register | See more webinars (hide this)