You need to enable JavaScript to run this app.
最新活动
大模型
产品
解决方案
定价
生态与合作
支持与服务
开发者
了解我们

Avalonia开发Windows游戏辅助窗口:如何实现背景点击穿透且控件可单独设置点击响应

Avalonia开发Windows游戏辅助窗口:如何实现背景点击穿透且控件可单独设置点击响应

我完全懂你现在的困扰——想用Avalonia做一个Windows游戏辅助窗口,既要实现背景点击穿透(能点到下面的游戏),又要让窗口里的控件正常响应交互;之前用WS_EX_TRANSPARENT虽然实现了穿透,但直接把整个窗口的点击事件全屏蔽了,这完全不是你想要的效果。

别担心,我们可以通过调整Windows分层窗口的设置,结合Avalonia的UI配置来解决这个问题,核心思路是用颜色键(Color Key)实现局部穿透,而不是全窗口屏蔽点击。


一、替换WS_EX_TRANSPARENT:用分层窗口颜色键实现精准穿透

WS_EX_TRANSPARENT的本质是让Windows完全忽略整个窗口的所有点击事件,这显然不符合我们的需求。我们需要用WS_EX_LAYERED(分层窗口)配合SetLayeredWindowAttributes函数,只让窗口中特定颜色的区域(也就是你的背景)允许点击穿透,其他区域(控件)保留交互能力。

1. 补充P/Invoke函数声明

首先在你的窗口类里补充需要的Windows API声明:

using System.Runtime.InteropServices;
using Avalonia;
using Avalonia.Controls;
using Avalonia.Interactivity;

public partial class MainWindow : Window
{
    private const int GWL_EXSTYLE = -20;
    private const int WS_EX_LAYERED = 0x80000;
    private const int WS_EX_TOOLWINDOW = 0x80;
    private const int WS_EX_TRANSPARENT = 0x20; // 用于清理之前的设置
    private const uint LWA_COLORKEY = 0x00000001;
    private const uint LWA_ALPHA = 0x00000002;

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

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

    [DllImport("user32.dll", SetLastError = true)]
    private static extern bool SetLayeredWindowAttributes(IntPtr hWnd, uint crKey, byte bAlpha, uint dwFlags);

    // 窗口的其他代码...
}

2. 封装窗口样式设置方法

把设置窗口样式的逻辑封装成一个方法,方便在多个事件中调用(比如窗口加载、激活时):

private void ApplyGameHelperWindowStyles(Window? window)
{
    if (window is null || !RuntimeInformation.IsOSPlatform(OSPlatform.Windows))
        return;

    var platformHandle = window.TryGetPlatformHandle()?.Handle;
    if (platformHandle is not { HasValue: true, Value: not IntPtr.Zero })
        return;

    IntPtr hWnd = platformHandle.Value;
    int currentExStyle = GetWindowLong(hWnd, GWL_EXSTYLE);

    // 保留WS_EX_LAYERED(分层窗口)和WS_EX_TOOLWINDOW(无Alt+Tab项),移除WS_EX_TRANSPARENT
    int newExStyle = currentExStyle | WS_EX_LAYERED | WS_EX_TOOLWINDOW;
    newExStyle &= ~WS_EX_TRANSPARENT; // 确保清理之前可能设置的WS_EX_TRANSPARENT

    // 应用新的扩展样式
    _ = SetWindowLong(hWnd, GWL_EXSTYLE, newExStyle);

    // 设置颜色键:用洋红色#FFFF00FF作为穿透色(这个颜色极少在UI中使用)
    // 只有完全匹配这个颜色的区域会允许点击穿透,其他区域正常响应交互
    _ = SetLayeredWindowAttributes(hWnd, 0xFFFF00FF, 255, LWA_COLORKEY | LWA_ALPHA);
}

3. 在窗口事件中调用样式设置

在窗口的LoadedActivated事件中调用这个方法,确保窗口样式在启动和激活时都能正确应用:

public MainWindow()
{
    InitializeComponent();
    Loaded += Window_Loaded;
    Activated += OnWindowActivated;
}

private void Window_Loaded(object? sender, RoutedEventArgs e)
{
    ApplyGameHelperWindowStyles(sender as Window);
}

private void OnWindowActivated(object? sender, EventArgs e)
{
    ApplyGameHelperWindowStyles(sender as Window);
}

二、调整Avalonia窗口XAML配置

接下来修改窗口的XAML,让窗口背景使用我们指定的颜色键,同时确保控件区域不使用该颜色:

<Window xmlns="https://github.com/avaloniaui"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        x:Class="ExileWatch.Views.MainWindow"
        WindowState="FullScreen"
        ShowInTaskbar="False"
        Background="#FFFF00FF" <!-- 这里用我们设置的穿透色:洋红色 -->
        SystemDecorations="None"
        TransparencyLevelHint="Transparent"
        Topmost="True"
        IsHitTestVisible="True" <!-- 必须设为True,否则Avalonia会屏蔽所有控件交互 -->
        Loaded="Window_Loaded"
        Activated="OnWindowActivated">

    <!-- 容器Grid:背景用透明或其他非穿透色,确保控件区域不被穿透 -->
    <Grid x:Name="AppContainer"
          Background="Transparent" <!-- 或者用你需要的背景色,只要不是#FFFF00FF -->
          HorizontalAlignment="Stretch"
          VerticalAlignment="Stretch">

        <!-- 示例控件:这个按钮会正常响应点击 -->
        <Button Content="游戏辅助按钮"
                Width="120"
                Height="40"
                HorizontalAlignment="Center"
                VerticalAlignment="Center"
                Background="#FF2C3E50"
                Foreground="White"/>

        <!-- 如果需要某个控件也能穿透,只要把它的Background设为#FFFF00FF即可 -->
        <Border Width="200"
                Height="200"
                Background="#FFFF00FF"
                HorizontalAlignment="Right"
                VerticalAlignment="Bottom"/>
    </Grid>
</Window>

三、原理说明

  • 分层窗口(WS_EX_LAYERED):让Windows允许我们对窗口的透明度和点击区域进行精细化控制。
  • SetLayeredWindowAttributes:通过LWA_COLORKEY参数指定一个颜色,所有完全匹配该颜色的像素区域会被Windows视为“可穿透”,点击事件会直接传递到下面的窗口(比如游戏);非该颜色的区域则保留正常的点击交互。
  • Avalonia配置:窗口的IsHitTestVisible="True"确保Avalonia能处理控件的点击事件,容器和控件的背景不用穿透色,就能正常响应交互。

额外注意事项

  1. 如果你需要半透明的控件,只要控件的背景色不是完全匹配穿透色,半透明区域不会被穿透,依然能响应点击。
  2. 可以根据需求更换穿透色,只要确保这个颜色不会在你的控件中被使用即可。
  3. 如果窗口样式偶尔被重置(比如系统主题变化),可以在窗口的DeactivatedGotFocus事件中再次调用样式设置方法。

内容来源于stack exchange

火山引擎 最新活动