如果我需要为用户生成一个看起来像他/她的键盘的自定义键盘布局,我该怎么做?
例如这样的:
法语、瑞典语、英语、加拿大语等会有不同的布局,对吧。这是很多工作还是只是使用某种内置的 .NET 区域类的问题?
如果我需要为用户生成一个看起来像他/她的键盘的自定义键盘布局,我该怎么做?
例如这样的:
法语、瑞典语、英语、加拿大语等会有不同的布局,对吧。这是很多工作还是只是使用某种内置的 .NET 区域类的问题?
按键会生成一个硬件事件,向 Windows 操作系统报告“扫描代码”。然后,根据扫描码以及其他键盘状态因素(状态、//键状态以及任何未决的死键击键),将此扫描码转换为“虚拟键Caps Lock码Shift” 。转换后的 VK 值是事件等报告的值。AltCtrlKeyDown
从扫描码到 VK 码的转换取决于当前的输入语言环境——简单地说,输入语言环境定义了扫描码和虚拟键码之间的映射。有关键盘输入的完整描述,请参阅MSDN 文档。
通过逆向这个查找过程,可以确定每个虚拟键码对应的扫描码(当然,同一个扫描码会因为shift/ctrl/alt状态等原因映射到多个VK码)。Win32 API通过使用该选项提供了MapVirtualKeyEx
执行此映射的功能。MAPVK_VK_TO_VSC_EX
您可以使用它来确定哪个扫描代码生成特定的 VK 代码。
不幸的是,这只是您可以通过编程进行的范围 - 无法确定键盘的物理布局或每个扫描码的按键位置。但是,大多数物理键盘的接线方式相同,因此(例如)左上角的键在大多数物理键盘设计上将具有相同的扫描码。您可以根据基本的物理键盘布局(101 键、102 键等),使用此假定约定来推断与扫描码对应的物理位置。不能保证,但这是一个非常安全的猜测。
以下代码是我编写的一个更大的键盘处理库的摘录(我一直打算开源它,但没有时间)。该方法初始化一个数组 ( this._virtualKeyScanCodes
),该数组由给定输入语言环境的 VK 代码索引(存储在this._inputLanguage
其中,声明为 a)System.Windows.Forms.InputLanguage
。您可以使用该数组来确定与 VK 代码相对应的扫描代码,例如this._virtualKeyScanCodes[VK_NUMPAD0]
- 如果扫描code 为零,则该 VK 在当前输入语言环境中的键盘上不可用;如果它不为零,则可以从中推断出物理键的扫描代码。
不幸的是,当您进入死键领域(例如,产生重音字符的多个键组合)时,事情会比这稍微复杂一些。这一切现在都太复杂了,但如果你想进一步探索,Michael S. Kaplan 写了一系列详细的博客文章。祝你好运!
private void Initialize()
{
this._virtualKeyScanCodes = new uint[MaxVirtualKeys];
// Scroll through the Scan Code (SC) values and get the Virtual Key (VK)
// values in it. Then, store the SC in each valid VK so it can act as both a
// flag that the VK is valid, and it can store the SC value.
for (uint scanCode = 0x01; scanCode <= 0xff; scanCode++)
{
uint virtualKeyCode = NativeMethods.MapVirtualKeyEx(
scanCode,
NativeMethods.MAPVK_VSC_TO_VK,
this._inputLanguage.Handle);
if (virtualKeyCode != 0)
{
this._virtualKeyScanCodes[virtualKeyCode] = scanCode;
}
}
// Add the special keys that do not get added from the code above
for (KeysEx ke = KeysEx.VK_NUMPAD0; ke <= KeysEx.VK_NUMPAD9; ke++)
{
this._virtualKeyScanCodes[(uint)ke] = NativeMethods.MapVirtualKeyEx(
(uint)ke,
NativeMethods.MAPVK_VK_TO_VSC,
this._inputLanguage.Handle);
}
this._virtualKeyScanCodes[(uint)KeysEx.VK_DECIMAL] =
NativeMethods.MapVirtualKeyEx(
(uint)KeysEx.VK_DECIMAL, NativeMethods.MAPVK_VK_TO_VSC, this._inputLanguage.Handle);
this._virtualKeyScanCodes[(uint)KeysEx.VK_DIVIDE] =
NativeMethods.MapVirtualKeyEx(
(uint)KeysEx.VK_DIVIDE, NativeMethods.MAPVK_VK_TO_VSC, this._inputLanguage.Handle);
this._virtualKeyScanCodes[(uint)KeysEx.VK_CANCEL] =
NativeMethods.MapVirtualKeyEx(
(uint)KeysEx.VK_CANCEL, NativeMethods.MAPVK_VK_TO_VSC, this._inputLanguage.Handle);
this._virtualKeyScanCodes[(uint)KeysEx.VK_LSHIFT] =
NativeMethods.MapVirtualKeyEx(
(uint)KeysEx.VK_LSHIFT, NativeMethods.MAPVK_VK_TO_VSC, this._inputLanguage.Handle);
this._virtualKeyScanCodes[(uint)KeysEx.VK_RSHIFT] =
NativeMethods.MapVirtualKeyEx(
(uint)KeysEx.VK_RSHIFT, NativeMethods.MAPVK_VK_TO_VSC, this._inputLanguage.Handle);
this._virtualKeyScanCodes[(uint)KeysEx.VK_LCONTROL] =
NativeMethods.MapVirtualKeyEx(
(uint)KeysEx.VK_LCONTROL, NativeMethods.MAPVK_VK_TO_VSC, this._inputLanguage.Handle);
this._virtualKeyScanCodes[(uint)KeysEx.VK_RCONTROL] =
NativeMethods.MapVirtualKeyEx(
(uint)KeysEx.VK_RCONTROL, NativeMethods.MAPVK_VK_TO_VSC, this._inputLanguage.Handle);
this._virtualKeyScanCodes[(uint)KeysEx.VK_LMENU] =
NativeMethods.MapVirtualKeyEx(
(uint)KeysEx.VK_LMENU, NativeMethods.MAPVK_VK_TO_VSC, this._inputLanguage.Handle);
this._virtualKeyScanCodes[(uint)KeysEx.VK_RMENU] =
NativeMethods.MapVirtualKeyEx(
(uint)KeysEx.VK_RMENU, NativeMethods.MAPVK_VK_TO_VSC, this._inputLanguage.Handle);
this._virtualKeyScanCodes[(uint)KeysEx.VK_LWIN] =
NativeMethods.MapVirtualKeyEx(
(uint)KeysEx.VK_LWIN, NativeMethods.MAPVK_VK_TO_VSC, this._inputLanguage.Handle);
this._virtualKeyScanCodes[(uint)KeysEx.VK_RWIN] =
NativeMethods.MapVirtualKeyEx(
(uint)KeysEx.VK_RWIN, NativeMethods.MAPVK_VK_TO_VSC, this._inputLanguage.Handle);
this._virtualKeyScanCodes[(uint)KeysEx.VK_PAUSE] =
NativeMethods.MapVirtualKeyEx(
(uint)KeysEx.VK_PAUSE, NativeMethods.MAPVK_VK_TO_VSC_EX, this._inputLanguage.Handle);
this._virtualKeyScanCodes[(uint)KeysEx.VK_VOLUME_UP] =
NativeMethods.MapVirtualKeyEx(
(uint)KeysEx.VK_VOLUME_UP, NativeMethods.MAPVK_VK_TO_VSC_EX, this._inputLanguage.Handle);
this._virtualKeyScanCodes[(uint)KeysEx.VK_VOLUME_DOWN] =
NativeMethods.MapVirtualKeyEx(
(uint)KeysEx.VK_VOLUME_DOWN, NativeMethods.MAPVK_VK_TO_VSC_EX, this._inputLanguage.Handle);
this._virtualKeyScanCodes[(uint)KeysEx.VK_VOLUME_MUTE] =
NativeMethods.MapVirtualKeyEx(
(uint)KeysEx.VK_VOLUME_MUTE, NativeMethods.MAPVK_VK_TO_VSC_EX, this._inputLanguage.Handle);
this._virtualKeyScanCodes[(uint)KeysEx.VK_MEDIA_NEXT_TRACK] =
NativeMethods.MapVirtualKeyEx(
(uint)KeysEx.VK_MEDIA_NEXT_TRACK, NativeMethods.MAPVK_VK_TO_VSC_EX, this._inputLanguage.Handle);
this._virtualKeyScanCodes[(uint)KeysEx.VK_MEDIA_PREV_TRACK] =
NativeMethods.MapVirtualKeyEx(
(uint)KeysEx.VK_MEDIA_PREV_TRACK, NativeMethods.MAPVK_VK_TO_VSC_EX, this._inputLanguage.Handle);
this._virtualKeyScanCodes[(uint)KeysEx.VK_MEDIA_PLAY_PAUSE] =
NativeMethods.MapVirtualKeyEx(
(uint)KeysEx.VK_MEDIA_PLAY_PAUSE, NativeMethods.MAPVK_VK_TO_VSC_EX, this._inputLanguage.Handle);
this._virtualKeyScanCodes[(uint)KeysEx.VK_MEDIA_STOP] =
NativeMethods.MapVirtualKeyEx(
(uint)KeysEx.VK_MEDIA_STOP, NativeMethods.MAPVK_VK_TO_VSC_EX, this._inputLanguage.Handle);
this._stateController = new KeyboardStateController();
this._baseVirtualKeyTable = new VirtualKeyTable(this);
}
编辑:另请参阅与您的问题类似的这个问题。
没有包含键盘布局的内置 .NET 类。键盘布局是操作系统的功能,通常是 Windows。当 .NET 介入时,按下的键已经从硬件事件转换为软件事件。如果您想看到这一点,请找到 2 个键盘布局,其中一个键在它们之间移动。在事件上设置一个带有事件处理程序的虚拟应用程序Key_Down
,然后注意事件参数是相同的;如果您按下该-键,则-无论该-键位于何处,您都按下了该键。