如何为C#透明穿透窗体设置边框点击识别例外?
实现点击穿透窗体的边框点击例外
我有一个透明且支持点击穿透的C#窗体,点击操作会直接穿透到窗体下层。但该窗体绘制了20像素宽的红色边框,我希望点击该边框时能被窗体识别而非穿透,请问是否可以实现这种点击例外?
现有实现代码
1. 设置窗体透明
FormBorderStyle = FormBorderStyle.None; this.SetStyle(ControlStyles.SupportsTransparentBackColor, true); this.TransparencyKey = this.BackColor = Color.Turquoise;
2. 设置点击穿透
int initialStyle = GetWindowLong(this.Handle, -20); SetWindowLong(this.Handle, -20, initialStyle | 0x80000 | 0x20); // Create and add the custom border control to the form
3. 绘制边框
protected override void OnPaint(PaintEventArgs e) { base.OnPaint(e); // Draw a red border around the form using (Pen pen = new Pen(Color.Red, 20)) { e.Graphics.DrawRectangle(pen, 0, 0, Width - 1, Height - 1); } }
我尝试的代码,思路是否正确?
protected override void WndProc(ref Message m) { const int WM_NCHITTEST = 0x0084; const int HTTRANSPARENT = -1; const int HTCLIENT = 1; if (m.Msg == WM_NCHITTEST) { if (Control.MouseButtons == MouseButtons.Left && !isClickThrough) { // Set the window style to make it click-through int initialStyle = GetWindowLong(this.Handle, -20); SetWindowLong(this.Handle, -20, initialStyle | 0x80000 | 0x20); isClickThrough = true; } else if (Control.MouseButtons == MouseButtons.None && isClickThrough) { // Reset the window style to make it non-click-through int initialStyle = GetWindowLong(this.Handle, -20); SetWindowLong(this.Handle, -20, initialStyle & ~0x80000 & ~0x20); isClickThrough = false; } m.Result = (IntPtr)(isClickThrough ? HTTRANSPARENT : HTCLIENT); return; } base.WndProc(ref m); }
解答
你的思路存在问题,当前代码通过鼠标按键状态全局切换窗体的点击穿透属性,无法精准识别点击位置是否在边框上,达不到仅边框可点击的效果。
正确的做法是在WM_NCHITTEST消息中直接判断鼠标位置是否落在边框区域内:
- 先将屏幕坐标转换为窗体客户端坐标
- 判断坐标是否处于距离窗体边缘20像素的范围内
- 若在边框区域,返回
HTCLIENT让窗体接收点击;否则返回HTTRANSPARENT允许点击穿透
修改后的WndProc代码如下:
private const int WM_NCHITTEST = 0x0084; private const int HTTRANSPARENT = -1; private const int HTCLIENT = 1; private const int BORDER_WIDTH = 20; // 与绘制的边框宽度保持一致 protected override void WndProc(ref Message m) { if (m.Msg == WM_NCHITTEST) { // 将消息中的屏幕坐标转换为窗体客户端坐标 Point screenPos = new Point(m.LParam.ToInt32() & 0xFFFF, m.LParam.ToInt32() >> 16); Point clientPos = this.PointToClient(screenPos); // 判断鼠标是否点击在边框区域 bool isOnBorder = clientPos.X <= BORDER_WIDTH || clientPos.X >= this.ClientSize.Width - BORDER_WIDTH || clientPos.Y <= BORDER_WIDTH || clientPos.Y >= this.ClientSize.Height - BORDER_WIDTH; // 根据判断结果设置消息返回值 m.Result = isOnBorder ? (IntPtr)HTCLIENT : (IntPtr)HTTRANSPARENT; return; } base.WndProc(ref m); }
注意事项:
- 无需动态修改窗体的扩展样式(
0x80000和0x20),保持初始的点击穿透设置即可,通过WM_NCHITTEST的返回值控制单点击的穿透行为。 - 确保
BORDER_WIDTH常量值与你绘制边框时的宽度一致,避免判断范围出现偏差。
内容的提问来源于stack exchange,提问作者Chefke
相关产品推荐
相关产品推荐

