【问题标题】:C# WPF How to not have the MainWindow activated when mouse clicks on a controlC#WPF如何在鼠标单击控件时不激活MainWindow
【发布时间】:2018-06-05 13:12:39
【问题描述】:

简介

我正在尝试用 C# 创建一个屏幕键盘应用程序。当用户单击按钮时,此键盘应像屏幕键盘一样模拟键盘输入。例如,当用户单击“A”按钮时,应在输入框中(在另一个应用程序中)输入a

由于我昨晚才有这个想法,现在开始开发,所以我只做了一个非常简单的测试ui:

它只有一个按钮“A”。

问题

其中一个问题是将窗口设置为在鼠标单击时不激活。感谢我在an earlier question 中发表的评论和这个answer,我现在有了以下代码:

    protected override void OnSourceInitialized(EventArgs e)
    {
        base.OnSourceInitialized(e);
        var winH = (HwndSource) PresentationSource.FromVisual(this);
        // set mouse activate false
        winH.AddHook(WndProc);
    }

    #region WndProc

    private const int WM_MOUSEACTIVATE = 0x0021;
    private const int MA_NOACTIVATE = 0x0003;

    private static IntPtr WndProc(IntPtr hwnd, int msg, IntPtr wParam, IntPtr lParam, ref bool handled)
    {
        if (msg != WM_MOUSEACTIVATE)
            return IntPtr.Zero;

        handled = true;
        return new IntPtr(MA_NOACTIVATE);
    }

    #endregion WndProc

但是,代码运行不佳。仅当单击MainWindow 的背景(空白区域)时才禁用鼠标激活。如果单击Button,窗口将被激活。由于 wpf 控件没有 HWND,因此我无法 AddHook 对其。

此外,虽然点击MainWindow 的空白处不会将应用程序带到最前面,但似乎会激活应用程序,因为它会在任务栏上突出显示。

附录 - 完整代码

MainWindow.xaml:

<Window x:Class="OnScreenKeyPad.MainWindow"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
        xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
        xmlns:local="clr-namespace:OnScreenKeyPad"
        mc:Ignorable="d"
        Title="MainWindow" Height="200" Width="200"
        WindowStyle = "None"
        AllowsTransparency="True"            >
    <StackPanel>
        <Button x:Name="KeyA" Content="A" Width="50" Margin="10"/>
    </StackPanel>
</Window>

MainWindow.xaml.cs:

using System;
using System.Runtime.InteropServices;
using System.Windows;
using System.Windows.Interop;

namespace OnScreenKeyPad
{
    public partial class MainWindow
    {

        public MainWindow()
        {
            InitializeComponent();
        }

        protected override void OnSourceInitialized(EventArgs e)
        {
            base.OnSourceInitialized(e);
            var winH = (HwndSource) PresentationSource.FromVisual(this);
            // set mouse activate false
            winH.AddHook(WndProc);
        }

        #region WndProc

        private const int WM_MOUSEACTIVATE = 0x0021;
        private const int MA_NOACTIVATE = 0x0003;

        private static IntPtr WndProc(IntPtr hwnd, int msg, IntPtr wParam, IntPtr lParam, ref bool handled)
        {
            if (msg != WM_MOUSEACTIVATE)
                return IntPtr.Zero;

            handled = true;
            return new IntPtr(MA_NOACTIVATE);
        }

        #endregion WndProc
    }
}

更新 1

我已经记录了一个 WindowProc 消息列表:

ID   Event

33   WM_MOUSEACTIVATE 
32   WM_SETCURSOR
513  WM_LBUTTONDOWN
NA   OnMouseDownPreviewEvent
70   WM_WINDOWPOSCHANGING
71   WM_WINDOWPOSCHANGED
28   WM_ACTIVATEAPP
134  WM_NCACTIVATE
6    WM_ACTIVATE 
641  WM_IME_SETCONTEXT 
642  WM_IME_NOTIFY
7    WM_SETFOCUS 
8    WM_KILLFOCUS 
641  WM_IME_SETCONTEXT 
641  WM_IME_SETCONTEXT 
7    WM_SETFOCUS 
641  WM_IME_SETCONTEXT 
641  WM_IME_SETCONTEXT 
132  WM_NCHITTEST 
512  WM_NCHITTEST 
132  WM_MOUSEFIRST
514  WM_LBUTTONUP 
533  WM_CAPTURECHANGED
132  WM_NCHITTEST 
NA   ButtonOnClickEvent

现在我大概明白为什么代码不起作用了。 MOUSEACTIVATE 在单击鼠标按钮后立即触发,即在实际鼠标按钮事件 (ID 513) 之前。尽管处理了MOUSEACTIVATE 并阻止了窗口激活,但鼠标按钮事件将再次激活ID 为28 WM_ACTIVATEAPP 的窗口。我检查了这些事件的文档,没有一个可以被阻止。

也许我可以使用标签并利用其PreviewMouseDown 事件。

【问题讨论】:

  • 以防万一您不知道,Windows 内置了一个屏幕键盘,可以处理所有这些问题以及您尚未遇到的许多问题(如重音和非欧洲字符)。 windowscentral.com/…
  • 如果您的问题明确指出“问题 1”、“问题 2”,那么您是在邀请“过于宽泛”的结束。请每个问题一个问题。
  • @Neil 是的,相似但不一样。我不想要这么多键,只想要 WASD、方向键、esc 等。
  • @spender 没有意识到,现在我想这很好:)
  • “第一个问题是设置窗口在鼠标点击时不激活。” 为什么?如果窗口未激活,您的应用程序应如何响应按钮单击?

标签: c# wpf


【解决方案1】:

您需要设置 WS_EX_NOACTIVATE 窗口样式而不是自己尝试吃消息:

private const int WS_EX_NOACTIVATE = 0x08000000;
private const int GWL_EXSTYLE = -20;

[DllImport("user32.dll", CharSet = CharSet.Auto, SetLastError = true)]
private static extern int GetWindowLong(IntPtr hwnd, int index);

[DllImport("user32.dll", CharSet = CharSet.Auto, SetLastError = true)]
private static extern int SetWindowLong(IntPtr hwnd, int index, int newStyle);

protected override void OnSourceInitialized(EventArgs e)
{
  var hWnd = new WindowInteropHelper(this).Handle;
  int style = GetWindowLong(hWnd, GWL_EXSTYLE);
  SetWindowLong(hWnd, GWL_EXSTYLE, style | WS_EX_NOACTIVATE);

  base.OnSourceInitialized(e);
}

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2014-10-13
    • 2018-06-29
    • 1970-01-01
    • 2010-10-24
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多