简体   繁体   English

点击不起作用-SetWindowLong是正确的技术吗?

[英]click-through is not working - is SetWindowLong the proper technique?

I've been struggling with this a lot and have looked at several stackoverflow posts that recommend this as the correct procedure: 我一直在为此苦苦挣扎,并查看了一些将其推荐为正确过程的stackoverflow帖子:

Transparent window layer that is click-through and always stays on top 透明的窗口层是可点击的,始终位于顶部

In my code, I'm following this technique almost exactly. 在我的代码中,我几乎完全遵循这种技术。 And yet, my code is not working and I'm a little confused to why. 但是,我的代码无法正常工作,我对此感到有些困惑。 I'm wondering if I'm using the wrong procedure? 我想知道我是否使用了错误的程序? To be clear, my desired effect is for the user to click my form and to access something underneath it. 明确地说,我想要的效果是用户单击我的表单并访问其下方的内容。 For example, I am running on top of visual studio. 例如,我在Visual Studio上运行。 If I try to click the app, I click visual studio instead. 如果我尝试单击该应用程序,则改为单击Visual Studio。

UPDATE: 更新:

When I call my code, one of two things happens (depending on where I call the setwindowlong method): 当我调用代码时,会发生以下两种情况之一(取决于我在哪里调用setwindowlong方法):

  1. The window does not draw 窗口不绘制
  2. The window draws, but is clickable 窗口已绘制,但可单击

Option 1 happens when I run the code right after initializecomponent Option 2 happens when I run it before initializecomponent 选项1发生在我初始化组件之后立即运行代码时选项2发生在我初始化组件之后立即运行代码时

Here is the complete code to draw my form before anything else: 这是绘制我的表单之前的完整代码:

    [DllImport("user32.dll", SetLastError = true)]
    static extern int GetWindowLong(IntPtr hWnd, int nIndex);

    [DllImport("user32.dll")]
    static extern int SetWindowLong(IntPtr hWnd, int nIndex, int dwNewLong);

    [DllImport("user32.dll")]
    static extern bool SetLayeredWindowAttributes(IntPtr hwnd, uint crKey, byte bAlpha, uint dwFlags);

    public frmPhoneQueueViewer()
    {
        InitializeComponent();
        // Set the form click-through
        int initialStyle = GetWindowLong(this.Handle, -20);
        SetWindowLong(this.Handle, -20, initialStyle | 0x80000 | 0x20);


        //Get height of taskbar to exclude it, then bind to lower right of screen
        int nTaskBarHeight = Screen.PrimaryScreen.Bounds.Bottom -Screen.PrimaryScreen.WorkingArea.Bottom;
        Rectangle workingArea = Screen.GetWorkingArea(this);
        this.Location = new Point(Screen.PrimaryScreen.Bounds.Right - this.Size.Width, workingArea.Bottom - Size.Height + nTaskBarHeight);
        this.TopMost = true;

        this.FormBorderStyle = FormBorderStyle.None;
        this.ControlBox = false;
        this.Text = string.Empty;
        this.ShowInTaskbar = false;


        PopulatePhoneQueueData();
    }

We have WS_EX_TRANSPARENT = 0x20 which will make your form totally transparent.This extended style is required to make a click through window . 我们有WS_EX_TRANSPARENT = 0x20 ,它将使您的表单完全透明。此扩展样式是click through window所必需的。 So we would have some way to show your window normally (otherwise it's transparent and that's why you thought it wasn't drawn ), we do this by using the win32 api function SetLayeredWindowAttributes (as declared in your code) or simply set the Opacity property of your form. 因此,我们将有某种方法可以正常显示您的窗口(否则它是透明的,这就是您认为未绘制窗口的原因),我们可以使用win32 api函数SetLayeredWindowAttributes (在您的代码中声明)来完成此操作,或者只需设置Opacity属性即可你的形式。 By the way, you should override the CreateParams to initialize the extended style without declaring and using the methods GetWindowLong and SetWindowLong . 顺便说一句,您应该重写CreateParams来初始化扩展样式,而无需声明和使用方法GetWindowLongSetWindowLong Here is the code which should work (at least solve your problem: the window isn't drawn ): 这是应该起作用的代码(至少解决了您的问题: 未绘制窗口 ):

public frmPhoneQueueViewer()
{
    InitializeComponent();
    //The default Opacity = 1 won't show your form
    Opacity = 0.2f; //or even Opacity = 0.999 if you like
    //....
    //....
}
protected override CreateParams CreateParams {
    get {
         CreateParams cp = base.CreateParams;
         //WS_EX_LAYERED = 0x80000  WS_EX_TRANSPARENT = 0x20
         cp.ExStyle |= 0x80000 | 0x20;                
         return cp;
    }
}

NOTE : There is an interesting thing here which I've discovered. 注意 :这里发现了一件有趣的事情。 If you override the CreateParams as the code above, Opacity=1 won't show your form (totally transparent), you must change the Opacity to another value, for example 0.2 to make it partially transparent, even Opacity=0.9999 will show your form (looks like 100% opacity). 如果您在上面的代码中覆盖了CreateParams ,则Opacity=1不会显示您的表单(完全透明),您必须将Opacity更改为另一个值,例如0.2以使其部分透明,即使Opacity=0.9999也会显示您的表单(看起来不透明度为100%)。 However if you use some bool flag to prevent initializing the styles in CreateParams and apply the styles later using UpdateStyles() , your form will show OK when the Opacity=1 , the code looks like this: 但是,如果使用一些bool flag来防止在CreateParams初始化样式,并在以后使用UpdateStyles()应用样式,则当Opacity=1时,表单将显示OK,代码如下所示:

public frmPhoneQueueViewer()
{
    InitializeComponent();
    //The default Opacity = 1 will show your form normally
    Load += (s,e) => {
        appliedStyles = true;
        UpdateStyles();//Call this to apply the styles to make your window able to click through.
    };
    //....
    //....
}
bool appliedStyles;
protected override CreateParams CreateParams {
    get {
         CreateParams cp = base.CreateParams;
         //WS_EX_LAYERED = 0x80000  WS_EX_TRANSPARENT = 0x20
         if(appliedStyles) cp.ExStyle |= 0x80000 | 0x20;                
         return cp;
    }
}

The right way to set additional window styles is by overriding the CreateParams getter. 设置其他窗口样式的正确方法是重写CreateParams getter。

That way the styles will be present from creation. 这样,样式便会从创作中呈现出来。 SetWindowLong may set them too late to be 100% effective. SetWindowLong可能将它们设置得太迟而无法100%有效。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

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