You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何创建带可点击控件的半透明覆盖层?WPF实现遇阻求方案

实现半透明可穿透且保留控件点击的窗口方案

以下针对WPF、WinForms、WinUI三个框架分别提供可行方案,核心思路是让窗口背景区域允许鼠标穿透,仅保留指定控件的交互能力。


WPF 实现方案

核心逻辑

通过自定义窗口样式+重写窗口消息处理,判断鼠标位置是否在可交互控件上:不在则标记窗口为透明区域(允许穿透),在则正常响应点击。

1. XAML 窗口基础配置

<Window x:Class="TransparentClickThrough.MainWindow"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        Title="半透明穿透窗口" Height="400" Width="600"
        WindowStyle="None" AllowsTransparency="True"
        Background="Transparent" Topmost="True">
    <Grid>
        <!-- 半透明黑色背景层 -->
        <Rectangle Fill="#80000000" />
        <!-- 可交互控件容器 -->
        <StackPanel HorizontalAlignment="Center" VerticalAlignment="Bottom" Margin="0,0,0,20">
            <Button x:Name="BtnIncrease" Content="+" Width="40" Height="40" Click="BtnIncrease_Click"/>
            <Button x:Name="BtnDecrease" Content="-" Width="40" Height="40" Margin="0,10,0,0" Click="BtnDecrease_Click"/>
        </StackPanel>
    </Grid>
</Window>

2. 后台代码实现鼠标穿透与交互

using System.Windows;
using System.Windows.Interop;
using System.Windows.Media;

public partial class MainWindow : Window
{
    private const int WM_NCHITTEST = 0x0084;
    private const int HTTRANSPARENT = -1;

    public MainWindow()
    {
        InitializeComponent();
    }

    protected override void OnSourceInitialized(EventArgs e)
    {
        base.OnSourceInitialized(e);
        HwndSource source = PresentationSource.FromVisual(this) as HwndSource;
        source?.AddHook(WndProc);
    }

    private IntPtr WndProc(IntPtr hwnd, int msg, IntPtr wParam, IntPtr lParam, ref bool handled)
    {
        if (msg == WM_NCHITTEST)
        {
            // 转换鼠标坐标为窗口内相对位置
            Point mousePos = PointFromScreen(new Point(
                lParam.ToInt32() & 0xFFFF,
                lParam.ToInt32() >> 16
            ));

            // 检测鼠标是否在可交互控件上
            bool isOverControl = false;
            VisualTreeHelper.HitTest(this, null,
                result =>
                {
                    if (result.VisualHit is UIElement elem && elem.IsHitTestVisible)
                    {
                        isOverControl = true;
                        return HitTestResultBehavior.Stop;
                    }
                    return HitTestResultBehavior.Continue;
                },
                new PointHitTestParameters(mousePos));

            if (!isOverControl)
            {
                handled = true;
                return (IntPtr)HTTRANSPARENT; // 标记为透明区域,允许鼠标穿透
            }
        }
        return IntPtr.Zero;
    }

    // 透明度增减逻辑
    private void BtnIncrease_Click(object sender, RoutedEventArgs e)
    {
        if (Opacity < 1) Opacity += 0.1;
    }

    private void BtnDecrease_Click(object sender, RoutedEventArgs e)
    {
        if (Opacity > 0.1) Opacity -= 0.1;
    }
}

WinForms 实现方案

核心逻辑

借助窗口扩展样式WS_EX_LAYERED(分层窗口)和WS_EX_TRANSPARENT(鼠标穿透),通过监听鼠标移动动态切换样式:鼠标在控件上时移除穿透样式,否则添加。

1. 窗口初始化配置

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

public partial class TransparentForm : Form
{
    private const int GWL_EXSTYLE = -20;
    private const int WS_EX_LAYERED = 0x80000;
    private const int WS_EX_TRANSPARENT = 0x20;

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

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

    public TransparentForm()
    {
        InitializeComponent();
        FormBorderStyle = FormBorderStyle.None;
        SetStyle(ControlStyles.SupportsTransparentBackColor, true);
        BackColor = System.Drawing.Color.Transparent;
        Opacity = 0.5; // 初始透明度

        // 设置分层+穿透样式
        int exStyle = GetWindowLong(Handle, GWL_EXSTYLE);
        SetWindowLong(Handle, GWL_EXSTYLE, exStyle | WS_EX_LAYERED | WS_EX_TRANSPARENT);
    }
}

