为按钮创建自定义形状

时间:2015-02-06 19:49:26

标签: c# .net winforms

我想创建一个显示每个状态的地图,当悬停在某个状态时,相应的形状会改变颜色,并会显示有关它的一些信息。

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

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

2 个答案:

答案 0 :(得分:2)

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

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

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

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

答案 1 :(得分:0)

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

确定状态很简单:

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

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

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

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

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

因此,您可以使用地图的重叠副本,并将鼠标悬停在其上的状态进行填充..(为此,您需要确保状态可以被填充,即它们有封闭的轮廓。这可能是着色它们的准备工作的一部分。)

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

你的例子有一个很好的,有点慢的动画。这将更加棘手。如果你需要,那么WPF可能真的值得考虑..尽管Winforms中也可以使用着色动画,也许有一个Color Matrix和一个Timer它肯定不是&// simple but effective floodfill void Fill4(Bitmap bmp, Point pt, Color c0, Color c1) { Rectangle bmpRect = new Rectangle(Point.Empty, bmp.Size); Stack<Point> stack = new Stack<Point>(); 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(p.X, p.Y); if (cx == Color.Black) return; if (cx == SeaColor) return; if (cx == c0) { bmp.SetPixel(p.X, p.Y, c1); stack.Push(new Point(p.X, p.Y + 1)); stack.Push(new Point(p.X, p.Y - 1)); stack.Push(new Point(p.X + 1, p.Y)); stack.Push(new Point(p.X - 1, p.Y)); } } } // 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 pictureBox1

您需要运行的只有Timer timer1Tick以及只有3种颜色的地图版本:黑色,白色和海蓝宝石。

它将做的是以随机颜色绘制悬停的状态。

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

如果稍微扩展一下,可以使用上面的代码进行着色。

最后,您在Tooltip事件中编写查询代码,以便在Bitmap显示信息..

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

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