Как создать кликабельную область неправильной формы в c#

у меня есть изображение неправильной формы, как сердце или любая случайная форма. Я могу сделать его прозрачным визуально, но мне нужно сделать его кликабельным только в области формы. Я слышал, что я должен использовать "регион" для этого, но я не могу понять, как.

Я попытался найти все пиксели, которые не являются нулевыми, прозрачными или пустыми, и создать с ними массив точек, но я не могу создать/изменить текущую область управления. Я пытаюсь сделать пользовательский элемент управления, который вы можете выбрать кнопку или картину, и они неправильной формы и близко друг к другу.

вот с чем я имею дело: enter image description here

как вы можете видеть на картинке, есть 8 различных частей (при условии, что правая и левая стороны объединяются). Как видно, они близки друг к другу, и некоторые из них даже поместиться в свободное пространство между другими.

моя цель, например, если я нажал на грудную клетку (красная зона на рисунке), она изменится на цветную версию он, и куча другого кода будет работать.

проблема в том, по умолчанию, когда мы добавляем любое изображение с PictureBox, Это создаст Rectangle вокруг этой картины, начиная с ее границ. Поэтому, если я помещаю два изображения (как на рисунке) близко друг к другу, пустая зона одного мешает мне щелкнуть другого.

также он поднимает неправильный объект ClickEvent из-за этой проблемы.

Я пытаюсь установить "регион событий Raise", который, как я предполагаю, мы назвали Graphic Region только там, где картина существует. Я могу собирать позиции пикселей с помощью цикла, который определяет, какие координаты этого изображения имеют "цвет" (то есть это часть изображения, область, которую я хочу щелкнуть), но я не могу ограничить эту область этими данными.

пример того, чего я пытаюсь достичь:https://www.youtube.com/watch?v=K_JzL4kzCoE

каков наилучший способ сделать это?

3 ответов


эти два подхода к этой проблеме:

  • работы с Regions.

  • работа с прозрачными Images.

первый метод включает в себя создание элементов управления, e.g PictureBoxes или Panels , которые формы изображения и только кликабельны внутри этой формы.

это хорошо, при условии, что у вас есть доступ к векторный контур, что составляет форма.

вот пример, который ограничивает видимый & clickable Region of a Panel в неправильной форме 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 в качестве списка векторных фигур они состоят из точек, но только для создания содержащих векторы, а не пиксели..

вы смогли трассировать вокруг форм но это большая работа, и Имо не стоит того.

Итак, если у вас нет векторных фигур: перейдите ко второму методу:

это предполагает, что у вас есть изображения (возможно, PNGs), которые прозрачны во всех местах, где не должны приниматься клики.

наиболее простым и эффективным способом будет поместить их в список вместе с точками, где они должны быть расположены; затем, когда они изменились, собрать их всех в одно изображение, которое вы можете назначить а 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];
    }

}

вот как я объединил изображения в один. Обратите внимание, что для тестирования я добавил их в


вот пример 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 есть Image загружено из ресурса сердечной формы, который я свободно передал.


вы пробовали карту?

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

Это должно дать вам то, что вам нужно, чтобы начать делать карты для наложения на изображения.