如何创建一个透明的窗体,使其能够被点击穿透

3
以下是我的一个dll,当提供pos、pic和透明度时,它会在屏幕上显示图片,而该图片将成为最顶部的对象,您也可以通过该图片进行点击。但问题在于,当使用圆形图片时,圆形的边缘上有白色的边框使其变成正方形。我希望去除那些白色的边框,只留下最上面的圆形,并且也能够进行点击。以下代码中的所有内容都可行,除了当f.TransparencyKey = BackColor;时,我不能再通过图片进行点击,但它确实将图片变成了圆形。现在该怎么做才能既让图片变成圆形又可以进行点击?
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Drawing;
using System.Windows.Forms;
using System.Runtime.InteropServices;
namespace pic
{
public class Class1
{
    [DllImport("user32.dll", SetLastError = true)]
    private static extern UInt32 GetWindowLong(IntPtr hWnd, int nIndex);
    [DllImport("user32.dll")]
    static extern int SetWindowLong(IntPtr hWnd, int nIndex, IntPtr dwNewLong);
    [DllImport("user32.dll")]
    static extern bool SetLayeredWindowAttributes(IntPtr hwnd, uint crKey, byte bAlpha, uint dwFlags);
    public const int GWL_EXSTYLE = -20;
    public const int WS_EX_LAYERED = 0x80000;
    public const int WS_EX_TRANSPARENT = 0x20;
    public const int LWA_ALPHA = 0x2;
    public const int LWA_COLORKEY = 0x1;


    public void t(int LocalX, int LocalY, string PicLocal, byte transparency)
    {
        Bitmap bitmap;
        Form f = new Form();
        f.BackColor = Color.White;
        f.FormBorderStyle = FormBorderStyle.None;
        f.Bounds = Screen.PrimaryScreen.Bounds;
        f.TopMost = true;
        bitmap = new Bitmap(PicLocal);
        f.Size = new Size(bitmap.Size.Width, bitmap.Size.Height);
        f.StartPosition = FormStartPosition.Manual;
        f.SetDesktopLocation(LocalX, LocalY);
        Application.EnableVisualStyles();
        SetWindowLong(f.Handle, GWL_EXSTYLE,
        (IntPtr)(GetWindowLong(f.Handle, GWL_EXSTYLE) | WS_EX_LAYERED | WS_EX_TRANSPARENT));
        // set transparency to 50% (128)
        SetLayeredWindowAttributes(f.Handle, 0, transparency, LWA_ALPHA);
        f.BackgroundImage = Bitmap.FromFile(PicLocal);
        //f.AllowTransparency = true;
        //Color BackColor = Color.White;
        // Make the background color of form display transparently. 
        //f.TransparencyKey = BackColor;
        Application.Run(f);

    }

}
}

1
当您添加标志时,您不需要使用exclusive XOR(^),只需使用普通的OR(|)。因此,请将代码更改为:GetWindowLong(f.Handle, GWL_EXSTYLE) | WS_EX_LAYERED | WS_EX_TRANSPARENT) - Cody Gray
@CodyGray 我已经改成那样了,但什么也没变。 - hurnhu
3个回答

3

试试这个

using System;
using System.Drawing;
using System.Runtime.InteropServices;
using System.Windows.Forms;

namespace WindowsFormsApplication4
{
static class Program
{
    [DllImport("Gdi32.dll", EntryPoint = "CreateRoundRectRgn")]
    private static extern IntPtr CreateRoundRectRgn
    (
        int nLeftRect, // x-coordinate of upper-left corner
        int nTopRect, // y-coordinate of upper-left corner
        int nRightRect, // x-coordinate of lower-right corner
        int nBottomRect, // y-coordinate of lower-right corner
        int nWidthEllipse, // height of ellipse
        int nHeightEllipse // width of ellipse
     );

    [STAThread]
    static void Main()
    {
        Application.EnableVisualStyles();
        Application.SetCompatibleTextRenderingDefault(false);

        string PicLocal = @"C:\Projects\Screenshot_1.bmp";

        Form f = new Form() {  FormBorderStyle = FormBorderStyle.None, StartPosition = FormStartPosition.CenterScreen};
        f.BackgroundImage = new Bitmap(PicLocal);
        f.Size = new Size(f.BackgroundImage.Size.Width, f.BackgroundImage.Size.Height);
        f.Region = System.Drawing.Region.FromHrgn(CreateRoundRectRgn(0, 0, 200, 200, 2000, 2000));
        f.Click += (s, e) => { System.Windows.Forms.MessageBox.Show("Clicked"); };

        Application.Run(f);

    }
}

我在这里发现了一个更简单的解决方案使表单的背景透明

你不需要使用Region属性和CreateRoundRectRgn,只需要确保你有一个圆形透明图片即可。

