2011-01-27 64 views
6

如果我需要为用户定制的键盘布局,看起来像他/她的键盘,我该怎么做?是否可以创建与使用的键盘相同的键盘布局?

例如是这样的:

enter image description here

法语,瑞典语,英语,加拿大等都会有不同的布局,正确的。这是很多工作,或者只是使用某种内置.NET区域类的问题?

+4

看起来像是一个很好的附加组件,可供人写作;) – Oded 2011-01-27 21:50:39

+0

只需使用Google图片即可获得国际键盘布局的图片。例如,尝试使用[法语键盘布局]的谷歌搜索(http://www.google.com/images?hl=zh-CN&q=french+keyboard+layout)。 – Timwi 2011-01-27 21:51:47

+0

是的,但我需要用预定义的样式从头开始绘制它。另外,如果我使用布局,那么我需要存储所有的图像,对不对?我需要在运行时动态执行此操作(启动自定义对话框时)。 – 2011-01-27 21:53:47

回答

8

按键生成一个硬件事件,向Windows操作系统报告“扫描代码”。然后,该扫描码被转换成基于与其他键盘状态因素沿着扫描码的“虚拟键码”(大写锁定状态,移位/Alt键/Ctrl键 keystate,以及任何未决的死键招)。转换的VK值是由KeyDown事件等报告的内容。

从扫描代码到VK代码的转换取决于当前输入语言环境 - 简单地说,输入语言环境定义扫描代码和虚拟键代码之间的映射。有关键盘输入的完整说明,请参阅the MSDN documentation

通过颠倒这个查找过程,可以确定与每个虚拟键码相对应的扫描码(当然,由于shift/ctrl/alt状态等,相同的扫描码将映射到多个VK码)。 Win32 API通过使用MAPVK_VK_TO_VSC_EX选项提供MapVirtualKeyEx函数来执行此映射。您可以使用它来确定哪个扫描代码生成特定的VK代码。

不幸的是,这是尽可能以编程方式 - 无法确定键盘的物理布局或每个扫描代码的密钥位置。但是,大多数物理键盘的布线方式相同,因此(例如)左上角的键在大多数物理键盘设计上都具有相同的扫描代码。根据基本的物理键盘布局(101键,102键等),您可以使用此假定惯例来推断与扫描代码对应的物理位置。这并不能保证,但这是一个非常安全的猜测。

下面的代码是我写的一个更大的键盘处理库的摘录(我一直打算开源,但没有时间)。该方法初始化一个数组(this._virtualKeyScanCodes),该数组是以VK代码为索引的给定输入语言环境(存储在this._inputLanguage中,声明为System.Windows.Forms.InputLanguage。可以使用该数组来确定与VK代码对应的扫描代码,例如通过检查this._virtualKeyScanCodes[VK_NUMPAD0] - 如果扫描码为零,那么在当前输入区域设置中,键盘上的VK不可用;如果扫描码不为零,则为您可以从中推断出物理键的扫描码。

不幸的是,当您进入死键(例如,生成重音字符的多个组合键)的领域时,事情稍微复杂一些。这对于现在来说太复杂了,但如果你想进一步探索,Michael S. Kaplan写了一系列详细的blog posts。祝你好运!

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); 
} 

编辑:另见this question这与您的相似。

2

没有包含键盘布局的内置.NET类。键盘布局是操作系统的功能,通常是Windows。在.NET介入时,按下的键已从硬件事件转换为软件事件。如果你想看到这个动作,找到两个键盘布局,其中一个键在它们之间移动。使用Key_Down事件上的事件处理程序设置虚拟应用程序,然后注意事件参数是相同的;如果按下了密钥,则无论-密钥位于何处,您都按下了密钥-