简体   繁体   中英

Use image Button with transparent color area

I have a picture in PNG with transparent and normal color.

I use this for one Button:

this.Button1.BackColor = System.Drawing.Color.Transparent;
this.Button1.BackgroundImage = Image;
this.Button1.BackgroundImageLayout = System.Windows.Forms.ImageLayout.None;
this.Button1.FlatAppearance.BorderSize = 0;
this.Button1.FlatAppearance.MouseDownBackColor = System.Drawing.Color.Transparent;
this.Button1.FlatAppearance.MouseOverBackColor = System.Drawing.Color.Transparent;
this.Button1.FlatStyle = System.Windows.Forms.FlatStyle.Flat;
this.Button1.Location = new System.Drawing.Point(0, 0);
this.Button1.Margin = new System.Windows.Forms.Padding(0);
this.Button1.Name = "Skin";
this.Button1.Size = new System.Drawing.Size(294, 194);
this.Button1.TabIndex = 7;
this.Button1.UseVisualStyleBackColor = false;

And I when I click at transparent area, it still counts as a click on the Button.

How can I make this Button only call click event when I click at the colored area?

And when I click at the transparent area, I want it to count as a click on the object behind this button.

You have two options:

  • Use a Button with a Region .

  • Use a Button with a BackgroundImage and check what the user hits with each Click

Option one is only feasible if you can create a Region , which takes a GraphicsPath , which means that you need to create the shape you need from the Graphics primitves like lines and curves etc..

If you only have a Bitmap with transparency, you are better off not using a Button with a Region .

Instead you can use your Button1 and on every click you check the transparency of the clicked pixel.

If it is transparent you call the click event of the control below it..

private void Button1_MouseClick(object sender, MouseEventArgs e)
{
    Size r = Button1.BackgroundImage.Size;
    // check that we have hit the image and hit a non-transparent pixel
    if ((e.X < r.Width && e.Y < r.Height) &&
            ((Bitmap)Button1.BackgroundImage).GetPixel(e.X, e.Y).A != 0)
    {
        Console.WriteLine("BUTTON clicked");  // test
        // do or call your click actions here!
    }
    // else pass the click on..
    else
    {
        // create a valid MouseEventArgs
        MouseEventArgs ee = new MouseEventArgs(e.Button, e.Clicks, 
                                e.X + Button1.Left, e.Y + Button1.Top, e.Delta);
        // pass it on to the stuff below us
        pictureBox1_MouseClick(pictureBox1, ee);

        Console.WriteLine("BUTTON NOT clicked");  // test
    }
}

Note that the check assumes that you have a normal layout, with the button image at the top left and no scaling. If you need to scale the image you should keep a scaled bitmap to do the checks on.. But if you can use an unscale image you should do so, as this will look better.

Note how I create a correct MouseEventArgs parameter for the control below, so you can access the button or the location of the mouse there as well..

Also note that it is easier to use the MouseClick event instead of the Click event as it has the mouse location already..

If you need/want to use the Click event instead, you can skip creating the EventArgs , as it doesn't have meaningful data; just pass out the e from the click..

Here is how the Click event could start:

private void Button1_Click(object sender, EventArgs e)
{
    // we need the location of the clicked pixel:
    Point clickLocation = Button1.PointToClient(Control.MousePosition);
    // the rest can proceed as above, just with simple EventArgs..

If you want to the check on all mouse clicking event and pass each of them down to the parent you will have to code them all.

First let's look at the order of events on MSDN

  1. MouseDown event.
  2. Click event.
  3. MouseClick
  4. MouseUp event.

So we need to start at the MouseDown . We can do the test in a helper function hitTest , so we can re-use it..:

Button clickedButton = null;
MouseEventArgs ee = null;

void hitTest(Button btn, MouseEventArgs e)
{
    Size r = btn.BackgroundImage.Size;
    // check that we have hit the image and hit a non-transparent pixel
    if ((e.X < r.Width && e.Y < r.Height) &&
            ((Bitmap)btn.BackgroundImage).GetPixel(e.X, e.Y).A != 0)
    {
        clickedButton = btn;
        ee = new MouseEventArgs(e.Button, e.Clicks, e.X + btn.Left, e.Y + btn.Top, e.Delta);
    }
    else clickedButton = null;
}

Now we code all four events. We need to call hitTest only once and we pass the simple, unmodified e parameter in the Click event:

private void Button1_MouseDown(object sender, MouseEventArgs e)
{
    hitTest(sender as Button, e);
    if (sender != clickedButton)
        yourParent_MouseDown((sender as Button).Parent, ee);
    else // do your stuff
}

private void Button1_Click(object sender, EventArgs e)
{
    if (sender != clickedButton)
        yourParent_Click((sender as Button).Parent, e);
    else // do your stuff
}

private void Button1_MouseClick(object sender, MouseEventArgs e)
{
    if (sender != clickedButton)
        yourParent_MouseClick((sender as Button).Parent, ee);
    else // do your stuff
}

private void Button1_MouseUp(object sender, MouseEventArgs e)
{
    if (sender != clickedButton)
        yourParent_MouseUp((sender as Button).Parent, ee);
    else // do your stuff
}

Of course you need to code all four events for yourParent also..

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM