我想制作一个显示每个状态的地图,当将鼠标悬停在某个状态上时,相应的形状会改变颜色并且会显示一些关于它的信息。为按钮创建自定义形状
下面是类似的东西 kartograph.org/showcase/usa-projection
使用.NET 4.5,C#基于Web的例子,和WinForms是否有可能与巴顿和处理鼠标事件的实现这一目标?
我想制作一个显示每个状态的地图,当将鼠标悬停在某个状态上时,相应的形状会改变颜色并且会显示一些关于它的信息。为按钮创建自定义形状
下面是类似的东西 kartograph.org/showcase/usa-projection
使用.NET 4.5,C#基于Web的例子,和WinForms是否有可能与巴顿和处理鼠标事件的实现这一目标?
这不是一个完整的答案,但可能会让你走上正确的道路。
WinForms不会让你以这种方式使用Button
对象; WinForms按钮的定制能力非常有限 - 如果它是一个选项,WPF可能会适用于此。
要在WinForms中执行此操作,您可能需要使用GDI并将每个状态加载到它自己的Graphics对象中,并为Click事件等编写自己的管道。虽然我不能提供一个具体的例子,但它应该是可行的,但它也可能是一个相当大的工作量(特别是对于诸如图像的透明部分之类的东西)。但是,如果您要么查看WPF,要么查看与GDI对象的交互,您应该可以取得进展。
我并不想要一个特别的按钮,但一些工作..我想我会采取你的建议和使用WPF,但我必须先熟悉它自己..感谢您的帮助 – 2015-02-06 20:37:11
这个答案完全忽略了关于创建具有滑稽形状的按钮的问题,而只处理了构建类似于您显示的链接的示例:通过单击或悬停鼠标来标识地图上的状态。
要识别的状态很简单:
如果你可以指定每个状态的颜色(即使它仅仅是非常略有不同),你可以使用GetPixel
来检查哪个国家/彩色鼠标点击或为悬停..
如果您不想看到可见的颜色,您仍然可以使用相同的技巧,只需使用两个叠加的地图并显示顶部地图,同时使用下面的彩色地图作为查找表。
当然,你甚至不需要把查找映射到控件中;只要它与可见地图尺寸相同,就可以简单地使用它的Bitmap
。
在winforms中不会占用更多的代码行。设置状态列表并填充颜色将会有更多的工作。
改变颜色比较棘手。我想我会在这里使用一个相当不同的方法:编码floodfill算法非常简单; wikipedia有几个很好的,特别是没有递归(基于队列)的实现非常简单。
所以,你可以使用地图的覆盖副本,floodfill状态鼠标悬停在..(对于这个工作,你需要确保状态可以被floodfilled,即他们有封闭轮廓,这可能是他们着色的准备工作的一部分。)
当鼠标移动到不同的国家/颜色,你会恢复原来的地图..
你举的例子有一个很好的,如果有点慢,动画。这将更加棘手。如果你需要,也许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 timer1
,并且只有3种颜色地图的版本:黑色,白色和海蓝宝石。
它会做的是画一个随机颜色悬停的状态。
您的下一步是创建一个包含数字,标题和信息文本的所有状态列表。然后,您创建第二个版本的地图,使用从州编号中派生出的颜色为每个州的颜色着色。
如果稍微扩大一点,可以使用上面的代码着色。
最后你的代码在Tick
事件的查找来获得信息,以在Tooltip
显示..
当然,这是假设你是满意与地图为Bitmap
工作。要链接的源使用SVG
文件,其中所有数据都以XML
格式存储为矢量数据。解析这个得到Points
为GraphicsPath
也是一个选项,然后将在矢量领域工作。但我想这可能需要几天的时间才能建立..
我完成,粗糙的版本,包括创建颜色映射的代码和执行查找的代码进来ca. 150行,没有评论。
你在用什么?的WinForms? WPF? – Omada 2015-02-06 19:51:29
欢迎来到StackOverflow,很难根据你的问题提供答案。你究竟想达到什么目的?你使用WPF,WinForms,网页吗?什么框架版本 - 你尝试过什么? – STW 2015-02-06 19:52:05
在什么情况下的按钮?一个WPF或Windows商店应用程序?使用混合来追踪一个PNG的形状。 – OakNinja 2015-02-06 19:52:41