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

时间:2013-09-05 15:20:24

标签: c# winforms

我一直在努力解决这个问题并且已经看了几个stackoverflow帖子,建议将其作为正确的程序:

Transparent window layer that is click-through and always stays on top

在我的代码中,我几乎完全遵循这种技术。然而,我的代码不起作用,我有点困惑为什么。我想知道我是否使用了错误的程序?要清楚,我想要的效果是用户点击我的表单并访问它下面的内容。例如,我在visual studio上运行。如果我尝试点击该应用程序,我会点击visual studio。

更新:

当我调用我的代码时,会发生以下两种情况之一(取决于我调用setwindowlong方法的位置):

  1. 窗口无法绘制
  2. 窗口绘制,但可点击
  3. 当我在initializecomponent之后运行代码时,会发生选项1 当我在initializecomponent

    之前运行它时,会发生选项2

    以下是在其他任何内容之前绘制表单的完整代码:

        [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();
        }
    

2 个答案:

答案 0 :(得分:1)

我们WS_EX_TRANSPARENT = 0x20会使您的表单完全透明。制作click through window时需要使用此扩展样式。因此我们可以通过某种方式正常显示您的窗口(否则它是透明的,这就是您认为未绘制的原因),我们通过使用win32 api函数SetLayeredWindowAttributes(如在您的代码中声明)或只是设置表单的Opacity属性。顺便说一句,您应该覆盖CreateParams以初始化扩展样式,而无需声明和使用方法GetWindowLongSetWindowLong。这是应该工作的代码(至少解决你的问题:窗口未绘制):

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

注意:我发现这里有一件有趣的事情。如果您覆盖CreateParams作为上述代码,Opacity=1将无法显示您的表单(完全透明),您必须将Opacity更改为其他值,例如0.2为了使其部分透明,即使Opacity=0.9999也会显示您的表单(看起来像100%不透明度)。但是,如果您使用某些bool flag来阻止初始化CreateParams中的样式并稍后使用UpdateStyles()应用样式,那么您的表单会在Opacity=1时显示正常,代码看起来像这样:

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

答案 1 :(得分:0)

设置其他窗口样式的正确方法是覆盖CreateParams getter。

这样的风格将从创作中呈现出来。 SetWindowLong可能会设置得太晚,无法100%有效。