Thursday, April 3, 2008

Deep Zoom - Highlighting the clicked SubImage

Having played with the Hard Rock demo extensively, I noticed that it is a little difficult to see which image is associated with the displayed metadata. In general, it is the centered image but it could get confusing sometimes. This could be alleviated if there is a highlight or a border around the image.

It's not that hard actually...

1. Changes to the XAML
<Grid x:Name="LayoutRoot">
<RowDefinition Height="*"/>
<RowDefinition Height="5"/>
<RowDefinition Height="30"/>
<RowDefinition Height="30"/>

<Border x:Name="msiBorder" Grid.Row="0" BorderThickness="1" BorderBrush="Black">
<MultiScaleImage x:Name="msi" UseSprings="false" Margin="2,2,2,2"/>
<Canvas Grid.Row="0" Margin="3,3,2,2">
<Border x:Name="imageBorder" BorderThickness="2" BorderBrush="Black" Opacity="0" />


2. The code to show and hide the highlight on a image

public void ShowImageHighlight(int nSubImageIndex)
{ // Hightlight the image by drawing a border around it

int borderWidth = 2; // Setting the default border width to 2
if (msi.ViewportWidth < 1) // This adjusts for zoomed images where a border of 2 is insufficient
borderWidth = (int) (borderWidth / msi.ViewportWidth);

Rect borderRect = ExpandRect(msiLogicalToElementRect(GetSubImageRect(nSubImageIndex)), borderWidth);

imageBorder.Width = borderRect.Width;
imageBorder.Height = borderRect.Height;
imageBorder.SetValue(Canvas.LeftProperty, borderRect.X);
imageBorder.SetValue(Canvas.TopProperty, borderRect.Y);
imageBorder.BorderThickness = new Thickness(borderWidth);
imageBorder.Opacity = 1.0;

public void HideImageHighlight()
imageBorder.Opacity = 0.0;

Rect msiLogicalToElementRect(Rect rect)
return new Rect(msi.LogicalToElementPoint(new Point(rect.Left, rect.Top)),
msi.LogicalToElementPoint(new Point(rect.Right, rect.Bottom)));

Rect ExpandRect(Rect rect, int expandBy)
return new Rect(rect.Left - expandBy, rect.Top - expandBy, rect.Width + expandBy*2, rect.Height + expandBy*2);

3. The hooks to invoke the above code

int _clickedImageIndex = -1;

msi.MotionFinished += (sender, e) => { if (_clickedImageIndex >= 0) ShowImageHighlight(_clickedImageIndex); };

msi.MouseLeftButtonDown += (sender, e) => { _clickedImageIndex = -1; };
new MouseWheelHelper(this).Moved += (sender, e) => { _clickedImageIndex = -1; };

msi.MouseLeftButtonUp += delegate(object sender, MouseButtonEventArgs e)
if (mouseButtonPressed && !dragInProgress)
Point p = this.msi.ElementToLogicalPoint(e.GetPosition(this.msi));
int subImageIndex = SubImageHitTest(p);
if (subImageIndex >= 0) {
_clickedImageIndex = subImageIndex;

bool shiftDown = (Keyboard.Modifiers & ModifierKeys.Shift) == ModifierKeys.Shift;
if (shiftDown) Zoom(0.5, e.GetPosition(this.msi));
else Zoom(2.0, e.GetPosition(this.msi));
mouseButtonPressed = false;
dragInProgress = false;

I updated the previous example Dissecting Hard Rock Memorabilia and Silverlight Deep Zoom - Part 7 with this code so you can see how it works!


Jeremy Sharp said...

Do you have any plans to package and post your Deep Zoom project?


Wilfred Pinto said...


I would love to post my project, after cleanup of course :-), but I don't have access to Visual Studio yet. I hate to plunk down $500+ for something that I am currently doing as a hobby. I even looked at the empower for isv program but I can't justify joining that program since I am not going to create commercial software. I just want to play around with Silverlight and .Net and pass on my findings to the community. Hopefully I will make a career out of it later since I really love this technology!

For now, Notepad++ and Silverlight SDK works well for my experiments, but I really need VS 2008 for packaging and posting the project.

If anyone wants access to the entire project as is I will gladly email/ftp it to you. Just leave a comment with your email id and I will send it to you. All personal comments/requests like this will be deleted so that they don't make it to the blog.

Chris said...

I'm echoing all the comments you've received about your blog being a tremendous resource. Your project here has been great for helping me develop my own Deep Zoom project.

I'm curious if you've noticed the bug in the image highlighting code once filtering has been applied. The highlight appears in an odd place depending on where in the picture you click.

I've only just now noticed this, and thought I'd post about it here in case you weren't already aware of it. If I come up with anything, I'll be sure to let you know.

Again, many thanks for your contribution to Silverlight development community!

Wilfred Pinto said...

Thanks Chris for the kind words but more thanks for pointing out the bug. I hadn't noticed it before but then again I don't really test the stuff that I write! :) I will definitely look into it when I get some time.

If you do find more bugs please post them on this blog so that others benefit from this.

Wilfred Pinto said...


The highlighting bug after filtering is due to the fact that I am working with msi.SubImages (the entire collection) instead of _imagesToShow (the filtered collection).

If you change msi.SubImages to _imagesToshow in these following methods, it should work. I haven't tried it myself, so let me know if you try it and there are any issues.

int SubImageHitTest(Point p)
Rect GetSubImageRect(int indexSubImage)
public void ShowImageHighlight(int nSubImageIndex)
Rect msiLogicalToElementRect(Rect rect)
Rect ExpandRect(Rect rect, int expandBy)

Hope this helps!

Wilfred Pinto

Jyotsna said...

Very impressive!
Is there a way to highlight an the image on mouse over?
Can you please help me achieve this.
Thanks for your help.


Jyotsna said...

Can you please help me out to highlight the image on mouse hover.

Oran said...


This blog will help you highlight an image on mouse over...

See the section titled "Mapping SubImages to Element Coordinates "