如何在c#中创建可点击的不规则形状区域

我有一个像心脏或任何随机形状的不规则形状的图片。 我可以在视觉上使它透明,但我需要让它只在形状区域上可点击。 我听说我应该使用“Region”,但我无法弄清楚如何。

我试图搜索所有非空,透明或空的像素并使用它们创建一个点数组,但我无法创建/重塑当前控制区域。 我正在尝试制作一个自定义控件,你可以选择一个按钮或图片,它们是不规则形状并且彼此靠近。

这是我正在处理的事情: 在此处输入图像描述

正如您在图片中看到的,有8个不同的部分(假设右侧和左侧组合)。 正如您所看到的,它们彼此接近,其中一些甚至适合其他人之间的空白区域。

我的目标是,例如,如果我点击Pectorals(图中的红色区域),它将变为彩色版本,并运行一堆其他代码。

问题是,默认情况下,当我们使用PictureBox添加任何Picture时,它将从该边界开始围绕该图片创建一个Rectangle 。 因此,如果我将两张图片(如图所示)放在一起,则一个空区域会阻止我点击另一张图片。

由于这个问题,它也引发了错误对象的ClickEvent

我正在尝试设置“提升事件区域”,我假设我们将Graphic Region称为图像存在的位置。 我可以通过循环收集像素的位置,该循环确定该图片的哪个坐标具有“颜色”(意味着它是图片的一部分,我想要点击的区域)但是我不能用该数据限制该区域。

我想要实现的一个例子: https : //www.youtube.com/watch?v = K_JzL4kzCoE

做这个的最好方式是什么?

这是解决此问题的两种方法:

  • Regions

  • 使用透明Images

第一种方法涉及创建控件,例如PictureBoxesPanels ,它们具有图像的形状 ,并且只能在该形状内单击。

这很好,只要您可以访问构成形状的矢量轮廓

下面是一个示例,它将Panel的可见和可点击Region限制为从跟踪点列表创建的不规则形状的blob:

在此处输入图像描述

 List points = new List(); points.Add(new Point(50,50));points.Add(new Point(60,65));points.Add(new Point(40,70)); points.Add(new Point(50,90));points.Add(new Point(30,95));points.Add(new Point(20,60)); points.Add(new Point(40,55)); using (GraphicsPath gp = new GraphicsPath()) { gp.AddClosedCurve(points.ToArray()); panel1.Region = new Region(gp); } 

不幸的是,从其中包含的点开始制作一个Region是行不通的; 想象一个Region作为矢量形状的列表,它们由点组成,但仅用于创建包含向量,而不是像素。

你可以追踪形状,但这是很多工作,而且不值得。

因此,如果您没有矢量形状:请转到第二种方法:

这将假设您有图像(可能是PNG),这些图像在所有不应接受任何点击的位置都是透明的。

最简单和最有效的方法是将它们与它们所在的点一起列入一个列表; 然后,每当它们发生变化时,将它们全部绘制成一个图像,您可以将其分配给PictureBox.Image

这是一个Mouseclick事件,它将搜索Image列表中最顶层的图像以查找单击的图像。 要将它们与位置组合,我使用元组列表:

 List> imgList = new List>(); 

我们在每个MouseClick搜索此列表:

 private void pictureBox1_MouseClick(object sender, MouseEventArgs e) { int found = -1; // I search backward because I drew forward: for (int i = imageList1.Images.Count - 1; i >= 0; i--) { Bitmap bmp = (Bitmap) imgList[i].Item1; Point pt = (Point) imgList[i].Item2; Point pc = new Point(eX - pt.X, eY - pt.Y); Rectangle bmpRect = new Rectangle(pt.X, pt.Y, bmp.Width, bmp.Height); // I give a little slack (11) but you could also check for > 0! if (bmpRect.Contains(e.Location) && bmp.GetPixel(pc.X, pc.Y).A > 11) { found = i; break; } } // do what you want with the found image.. // I show the image in a 2nd picBox and its name in the form text: if (found >= 0) { pictureBox2.Image = imageList1.Images[found]; Text = imageList1.Images.Keys[found]; } } 

以下是我将图像合并为一个的方法。 请注意,对于测试,我已将它们添加到ImageList对象。 这具有严重的缺点,因为所有图像都缩放到通用尺寸。 您可能想要创建自己的正确列表!

 Bitmap patchImages() { Bitmap bmp = new Bitmap(pictureBox1.ClientSize.Width, pictureBox1.ClientSize.Height); imgList.Clear(); Random R = new Random(1); using (Graphics G = Graphics.FromImage(bmp) ) { foreach (Image img in imageList1.Images) { // for testing: put each at a random spot Point pt = new Point(R.Next(333), R.Next(222)); G.DrawImage(img, pt); // also add to the searchable list: imgList.Add(new Tuple(img, pt)); } } return bmp; } 

我在启动时打电话给它:

 private void Form1_Load(object sender, EventArgs e) { pictureBox1.Image = patchImages(); } 

旁白:这种将所有图像合成为一种图像的方式也是唯一可以让您自由重叠图像的方法。 Winforms不支持重叠控件的真实透明度。并且为每个形状测试一个Pixel (最多)也非常快。

这是一个处理图像蒙版的Winforms示例。 当用户点击掩码图像时,它会弹出一个消息框。 这种基本技术显然可以修改以适应。

 public partial class Form1 : Form { readonly Color mask = Color.Black; public Form1() { InitializeComponent(); } private void pictureBox1_Click(object sender, EventArgs e) { var me = e as MouseEventArgs; using (var bmp = new Bitmap(pictureBox1.Image)) { if (me.X < pictureBox1.Image.Width && me.Y < pictureBox1.Image.Height) { var colorAtMouse = bmp.GetPixel(me.X, me.Y); if (colorAtMouse.ToArgb() == mask.ToArgb()) { MessageBox.Show("Mask clicked!"); } } } } } 

pictureBox1有一个从我自由pictureBox1的心形资源加载的Image

你试过一张图片地图吗?

http://www.w3schools.com/TAGS/tag_map.asp

这应该可以为您提供开始制作地图以覆盖图像所需的内容。