C#WPF当鼠标点击控件

时间:2018-06-05 13:12:39

标签: c# wpf

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

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

enter image description here

它只有一个按钮“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

现在我可能理解为什么代码不起作用。单击moust按钮后立即触发MOUSEACTIVATE,该按钮位于实际的鼠标按钮事件之前(ID 513)。虽然处理了MOUSEACTIVATE并且阻止了窗口激活,但鼠标按钮事件将再次使用ID 28 WM_ACTIVATEAPP激活窗口。我检查了这些事件的文档,但没有一个可以阻止。

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

1 个答案:

答案 0 :(得分:0)

您需要设置 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);
}