Click here to Skip to main content
15,879,239 members
Articles / Programming Languages / C#

Silverlight - Creating an Image Map with Hotspots

Rate me:
Please Sign up or sign in to vote.
4.53/5 (11 votes)
28 Dec 2009CPOL6 min read 62.4K   923   32   8
An article on how to create an image map with hotspots and attach click event and tooltip to it.

Introduction

Recently, I was working on a Silverlight application, and one of the pages had a graphic. One of the requirements was that the graphic should contain clickable regions (hotspots) and display a dynamic navigation menu when the end users click on it. An image that contains one or more hotspots or clickable areas is called an image map. I have put together a simple tutorial on how to get this done.

Getting Started

An image map can be created by using Microsoft Expression Design or Expression Blend.

  1. If you do not have Microsoft Expression Design, you can download a trial version from here.
  2. If you do not have Microsoft Expression Blend, you can download a trial version from here.

Microsoft Expression Designer

Prepare your favorite image. In this tutorial, I use a map graphic to demonstrate that we can create hotspots in different shapes with Microsoft Expression Designer/ Blend. I have downloaded a random map from here. Follow the steps from below.

  1. Open Microsoft Expression Design 3.
  2. Go to File, New, fill up all the information, and click on the OK button.
  3. Go to View, check Snap to Points and Snap to Pixels.
  4. File, Import Image, and browse for your image and click the Open button.
  5. Double click on Layer1 and rename it to MainImage.

At this point, you should see something like below:

Figure 1

Main Image

Add a new layer at the top of the MainImage layer, highlight it, and select the Pen tool and draw a hotspot around Russia.

Figure 2

New layer

Pen Tool

Russia

Double click Layer 2 and name it Russia or something meaningful so we can utilize it in the Silverlight application. Highlight the points and click on the Properties tab. See below:

Figure 3

Add property

Pick a color you like and set its opacity to 40%.

Figure 4

Set opacity

After you have finished drawing the hotspots, click on File, Export, and copy the setting from below.

Figure 5

Export

Putting everything together

Open Visual Studio 2008, go to File, New, Project, and choose the Silverlight Application template. If you don't have the Silverlight Controls Toolkit, you can download it from here. Make sure you have added the reference for System.Windows.Controls.Toolkit. Open MainPage.xaml and drag a Viewbox control on to the page. This control will stretch the map image inside it proportionally when we resize the browser. Then, copy the content in MapsHotSpot.xaml into the Viewbox content in MainPage.xaml. See below.

Listing 1
XML
<UserControl x:Class="MapsHotspotDemo.MainPage"
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" 
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
    xmlns:controlsToolkit=
      "clr-namespace:System.Windows.Controls;
       assembly=System.Windows.Controls.Toolkit" 
    xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
    xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" 
    mc:Ignorable="d" d:DesignWidth="640" 
    d:DesignHeight="480"> 
  <Grid x:Name="LayoutRoot">
        <controlsToolkit:Viewbox  x:Name="MapsViewbox"  >
<!-- Maps content here -->
           <Canvas 
              xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" 
              xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" 
              x:Name="MapsHotSpot" 
              Width="800" Height="600" 
              Clip="F1 M 0,0L 800,0L 800,600L 0,600L 0,0" 
              UseLayoutRounding="False">
           ...
           </Canvas>
<!-- End Maps content here -->
        </controlsToolkit:Viewbox>
  </Grid>
</UserControl>

Expression Blend

If you choose to do it by using Expression Blend, here are the steps to follow:

  1. Open Microsoft Expression Blend.
  2. Click on File, select New Project, and follow the setup from below.
Figure 6

Blend New Project

Add a Viewbox into the LayoutRoot and name it MapsViewbox, then add a Canvas inside the Viewbox and name it MapsHotspot. After that, add a Canvas to MapsHotspot and an Image control to the Canvas. From the Image control properties, specify the image source and setup the appropriate width and height of the image. Then, create another Canvas and name it Russia or something meaningful or unique. Make sure that the width and height of the newly created Canvas match the image in order to draw points on it. Click on the Pen tool and draw points or vertices around Russia. Pick a brush color and set its opacity to certain percentages. Repeat the same procedure for the rest of the countries.

Figure 7

Blend Hot Spot

Tooltips

Remember that we named each layer at the beginning. The layers and points are translated into Canvas and Path objects, respectively, when we export them into XAML. We can provide each layer with a unique name, or include a tag attribute to it and use it to pull the country information from a database or resource dictionaries. For simplification sake, we will have a method to loop through each child element in the MapsHotSpot, grab the name, and set it to the tooltip content. Attach a MouseMove event to each layer to highlight the country on mouse hover. Also, attach a MouseLeftButtonUp event to pop up a menu when the left mouse button is released. See Listing 2.

