使用具有透明颜色区域的图像按钮

3

我有一张带透明和普通颜色的PNG图片。

我将其用于一个按钮:

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;

当我点击透明区域时,它仍被认为是对按钮的点击。

如何使这个按钮只在我点击有颜色的区域时才调用点击事件?

并且当我点击透明区域时,希望它被认为是对该按钮后面的对象的点击。


如果您可以获取一个排除透明区域的GraphicsPath,那么您可以限制按钮的区域。不幸的是,您无法将位图转换为路径。 - TaW
是的,我搜索了好几天,但没有找到解决方案。我真的想在winform中创建一个自定义按钮,但我所要做的就是让这个自定义按钮变成矩形... - Tranoze
那么,你的图像是什么样子的?简单的形状可以做到,但复杂的图像无法精确处理。请参见此帖子的开头(仅限)以了解如何将控件的区域限制为某个形状。 - TaW
它看起来像一片叶子。 - Tranoze
看看我的更新和简化的答案! - TaW
1个回答

2
您有两个选项:
  • 使用带有RegionButton

  • 使用带有BackgroundImageButton,并检查用户每次Click击中的内容。

如果您可以创建一个带有GraphicsPathRegion,那么选项一是可行的,这意味着您需要从Graphics原始线条和曲线等中创建所需的形状

如果您只有一个带有透明度的Bitmap,最好不要使用带有RegionButton

相反,您可以使用您的Button1,并在每次点击时检查所点击像素的透明度。

如果它是透明的,您就调用其下方控件的单击事件。

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
    }
}

请注意,此检查假定您具有正常的布局,其中按钮图像位于左上角且没有缩放。如果您需要缩放图像,则应保留缩放后的位图以进行检查。但是,如果可以使用未缩放的图像,则应该这样做,因为这样看起来更好。
请注意,我如何为下面的控件创建正确的MouseEventArgs参数,以便您也可以在那里访问按钮或鼠标位置。
另请注意,使用MouseClick事件比使用Click事件更容易,因为它已经具有了鼠标位置。
如果您需要/想要使用Click事件,则可以跳过创建EventArgs,因为它没有有意义的数据;只需将e从点击传递出去即可。
以下是Click事件的开始方式:
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..

如果您想检查所有鼠标点击事件并将每个事件传递给父级,则必须编写所有代码。
首先让我们看看MSDN上事件的顺序。
  1. MouseDown事件。
  2. Click事件。
  3. MouseClick事件。
  4. MouseUp事件。
所以我们需要从MouseDown开始。 我们可以在一个名为hitTest的辅助函数中进行测试,以便我们可以重复使用它...:
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;
}

现在我们编写所有四个事件。我们只需要调用一次hitTest,并在Click事件中传递简单未修改的e参数:
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
}

当然你也需要为yourParent编写这四个事件。

非常感谢!它已经生效了,我使用的是鼠标按下和鼠标松开事件而不是点击事件。 - Tranoze
但是有没有办法调用按钮的父级的鼠标点击/鼠标弹起/鼠标按下(所有鼠标事件)?我希望所有鼠标事件都传递给父级而不仅仅是鼠标点击。 - Tranoze
不,我不是指那个,我已经做了,我的意思是即使父级没有这些鼠标事件处理程序,我如何使其仍然在透明区域接收鼠标操作? - Tranoze
我不明白。什么是父级?你只能调用存在于你的代码中的事件。 - TaW
我不是指调用事件。我的意思是我们能否在某个位置调用“点击”动作,然后控件将检查该位置是否具有点击事件。 - Tranoze
显示剩余4条评论

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接