我正在尝试用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
}
}
我已经记录了一个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
事件。
答案 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);
}