Listing 2
C#
foreach (Canvas c in (this.FindName("MapsHotSpot") as Canvas).Children)
{
    if (!string.IsNullOrEmpty(c.Name))
    {
        c.Cursor = Cursors.Hand;
        ToolTip toolTip = new ToolTip { Content = c.Name };
        c.SetValue(ToolTipService.ToolTipProperty, toolTip);
        c.MouseMove += new MouseEventHandler(c_MouseMove);
        c.MouseLeftButtonUp += new MouseButtonEventHandler(c_MouseLeftButtonUp);
    }
}

Displayed below is the implementation of the c_MouseMove method. This method will highlights the Canvas/ country and clears the previous selection, if there is any, during the mouse hover event.

Listing 3
C#
void c_MouseMove(object sender, MouseEventArgs e)
{
    Canvas c = sender as Canvas;
    ResetLastSelected();

    if (!string.IsNullOrEmpty(c.Name))
    {
        lastSelected = c.Name;
        SetCanvasColor(c, Color.FromArgb(255, 92, 112, 171), 2, Colors.Green);
    }
}

View in browser, you should see something like below. If you resize the browser, you will still see the full map. Place the mouse over the map image to see the tooltip and the highlighted region.

Figure 8

Preview

We will build a simple menu with several links in it. First, add a simple class to hold the link properties. See below for an example.

Listing 4
C#
public class Links
{
    public string Title { get; set; }
    public string URL { get; set; }

    public Links(string t, string u)
    {
        Title = t;
        URL = u;
    }
}

After that, create a global List (T) class to hold the link objects, and populate the link class with the below data on page load.

Listing 5
C#
private List<Links> _links = new List<Links>();

void SetupLinks()
{
    _links.Add(new Links("About the people", 
               "http://www.countryreports.org/{0}.aspx"));
    _links.Add(new Links("Economy", 
               "http://www.economicexpert.com/a/{0}.htm"));
    _links.Add(new Links("Global Statistics", 
               "http://www.geohive.com/cntry/{0}.aspx"));
    _links.Add(new Links("Population", 
               "http://www.geohive.com/charts/population1.aspx"));
    _links.Add(new Links("Wiki", 
               "http://en.wikipedia.org/wiki/{0}"));
}

Below is the implementation of the c_MouseLeftButtonUp method. This method will bring up the popup menu by calling the PopulateContextMenu method in response to the mouse left button click event. The PopulateContextMenu takes a country name as an argument, loops through the list of link objects, and concatenates the country name to it. The purpose of the PositionContextMenu method is to set the popup menu position. I also added animation to fade in and fade out the popup menu, please refer to MainPage.xaml.

Listing 6
C#
void c_MouseLeftButtonUp(object sender, MouseButtonEventArgs e)
{
    Canvas c = sender as Canvas;
    if (!string.IsNullOrEmpty(c.Name))
    {
        PopulateContextMenu(c.Name);
        PositionContextMenu(e.GetPosition(
                 contextMenu.Parent as UIElement), true);
        e.Handled = true;
    }
}

void PositionContextMenu(Point p, bool useTransition)
{
    if (useTransition)
        contextMenu.IsOpen = false;
    contextMenu.HorizontalOffset = p.X;
    contextMenu.VerticalOffset = p.Y;
    contextMenu.IsOpen = true;
}

void PopulateContextMenu(string country)
{
    contextListBox.Items.Clear();

    foreach (Links l in _links)
    {
        HyperlinkButton hlb = new HyperlinkButton();
        hlb.Content = l.Title;
        hlb.NavigateUri = new Uri(string.Format(l.URL, country));
        hlb.TargetName = "_blank";

        contextListBox.Items.Add(hlb);
    }   
}

At this point, you should see something like below:

Figure 9

Mouse click

The final piece is to hide the popup menu and clear the highlighted country once we click on the non- clickable area of the map image. In order to achieve that, we can attach a MouseLeftButtonUp event to the layout grid on page load. See below.

Listing 7
C#
LayoutRoot.MouseLeftButtonUp += 
   new MouseButtonEventHandler(LayoutRoot_MouseLeftButtonUp);

void LayoutRoot_MouseLeftButtonUp(object sender, 
                MouseButtonEventArgs e)
{      
    ResetLastSelected();
    HideMenu();
}

void HideMenu()
{
    HidePopup.Begin();
    contextMenu.HorizontalOffset = -50.0;
}

Points of Interest

