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

İsm*_*rış 6 c# controls transparency region winforms

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

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

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

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

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

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

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

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

我正在努力实现的一个例子:https://www.youtube.com/watch?v = K_JzL4kzCoE

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

TaW*_*TaW 5

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

  • 与...合作Regions.

  • 使用透明Images.

第一种方法涉及创建控件,例如PictureBoxes或者Panels,具有图像的形状并且仅可在该形状内单击.

如果您可以访问构成形状的矢量轮廓,这很好.

这里是制约可见及可点击的一个例子RegionPanel,从跟踪点列表中创建一个不规则形状的斑点:

在此输入图像描述

List<Point> points = new List<Point>();
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);
}
Run Code Online (Sandbox Code Playgroud)

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

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

所以如果你没有矢量形状:去第二种方法:

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

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

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

List<Tuple<Image, Point>> imgList = new List<Tuple<Image, Point>>();
Run Code Online (Sandbox Code Playgroud)

我们在每个列表中搜索此列表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(e.X - pt.X, e.Y - 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];
    }

}
Run Code Online (Sandbox Code Playgroud)

以下是我将图像合二为一的方法.请注意,对于测试,我已将它们添加到 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<Image, Point>(img, pt));
        }
    }
    return bmp;
}
Run Code Online (Sandbox Code Playgroud)

我在启动时打电话给它:

private void Form1_Load(object sender, EventArgs e)
{
    pictureBox1.Image = patchImages();
}
Run Code Online (Sandbox Code Playgroud)

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