These are two approaches to this problem:
The first way involves creating controls, such as PictureBoxes or Panels , which are in the form of an image and are only available inside that form.
This is good if you have access to the vector path that makes up the shape.
Here is an example that restricts the visible & clickable Region from Panel to an irregular blob shape created from a list of trace points:

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); }
Unfortunately, creating a Region from the points it contains will not work; Imagine Region as a list of vector shapes, they are made up of dots, but only to create containing vectors, not pixels.
You can trace around the figures, but this is a lot of work, and imo is not worth it.
So, if you don't have vector shapes: go for the second method:
This assumes that you have images (possibly PNGs) that are transparent in all places where you don’t need to accept clicks.
The easiest and most effective way is to place them on the list along with the points where they should be located; then when they have changed, draw them all on one image, which you can assign PictureBox.Image .
Here is the Mouseclick event that will search for the topmost Image in the list of images to find the one that was clicked. To combine them with their locations, I use the Tuple list:
List<Tuple<Image, Point>> imgList = new List<Tuple<Image, Point>>();
We look at this list in each Mouseclick :
private void pictureBox1_MouseClick(object sender, MouseEventArgs e) { int found = -1;
This is how I combined the images into one. Note that for testing, I added them to the ImageList object. This has serious drawbacks, as all images are scaled to a common size. You probably want to create your own list!
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) {
I called it at startup:
private void Form1_Load(object sender, EventArgs e) { pictureBox1.Image = patchImages(); }
In addition: this way of drawing all the images in one is also the only one that allows you to overlay images freely. Winforms does not support real transparency with overlapping controls. And testing one Pixel (no more) for each of your shapes is also very fast.