I would appreciate any suggestions on best approaches to take to the following problem:
I would like to have a form in a VB.Net windows app that contains a map where each state/country/region acts like a button. The regions could change colors and trigger events when clicked, mouse-overed, etc.
The main challenge is that my regions are very irregularly shaped, and they must touch but not overlap. Is there an easy way to take a .bmp or .jpg image of a region and "convert" it into a button somehow? I have seen some code for creating shaped buttons using a GraphicsPath object and a series of points, but with over 700 regions on my map, I'd like to avoid that level of effort if there is an alternative.
You're talking about something fairly complex so if you're looking for an easy solution, it's going to be less than optimal. If I were doing something like this I would probably define the regions beforehand and store them in an array. When the user clicks the map just iterate through the array until you get the region containing the pixel that was clicked.
You might also consider a mousemap version of your map image. Essentially, you need to take your map and fill each territory with its own color. When the user clicks on a territory, use GetPixel (with mouse x & y coords) on your mousemap image to get the color. 'Course, then you have to figure out how to convert each color into some sort of unique identifier for each particular region.
Thanks for the suggestions. The mousemap idea is neat but doesn't seem to get me to the point where I could change the color of the regions on the map on the fly, the way I could if they were separate objects.
The alternative Machaira suggested seems to involve defining each area on my map as a region, which would also be a necessary prerequisite for setting up each area as a shaped button. I prefer the shaped button route if it is workable, because then each area on the map is a separate object with properties like color that can be manipulated.
I am having some difficulty adapting the code to my use. The line that is giving me particular trouble is this one:
Code:
' Make the button fit the region.
Button1.SetBounds(Button1.Location.X, _
Button1.Location.Y, pts(3).X + 5, pts(4).Y + 5)
I understand that this line is setting the X, Y, Width, and Height of the button, but how it is setting the width and height doesn't make sense to me. The pts variable contains a set of X,Y coordinates defining the "corners" of the region. I don't get why the code is picking the 3rd X coordinate or the 4th Y coordinate.
If I could understand that line, I think I could use this code to create buttons shaped like the areas on my map and place them on the map in the appropriate locations. The sample code works fine as written (creating an arrow shaped button) but doesn't seem to work as soon as I start fiddling with the coordinates stored in the pts variable.
If somebody could explain to me how that line of code works and how I could tweak it to work with any set of coordinates, I would be much obliged.
The last two params set the width and height respectively of the bounds. Those points in the array are the widest and tallest. I'm probably not explaining well so I hope this helps.
Thanks, Machaira, that did help. I got the code figured out so that it will draw any shaped button for me as long as I feed it a hard coded list of points.
The next step that is giving me trouble is modifying the code so that the point coordinates are loaded from a database (stored in a table called Corners) instead of being hard coded. Here is what I have so far:
Code:
Dim pts As Point
Dim c As Integer
Dim Pointx As Integer
Dim Pointy As Integer
OleDbDataAdapter1.Fill(DsCorners1)
For c = 0 To DsCorners1.Corners.Rows.Count() - 1
Pointx = DsCorners1.Corners.Rows(c).Item("X Value")
Pointy = DsCorners1.Corners.Rows(c).Item("Y Value")
Next
As you can see, the piece I am missing is the code that would take the values in the Pointx and PointY variables and add them as an X,Y pair to my pts variable. The code would need to add rather than overwrite since the for loop will cycle until it runs out of "corners" and I don't know in advance how many corners there will be.
Okay, I figured it out. My confusion stemmed from the fact that a points variable requires only one dimension to store its points, even though each point contains within it an X and Y value. Here is the code I used in case anyone is interested.
Code:
CornerCount = DsCorners1.Corners.Rows.Count
Dim pts(CornerCount) As System.Drawing.Point
For c = 0 To CornerCount - 1
Pointx = DsCorners1.Corners.Rows(c).Item("X Value")
Pointy = DsCorners1.Corners.Rows(c).Item("Y Value")
pts(c) = New Point(Pointx, Pointy)
Next
pts(CornerCount) = pts(0)
DsCorners1 is the data table storing the corners for this region. I actually have this routine working to the point where it will create separate, shaped buttons for all the regions on my map. It will do that, at least, once I get all the X and Y coordinates for every corner of every region entered into my database.
If anyone wants to see the complete routine I use to generate these buttons let me know. Otherwise, I think this case is closed.
The ASP.NET 2.0 Anthology
101 Essential Tips, Tricks & Hacks - Free 156 Page Preview. Learn the most practical features and best approaches for ASP.NET. subscribe