为按钮创建自定义形状

我想制作一张显示每个州的地图,当hover在某个状态时,相应的形状会改变颜色,并会出现一些有关它的信息。

这是一个类似于kartograph.org/showcase/usa-projection的基于网络的例子

使用.NET 4.5,C#和WinForms是否可以通过Button和处理鼠标事件来实现这一目标?

这不是一个完整的答案,但可能会让你走上正确的道路。

WinForms不会让你以这种方式使用Button对象; WinForms按钮的自定义能力非常有限 – 如果WPF是一个选项,WPF很可能适合这种情况。

要在WinForms中执行此操作,您可能需要使用GDI并将每个状态加载到它自己的Graphics对象中,并为点击事件等编写自己的管道。 虽然我不能提供具体的例子,但它应该是可行的,但它也可能是相当多的工作(特别是对于诸如图像的透明部分之类的事情)。

但是,如果您要查看WPF或与GDI对象进行交互,您应该能够取得进展。

这个答案完全忽略了你关于创建有趣形状的按钮的问题,而只是处理构建类似于你展示链接的例子:通过点击或用鼠标hover来识别地图上的状态。

识别状态很简单:

如果您可以为每个州分配一种颜色(即使它只是略有不同),您可以使用GetPixel来检查鼠标点击或hover的国家/地区颜色。

如果您不想要可见颜色,您仍然可以使用相同的技巧,只需使用两个重叠的地图并显示顶部地图,同时使用下面的彩色地图作为查找表。

当然,您甚至不需要将查找映射放入控件中; 你可以简单地使用它的Bitmap ,只要它与可见地图的大小相同。

在winforms中不会占用几行代码。 设置状态列表并用颜色填充它们会更有效。

改变它的颜色更加棘手。 我想我会在这里使用一种非常不同的方法:编写一个Floodfill算法非常简单; 维基百科有一些不错的,特别是没有递归的(基于队列)实现起来非常简单。

因此,您可以使用地图的重叠副本,并将鼠标hover在上面填充状态。(为此,您需要确保状态可以填充,即它们已关闭轮廓 。这可能是为它们着色的准备。)

当鼠标移动到不同的状态/颜色时,您将恢复原始地图。

你的例子有一个很好的,有点慢的动画。 这甚至更棘手。 如果你需要它可能WPF真的值得考虑..虽然在Winforms中也可以使用着色动画,也许使用Color MatrixTimer它肯定不是为glitz而构建的。

这是一段至少有一半的代码:

 // simple but effective floodfill void Fill4(Bitmap bmp, Point pt, Color c0, Color c1) { Rectangle bmpRect = new Rectangle(Point.Empty, bmp.Size); Stack stack = new Stack(); int x0 = pt.X; int y0 = pt.Y; stack.Push(new Point(x0, y0) ); while (stack.Any() ) { Point p = stack.Pop(); if (!bmpRect.Contains(p)) continue; Color cx = bmp.GetPixel(pX, pY); if (cx == Color.Black) return; if (cx == SeaColor) return; if (cx == c0) { bmp.SetPixel(pX, pY, c1); stack.Push(new Point(pX, pY + 1)); stack.Push(new Point(pX, pY - 1)); stack.Push(new Point(pX + 1, pY)); stack.Push(new Point(pX - 1, pY)); } } } // create a random color for the test Random R = new Random(); // current and last mouse location Point mouseLoc = Point.Empty; Point lastMouseLoc = Point.Empty; // recognize that we have move inside the same state Color lastColor = Color.White; // recognize the outside parts of the map Color SeaColor = Color.Aquamarine; // start a timer since Hover works only once private void pictureBox1_MouseMove(object sender, MouseEventArgs e) { mouseLoc = e.Location; timer1.Stop(); timer1.Interval = 333; timer1.Start(); } private void timer1_Tick(object sender, EventArgs e) { // I keep the map in the Background image Bitmap bmp = (Bitmap)pictureBox1.BackgroundImage; // have we left the image? if (!new Rectangle(Point.Empty, bmp.Size).Contains(mouseLoc)) return; // still in the same state: nothing to do if (lastColor == bmp.GetPixel(mouseLoc.X, mouseLoc.Y)) return; // a random color Color nextColor = Color.FromArgb(255, R.Next(255), R.Next(255), R.Next(256)); // we've been in the map before, so we restore the last state to white if (lastMouseLoc != Point.Empty) Fill4(bmp, lastMouseLoc, bmp.GetPixel(lastMouseLoc.X, lastMouseLoc.Y), Color.White ); // now we color the current state Fill4(bmp, mouseLoc, bmp.GetPixel(mouseLoc.X, mouseLoc.Y), nextColor); // remember things, show image and stop the timer lastMouseLoc = mouseLoc; lastColor = nextColor; pictureBox1.Image = bmp; timer1.Stop(); } 

你只需要运行它就是PictureBox pictureBox1Timer timer1以及只有3种颜色的地图版本:黑色,白色和海蓝gem。

它会做的是用随机颜色绘制你hover的状态。

接下来的步骤是创建一个包含数字,标题和信息文本的所有州的列表。 然后,您创建地图的第二个版本,您可以使用从州的编号中获得的颜色为每个州着色。

如果将其展开一点,可以使用上面的代码进行着色。

最后,您在Tick事件中编写查找代码以获取要在Tooltip显示的信息。

当然,这是假设您对使用地图作为Bitmap感到满意。 链接源使用SVG文件,其中所有数据都以XML格式存储为矢量数据。 解析这个以获取GraphicsPath Points也是一个选项,然后它将在向量领域中工作。 但我想可能还需要几天才能建成..

我完成的粗略版本包括用于创建颜色映射的代码和用于执行查找的代码。 150行,没有评论。