2

I have to make a windows form in C# where two PictureBox are overlapping. TopPictureBox is containing a transparent png picture. By default TopPictureBox can be clicked by clicking any visible or transparent area of the image in TopPictureBox. But I want to make that TopPictureBox only can be clicked by clicking visible area of image, not in transparent area. Also I want to make that cursor will only change on the visible area of the image, not in transparent area.

IS THERE ANY WAY TO DO THESE?

I am using this code to make TopPictureBox transparent.

TopPictureBox.BackColor = Color.Transparent;

Thank You for Help.

enter image description here

Reza Aghaei
  • 120,393
  • 18
  • 203
  • 398

2 Answers2

2

Checking if a position in PictureBox is Transparent or not depends on the Image and SizeMode property of PictureBox.

You can not simply use GetPixel of Bitmap because the image location and size is different based on SizeMode. You should first detect the size and location of Image based on SizeMode:

public bool HitTest(PictureBox control, int x, int y)
{
    var result = false;
    if (control.Image == null)
        return result;
    var method = typeof(PictureBox).GetMethod("ImageRectangleFromSizeMode",
      System.Reflection.BindingFlags.NonPublic | System.Reflection.BindingFlags.Instance);
    var r = (Rectangle)method.Invoke(control, new object[] { control.SizeMode });
    using (var bm = new Bitmap(r.Width, r.Height))
    {
        using (var g = Graphics.FromImage(bm))
            g.DrawImage(control.Image, 0, 0, r.Width, r.Height);
        if (r.Contains(x, y) && bm.GetPixel(x - r.X, y - r.Y).A != 0)
            result = true;
    }
    return result;
}

Then you can simply use HitTest method to check if the mouse is over a non-transparent area of PictureBox:

private void pictureBox1_MouseMove(object sender, MouseEventArgs e)
{
    if (HitTest(pictureBox1,e.X, e.Y))
        pictureBox1.Cursor = Cursors.Hand;
    else
        pictureBox1.Cursor = Cursors.Default;
}

private void pictureBox1_MouseClick(object sender, MouseEventArgs e)
{
    if (HitTest(pictureBox1, e.X, e.Y))
        MessageBox.Show("Clicked on Image");
}

Also setting BackColor to Color.Transparent only makes the PictureBox transparent relative to it's parent. For example if you have 2 PictureBox in a Form setting the transparent back color, just cause you see the background of form. To make a PictureBox which supports transparent background, you should draw what is behind the control yourself. You can find a TransparentPictureBox in this post: How to make two transparent layer with c#?

Community
  • 1
  • 1
Reza Aghaei
  • 120,393
  • 18
  • 203
  • 398
  • Oh so that's what you meant by the `SizeMode` reference in the comment. Thanks for the code! – Arctic Vowel Aug 16 '16 at 15:24
  • 1
    @AniruddhaVarma Yes. Using above code you can check if a point is transparent or not. It works properly using different `SizeMode` values like `StretchImage`, `Zoom`, `CenterImage` and `Normal`. – Reza Aghaei Aug 16 '16 at 15:32
  • 1
    Also setting `BackColor` to `Color.Transparent` only makes the `PictureBox` transparent relative to it's parent. For example if you have 2 `PictureBox` in a `Form` setting the transparent back color, just cause you see the background of form. To make a `PictureBox` which supports transparent background, you should draw what is behind the control yourself. You can find a `TransparentPictureBox` in this post: [How to make two transparent layer with c#?](http://stackoverflow.com/a/36102074/3110834) – Reza Aghaei Aug 16 '16 at 15:32
1

One way is to check whether the colour of the pixel where the user clicked, is the same as the background colour of the form. If yes, then the user clicked on a transparent area.

(Note : As Reza mentioned, this code can be used only when there are no overlapping PictureBoxes, i.e. only when the transparent area of the image is of the same colour as the Form's background)

Color pixelColour;

private void myPicturebox_MouseClick(object sender, MouseEventArgs e)
{
   if (e.Button == MouseButtons.Left) 
   {
     pixelColour = ((Bitmap)myPicturebox.Image).GetPixel(point.X, point.Y);
     if (this.BackColor == pixelColour)
     {
        // User clicked on transparent area
     }
     else
     {
        // User clicked on image
     }
   }
}
Arctic Vowel
  • 1,492
  • 1
  • 22
  • 34
  • Thank You & Sorry for asking that Is there any way to do it with Cursor? –  Aug 16 '16 at 11:10
  • @Suprovo Do you mean when the cursor moves over the form, instead of clicking ? – Arctic Vowel Aug 16 '16 at 11:39
  • **1.** What if the image is at center of `PictureBox` based on `SizeMode` of `PictureBox`? **2.** Comparing with `this.BackColor` seems wrong look at deep blue part of image which is under PictureBox. – Reza Aghaei Aug 16 '16 at 12:27
  • @Aniruddha Varma Yes, Cursor only change when it enters on visible image area or back to default when it exit from visible image area, not in transparent area. PictureBox_MouseEnte‌​r method change cursor on transparent area as it works with PictureBox. I mean that. I want to make only visible image area as a button with cursor change. –  Aug 16 '16 at 12:45
  • 1
    @RezaAghaei You're right thanks - I'll update the answer to mention that it won't work in those cases. Please edit if you know how! – Arctic Vowel Aug 16 '16 at 12:55
  • @Suprovo Try the `MouseMove` event instead of `MouseEnter`. – Arctic Vowel Aug 16 '16 at 12:56
  • I Posted a solution which will work regardless of `SizeMode` of `PictureBox`. – Reza Aghaei Aug 16 '16 at 14:32