Как создать кликабельную область неправильной формы в c#
у меня есть изображение неправильной формы, как сердце или любая случайная форма. Я могу сделать его прозрачным визуально, но мне нужно сделать его кликабельным только в области формы. Я слышал, что я должен использовать "регион" для этого, но я не могу понять, как.
Я попытался найти все пиксели, которые не являются нулевыми, прозрачными или пустыми, и создать с ними массив точек, но я не могу создать/изменить текущую область управления. Я пытаюсь сделать пользовательский элемент управления, который вы можете выбрать кнопку или картину, и они неправильной формы и близко друг к другу.
вот с чем я имею дело:
как вы можете видеть на картинке, есть 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, созданный из списка точек трассировки:
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
Это должно дать вам то, что вам нужно, чтобы начать делать карты для наложения на изображения.