【发布时间】: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 没有意识到,现在我想这很好:)
-
“第一个问题是设置窗口在鼠标点击时不激活。” 为什么?如果窗口未激活,您的应用程序应如何响应按钮单击?