2. 动态切换穿透样式

protected override void WndProc(ref Message m)
{
    const int WM_MOUSEMOVE = 0x0200;

    if (m.Msg == WM_MOUSEMOVE)
    {
        System.Drawing.Point mousePos = PointToClient(Cursor.Position);
        bool overControl = false;

        // 遍历控件检查鼠标位置
        foreach (Control ctrl in Controls)
        {
            if (ctrl.Bounds.Contains(mousePos))
            {
                overControl = true;
                break;
            }
        }

        int exStyle = GetWindowLong(Handle, GWL_EXSTYLE);
        if (overControl)
        {
            // 鼠标在控件上,关闭穿透
            SetWindowLong(Handle, GWL_EXSTYLE, exStyle & ~WS_EX_TRANSPARENT);
        }
        else
        {
            // 鼠标不在控件上,开启穿透
            SetWindowLong(Handle, GWL_EXSTYLE, exStyle | WS_EX_TRANSPARENT);
        }
    }
    base.WndProc(ref m);
}

// 按钮点击事件
private void btnIncrease_Click(object sender, EventArgs e)
{
    if (Opacity < 1) Opacity += 0.1;
}

private void btnDecrease_Click(object sender, EventArgs e)
{
    if (Opacity > 0.1) Opacity -= 0.1;
}

WinUI 3 实现方案

核心逻辑

通过AppWindow.Presenter.IsInputEnabled属性控制窗口是否接收输入:鼠标不在控件上时禁用输入(允许穿透),在则启用。

1. XAML 窗口配置

<Window x:Class="TransparentWinUI.MainWindow"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        Title="半透明穿透窗口" ExtendsContentIntoTitleBar="True">
    <Grid Background="#80000000">
        <StackPanel HorizontalAlignment="Center" VerticalAlignment="Bottom" Margin="0,0,0,20">
            <Button x:Name="BtnIncrease" Content="+" Width="40" Height="40" Click="BtnIncrease_Click"/>
            <Button x:Name="BtnDecrease" Content="-" Width="40" Height="40" Margin="0,10,0,0" Click="BtnDecrease_Click"/>
        </StackPanel>
    </Grid>
</Window>

2. 后台代码实现穿透逻辑

using Microsoft.UI.Windowing;
using Microsoft.UI.Xaml;
using Microsoft.UI.Xaml.Input;
using Windows.Foundation;

namespace TransparentWinUI
{
    public sealed partial class MainWindow : Window
    {
        private AppWindow _appWindow;

        public MainWindow()
        {
            InitializeComponent();
            _appWindow = AppWindow.GetFromWindowId(Win32Interop.GetWindowIdFromWindow(this.WindowHandle));
            
            // 设置无边框
            var presenter = _appWindow.Presenter as OverlappedPresenter;
            presenter.SetBorderAndTitleBar(false, false);
            
            // 监听鼠标移动事件
            this.PointerMoved += MainWindow_PointerMoved;
        }

        private void MainWindow_PointerMoved(object sender, PointerRoutedEventArgs e)
        {
            Point pointerPos = e.GetCurrentPoint(this).Position;
            bool isOverControl = false;

            // 检测鼠标是否在按钮上
            var elements = VisualTreeHelper.FindElementsInHostCoordinates(pointerPos, this);
            foreach (var elem in elements)
            {
                if (elem is Button)
                {
                    isOverControl = true;
                    break;
                }
            }

            // 动态切换窗口输入状态
            _appWindow.Presenter.IsInputEnabled = isOverControl;
        }

        // 透明度增减逻辑
        private void BtnIncrease_Click(object sender, RoutedEventArgs e)
        {
            if (Opacity < 1) Opacity += 0.1;
        }

        private void BtnDecrease_Click(object sender, RoutedEventArgs e)
        {
            if (Opacity > 0.1) Opacity -= 0.1;
        }
    }
}

内容的提问来源于stack exchange,提问作者elmor6942

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.06 07:34:54