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

标签 c# winforms controls transparency region

我有一张不规则形状的图片,例如心形或任何随机形状。 我可以让它在视觉上透明,但我需要让它只在形状区域上可点击。我听说我应该为此使用“区域”,但我不知道如何使用。

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

这是我正在处理的: enter image description here

如图所示,共有 8 个不同的部分(假设左右两边合在一起)。正如您所看到的,它们彼此靠近,其中一些甚至适合其他之间的空白空间。

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

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

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

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

我正在尝试实现的示例:https://www.youtube.com/watch?v=K_JzL4kzCoE

执行此操作的最佳方法是什么?

最佳答案

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

  • 使用区域

  • 使用透明图像

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

这很好,前提是您可以访问构成形状的矢量轮廓

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

enter image description here

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);
}

不幸的是,从其中包含的点创建一个 Region 是行不通的;将 Region 想象成一个矢量形状列表,它们由点组成,但只是为了创建包含矢量,而不是像素。

您可以在形状周围进行追踪,但这是一项繁重的工作,我觉得不值得。

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

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

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

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

List<Tuple<Image, Point>> imgList = new List<Tuple<Image, Point>>();

我们在每个 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];
    }

}

这是我如何将图像合二为一的。请注意,为了进行测试,我已将它们添加到 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;
}

我在启动时调用了它:

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

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

关于c# - 如何在 C# 中创建可点击的不规则形状区域,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/30134352/

相关文章:

c# - DownloadFileAsync 不下载文件

c# - 在用户控件之间共享数据

vb.net - 在不卡住 UI 的情况下向 ListView 添加 150,000 条记录

c++ - CMFCToolTipCtrl 或 CTooltipManager 示例?

c# - DataAnnotations 在 MVC 中是如何工作的?

c# - 使用 C# 从列表框中的选定项获取 ValueMember

delphi - 控件 ''没有父窗口: Why is control not named?

javascript - react js如何防止方向键切换控件

c# - WPF MenuItem图标分享

.net - 如何删除标签控件中的填充?