Initially, I was planning to use the right mouse button click event class that I found from here to trigger the popup menu, but I decided to leave it out after reading an article from here about its disadvantages.

I found this article useful although it was written in VB.NET and I can't get it to compile, but I'm able to utilize the logic embedded in it.

Conclusion

If you find any bugs or disagree with the contents, please drop me a line and I'll work with you to correct it. I would suggest downloading the demo and exploring it in order to grasp the full concept of it because I might have left out some useful information. I hope someone will find this article useful on how to create image hotspots or map on images.

Resources

License

This article, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)


Written By
Software Developer (Senior)
United States United States
I have over 10 years of experience working with Microsoft technologies. I have earned my Microsoft Certified Technology Specialist (MCTS) certification. I'm a highly motivated self-starter with an aptitude for learning new skills quickly.

Comments and Discussions

 
GeneralMy vote of 5 Pin
Manoj Kumar Choubey17-Feb-12 21:02
professionalManoj Kumar Choubey17-Feb-12 21:02 
Generalvote 4 Pin
shaheen_mix17-Dec-11 0:08
shaheen_mix17-Dec-11 0:08 
GeneralCant find Viewbox in Toolkit [modified] Pin
awesomeAkki9-Apr-11 12:28
awesomeAkki9-Apr-11 12:28 
Hello I did all the steps exaclty as given in the article but its neither showing any tooltip nor it is firirng events like mousemove and mouseleft button up for any canvas. Below is the code of my MainPage.xaml. Please help urgent.

