Define map hotspots in C#

[hotspots]

The example Display a scalable map with hotspots in C# uses a list of hotspots defined by the following code.

// The hotspots.
private List Hotspots = new List();
...
// Prepare the map for first viewing.
private void Form1_Load(object sender, EventArgs e)
{
    // Initialize the hotspots.
    Hotspots.Add(new Rectangle(88, 509, 22, 22));
    Hotspots.Add(new Rectangle(140, 577, 20, 20));
    Hotspots.Add(new Rectangle(161, 609, 20, 20));
    ...
    Hotspots.Add(new Rectangle(1234, 1076, 16, 18));
    ...
}

So how do you come up with the definitions for the hotspots? One way would be to carefully measure the map image and figure out where the hotspots should be, but this program provides an easier way.

The program includes a Data menu that holds a Make Hotspot item. When you trigger this item, you can click and drag to define a hotspot. The program then writes the code needed to define that hotspot in the Output window as shown in the picture above. You can then copy and paste that code into the program to create new hotspots.

To make using the program in “normal” mode easier, I set the Data menu’s Visible property to False at design time so you normally won’t see that menu.

Strangely enough, even though the Data menu is hidden, you can still access the Make Hotspot item by pressing its shortcut Ctrl+H. If you want to prevent the menu item from being usable even though its not visible, you can set its Enabled property to False. Or even better, you could have the form’s Load event handler set the menu item’s Enabled property equal to the Data menu’s Visible property.

The following code executes when you select the Data menu’s Make Hotspot command.

// Display a hotspot definition in the Output window.
private void mnuMakeHotspot_Click(object sender, EventArgs e)
{
    picMap.MouseMove -= picMap_MouseMove;
    picMap.MouseClick -= picMap_MouseClick;
    picMap.MouseDown += makeHotspot_MouseDown;
    picMap.Cursor = Cursors.Cross;
}

This code prepares the program to create a hotspot. First it removes the MouseMove and MouseClick event handlers that normally deal with the user moving over and clicking a hotspot. It then installs a new MouseDown event handler to start creating a hotspot. It also changes the PictureBox control’s cursor to a crosshair.

The following code shows the new event handler that executes when the user presses the mouse down.

// Begin defining a hotspot.
private Point HotspotStart, HotspotEnd;
private Bitmap HotspotBm;
private Graphics HotspotGr;
private void makeHotspot_MouseDown(object sender, MouseEventArgs e)
{
    HotspotStart = e.Location;
    picMap.MouseDown -= makeHotspot_MouseDown;
    picMap.MouseMove += makeHotspot_MouseMove;
    picMap.MouseUp += makeHotspot_MouseUp;

    // Get ready to draw a selection rectangle.
    HotspotBm = (Bitmap)Map.Clone();
    HotspotGr = Graphics.FromImage(HotspotBm);
    picMap.Image = HotspotBm;
}

This code saves the initial mouse location in the variable HotspotStart. It removes the MouseDown event handler and installs MouseMove and MouseUp event handlers. It finishes by creating a temporary copy of the original image, making a Graphics object associated with the image, and displaying the image.

When the user moves the mouse, the following code executes.

// Draw a selection rectangle.
private void makeHotspot_MouseMove(object sender, MouseEventArgs e)
{
    // Save the new point.
    HotspotEnd = e.Location;

    // Draw the selection rectangle.
    HotspotGr.DrawImage(Map, 0, 0);
    float x = Math.Min(HotspotStart.X, HotspotEnd.X) * MapScale;
    float y = Math.Min(HotspotStart.Y, HotspotEnd.Y) * MapScale;
    float wid = Math.Abs(HotspotStart.X - HotspotEnd.X) * MapScale;
    float hgt = Math.Abs(HotspotStart.Y - HotspotEnd.Y) * MapScale;
    using (Pen thin_pen = new Pen(Color.Red, 1 * MapScale))
    {
        thin_pen.DashStyle = DashStyle.Dash;
        HotspotGr.DrawRectangle(thin_pen, x, y, wid, hgt);
    }

    picMap.Refresh();
}

This code saves the mouse’s new location in the variable HotspotEnd. It then copies the original map onto the temporary image and draws a rectangle to show the area currently selected.

The following code executes when the user releases the mouse.

// Finish defining a hotspot.
private void makeHotspot_MouseUp(object sender, MouseEventArgs e)
{
    // End hotspot definition mode.
    picMap.MouseMove -= makeHotspot_MouseMove;
    picMap.MouseUp -= makeHotspot_MouseUp;
    picMap.MouseMove += picMap_MouseMove;
    picMap.MouseClick += picMap_MouseClick;
    picMap.Image = Map;
    picMap.Cursor = Cursors.Default;
    picMap.Refresh();

    // Display the hotspot definition.
    float x = Math.Min(HotspotStart.X, HotspotEnd.X) * MapScale;
    float y = Math.Min(HotspotStart.Y, HotspotEnd.Y) * MapScale;
    float wid = Math.Abs(HotspotStart.X - HotspotEnd.X) * MapScale;
    float hgt = Math.Abs(HotspotStart.Y - HotspotEnd.Y) * MapScale;
    Console.WriteLine(
        "    Hotspots.Add(new Rectangle({0}, {1}, {2}, {3}));",
        (int)x, (int)y, (int)wid, (int)hgt);
}

This code uninstalls the hotspot MouseMove and MouseUp event handlers and reinstalls the original MouseMove and MouseClick event handlers. It resets the map image and the cursor, and redisplays the map.

The code finishes by displaying the code needed to create this hotspot in the Output window.

One modification you might want to make would be to store hotspot data in objects other than Rectangle structures. For example, you could create a Hotspot class that holds a hotspot’s location plus a name, description, or other information. In that case, you could modify the makeHotspot_MouseUp method so it prompts you for that other information and then uses it to generate the code needed to create a new Hotspot object.


Download Example   Follow me on Twitter   RSS feed   Donate




This entry was posted in algorithms, graphics, mathematics and tagged , , , , , , , , , , , , , . Bookmark the permalink.

Leave a Reply

Your email address will not be published. Required fields are marked *