 f.BackColor = Color.Magenta;
 f.TransparencyKey = Color.Magenta;

我认为当图像本身是圆形透明时,不需要使用CreateRoundRectRgnf.Region = System.Drawing.Region.FromHrgn(CreateRoundRectRgn(0, 0, 200, 200, 2000, 2000)); - Matin Lotfaliee
马丁,这对我不起作用。你试过了吗? 无论如何,我找到了一个更简单的解决方案。我编辑了我的答案。 - George Vovos
是的,如果图像具有带透明度(alpha通道)的PNG、gif或BMP格式,它将起作用。 - Matin Lotfaliee
这确实消除了阿尔法通道,但我无法透过表单点击到其后面的内容。 - hurnhu

3
    SetWindowLong(f.Handle, GWL_EXSTYLE,
    (IntPtr)(GetWindowLong(f.Handle, GWL_EXSTYLE) | WS_EX_LAYERED | WS_EX_TRANSPARENT));

这是问题开始的地方。这个声明有太多的副作用,非常麻烦。核心问题在于使用Handle属性,它会强制Winforms创建本地窗口。而且时间过早,样式标志也不正确。WS_EX_LAYERED已经是Winforms自己使用的样式标志,它必须实现Form.Opacity和Form.TransparencyKey属性。
这段代码在Windows XP上以及任何关闭Aero的后续Windows版本中都会出现完全错误的行为。在这种情况下创建分层窗口需要更多的工作,Windows必须在视频适配器中创建一个视频叠加层。它提供了阿尔法和颜色键控效果,视频适配器有一个简单的混合器,将帧缓冲区中的像素与叠加层中的像素组合在一起。通过SetWindowLong()设置样式标志无法创建该覆盖层,必须通过CreateWindowEx()完成。
换句话说,在使用窗口样式标志创建窗口之前,您必须重写CreateParams属性。这正是虚拟属性的原因,重写窗口样式标志。之后设置TransparencyKey属性将没有预期的效果,因为窗口已经被创建得太早了。
解决方法非常简单,完全摆脱这段代码。既不需要SetWindowLong()也不需要SetLayeredWindowAttributes()。由于Winforms已经实现了您所尝试做的事情。修复:
    Color BackColor = Color.White;
    f.TransparencyKey = BackColor;
    f.Opacity = transparency/255f;

考虑将透明度除以100f,这样它就是真正的百分比。重命名为“不透明度”也无妨。


这样做会导致我无法点击图片背后的任何内容。 - hurnhu
你需要创建正确类型的图片,其背景也需要是透明的,这样才不会影响到 TransparencyKey。大多数半好的绘画程序都支持这个功能。例如可以使用 Paint.NET。或者将窗体的 TransparencyKey 设置为与图片的背景相匹配,这也可以。颜色键控是一个简单的概念,在天气预报中每天都能看到。只需让颜色匹配即可。 - Hans Passant
我已经按照你的建议摆脱了SetLayeredWindowAttributes和SetWindowLong。现在图片显示时是不透明的,但如果我点击它,点击事件不会穿过它,它只会将不透明的图片设置为焦点。 - hurnhu
你要点击什么?不要点击可见部分,因为它当然会被点击,因为你看不到它后面的东西。 - Hans Passant
是的,我一直点击可见部分,但我想要实现的是,当你点击该可见部分时,点击会穿过它到达背后的任何东西。这是可能的,我提供的代码就是这样做的,它有一个不透明的图片,无论你在图像的哪个位置点击,点击都会穿过去。但可悲的是,该图像总是正方形的,我无法使用圆形图像。 - hurnhu

0
我最终使用了一个 picturebox 来显示图片,这样可以在图片上单击时具有透明度的同时浏览图片。
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Drawing;
using System.Windows.Forms;
using System.Runtime.InteropServices;
namespace pic
{
public class Class1
{
    [DllImport("user32.dll", SetLastError = true)]
    private static extern UInt32 GetWindowLong(IntPtr hWnd, int nIndex);
    [DllImport("user32.dll")]
    static extern int SetWindowLong(IntPtr hWnd, int nIndex, IntPtr dwNewLong);
    [DllImport("user32.dll")]
    static extern bool SetLayeredWindowAttributes(IntPtr hwnd, uint crKey, byte bAlpha, uint dwFlags);
    public const int GWL_EXSTYLE = -20;
    public const int WS_EX_LAYERED = 0x80000;
    public const int WS_EX_TRANSPARENT = 0x20;
    public const int LWA_ALPHA = 0x2;
    public const int LWA_COLORKEY = 0x1;


    public void t(int LocalX, int LocalY, string PicLocal, byte transparency)
    {
        Bitmap bitmap;
        Form f = new Form();
        f.BackColor = Color.White;
        f.FormBorderStyle = FormBorderStyle.None;
        f.Bounds = Screen.PrimaryScreen.Bounds;
        f.TopMost = true;
        bitmap = new Bitmap(PicLocal);
        f.Size = new Size(600, 600);
        f.StartPosition = FormStartPosition.Manual;
        f.SetDesktopLocation(LocalX, LocalY);
        Application.EnableVisualStyles();



        PictureBox PictureBox1 = new PictureBox();

        PictureBox1.Location = new System.Drawing.Point(70, 120);
        PictureBox1.Image = bitmap;

       PictureBox1.Size = new System.Drawing.Size(140, 140);

        PictureBox1.SizeMode = PictureBoxSizeMode.StretchImage;

        f.Controls.Add(PictureBox1);

        f.AllowTransparency = true;
        SetWindowLong(f.Handle, GWL_EXSTYLE,
        (IntPtr)(GetWindowLong(f.Handle, GWL_EXSTYLE) | WS_EX_LAYERED | WS_EX_TRANSPARENT));
        // set transparency to 50% (128)
        SetLayeredWindowAttributes(f.Handle, 0, transparency, LWA_ALPHA);

        Color BackColor = Color.White;
        f.TransparencyKey = BackColor;
        f.Opacity = transparency / 255f;

        Application.Run(f);

    }

}
}

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