<navigation:Page x:Class="ForImage.Page2"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlnsBig Grin | :-D ="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
mc:Ignorable="d"
xmlns:navigation="clr-namespace:System.Windows.Controls;assembly=System.Windows.Controls.Navigation"
d:DesignWidth="640" d:DesignHeight="480"
Title="Page2 Page">
<Grid x:Name="LayoutRoot">
<Grid.RowDefinitions>
<RowDefinition Height="*"></RowDefinition>
<RowDefinition Height="20"></RowDefinition>
</Grid.RowDefinitions>
<Viewbox Grid.Row="0" Height="372" HorizontalAlignment="Left" Margin="10,10,0,0" Name="viewbox1" VerticalAlignment="Top" Width="618">
<Canvas xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" x:Name="ServiceImageNew" Width="1024" Height="768" Clip="F1 M 0,0L 1024,0L 1024,768L 0,768L 0,0" UseLayoutRounding="False">
<Canvas x:Name="ESPDelivery" Width="1024" Height="768" Canvas.Left="0" Canvas.Top="0">
<Path x:Name="Path" Width="180.437" Height="181.85" Canvas.Left="173.511" Canvas.Top="437.665" Stretch="Fill" Fill="#67FFFFFF" Data="F1 M 352,503L 320.016,455.811C 310.32,453.416 297.833,446.216 288.956,440.923C 288.525,440.665 262.359,437.201 260.009,437.717C 246.408,440.706 221.67,447.691 220.474,448.523C 214.394,452.751 192.532,471.964 191.984,472.922C 188.805,478.485 178.902,496.919 176.185,503.682C 175.345,505.772 173.536,531.272 173.511,537.029C 173.499,539.587 183.722,572.254 184.707,573.552C 189.482,579.847 211.71,603.903 211.853,603.971C 224.767,610.091 246.389,619.206 250.619,619.404C 258.27,619.763 278.169,619.185 280.094,618.556C 288.974,615.656 308.562,607.185 319.263,601.506C 320.807,600.686 327.27,595.426 327.778,594.726C 333.528,586.803 345.957,567.315 350.946,557.565C 351.844,555.81 352.501,554.371 352.834,553.371C 355.759,544.601 352,502 352,503 Z "/>
</Canvas>
<Canvas x:Name="Services" Width="1024" Height="768" Canvas.Left="0" Canvas.Top="0">
<Path x:Name="Path_0" Width="304.643" Height="302.677" Canvas.Left="348.011" Canvas.Top="240.794" Stretch="Fill" Fill="#67FFFFFF" Data="F1 M 461.089,244.087L 533.063,240.794L 544.172,244.764L 580.953,260.253L 591.808,268.281L 623.051,297.006L 633.365,312.861L 650.35,354.859L 652.654,369.955L 648.667,433.029L 642.843,449.975C 632.974,465.227 623.261,480.232 613.76,494.903C 612.193,497.324 577.97,519.632 558.068,532.167C 556.742,533.003 549.71,536.685 546.417,537.717C 538.302,540.258 482.056,543.741 480.554,543.455C 471.465,541.721 451.41,537.386 448.438,535.867C 434.444,528.718 407.017,512.951 398.845,507.119C 391.936,502.189 367.554,465.539 358.929,449.972C 356.626,445.815 348.015,411.005 348.012,409.988C 347.955,389.43 351.375,362.443 356.16,338.982C 356.273,338.428 363.112,321.822 364.209,320.178C 370.137,311.305 390.402,283.667 391.155,283.099C 405.411,272.338 422.519,260.21 434.616,252.429C 435.02,252.169 456.212,244.518 461.089,244.087 Z "/>
</Canvas>
<Canvas x:Name="GeneralResources" Width="1024" Height="768" Canvas.Left="0" Canvas.Top="0">
<Path x:Name="Path_1" Width="178.949" Height="182.923" Canvas.Left="644.514" Canvas.Top="437.061" Stretch="Fill" Fill="#67FFFFFF" Data="F1 M 803.832,468.942L 776.428,447.554L 735.929,437.061L 698.972,445.022L 686.043,451.98L 661.004,475.054L 653.218,488.108L 644.514,512.129L 645.566,555.726L 662.013,584.989L 667.146,590.805L 692.98,611.625L 735.95,619.984L 777.914,609.81L 800.839,591.713C 805.934,586.052 822.878,554.85 823.036,552.393C 823.84,539.917 824.495,508.565 815.218,484.815C 814.599,483.232 806.469,471.404 803.832,468.942 Z "/>
</Canvas>
<Canvas x:Name="ESPProgram" Width="1024" Height="768" Canvas.Left="0" Canvas.Top="0">
<Path x:Name="Path_2" Width="180.911" Height="180.267" Canvas.Left="411.286" Canvas.Top="38.9065" Stretch="Fill" Fill="#67FFFFFF" Data="F1 M 487.672,39.1724C 489.442,39.0722 491.225,38.9861 493.006,38.9139C 499.64,38.6448 539.07,45.8216 543.527,48.8205C 554.311,56.0751 565.553,64.407 575.256,72.8611C 576.629,74.0579 589.864,105.677 590.978,110.827C 593.03,120.309 592.09,142.859 591.076,146C 587.98,155.588 584.211,165.896 580.19,175.042C 579.643,176.286 575.911,181.856 571.608,187.623C 567.695,192.869 547.655,208.663 544.009,209.728C 533.918,212.676 518.005,216.134 502.982,219.121C 501.296,219.456 489.431,218.142 479.505,216.77C 472.395,215.788 460.434,210.726 460.279,210.614C 450.618,203.655 431.588,187.422 430.537,185.564C 425.195,176.118 419.938,166.02 415.247,156.434C 414.86,155.643 412.996,145.208 411.342,134.667C 411.151,133.451 411.47,118.068 412.033,115.885C 415.405,102.815 422.487,84.0891 425.596,80.4343L 446.854,55.916L 487.672,39.1724 Z "/>
</Canvas>
<Canvas Width="1024" Height="768" Canvas.Left="0" Canvas.Top="0">
<Image MouseMove="Image_MouseMove" x:Name="Image" Source="/ForImage;component/eslnDesigns-03_Images1/Image3.png" Width="805" Height="681" Canvas.Left="0" Canvas.Top="0">
<Image.RenderTransform>
<TransformGroup>
<MatrixTransform Matrix="1,0,0,1,97.5006,13.5015"/>
</TransformGroup>
</Image.RenderTransform>
</Image>
</Canvas>
</Canvas>
</Viewbox>
<TextBlock Grid.Row="1" Name="txt">HI</TextBlock>
</Grid>

</navigation:Page>

modified on Sunday, April 10, 2011 3:36 AM

GeneralPutting Everything Together Pin
Julian Yorke13-Mar-11 2:29
Julian Yorke13-Mar-11 2:29 
GeneralMy vote of 4 Pin
IvoYueh27-Oct-10 10:27
IvoYueh27-Oct-10 10:27 
GeneralMouseEnter instead of MouseMove Pin
IvoYueh27-Oct-10 10:21
IvoYueh27-Oct-10 10:21 
GeneralProgramatically highlighting a country Pin
Kjartan9319-Jan-10 4:47
Kjartan9319-Jan-10 4:47 
GeneralRe: Programatically highlighting a country Pin
Kjartan9319-Jan-10 4:54
Kjartan9319-Jan-10 4:54 

General General    News News    Suggestion Suggestion    Question Question    Bug Bug    Answer Answer    Joke Joke    Praise Praise    Rant Rant    Admin Admin   

Use Ctrl+Left/Right to switch messages, Ctrl+Up/Down to switch threads, Ctrl+Shift+Left/Right to switch pages.