2011-01-27 17 views
6

Se ho bisogno di generare un layout di tastiera per la personalizzazione all'utente che assomiglia alla sua tastiera, come posso farlo?È possibile creare un layout di tastiera identico alla tastiera utilizzata?

Per esempio qualcosa di simile:

enter image description here

francese, svedese, inglese, canadese, ecc avrà diversi layout, a destra. Si tratta di un sacco di lavoro o solo una questione di utilizzare una sorta di classi regionali .NET incorporate?

+4

Sembra un buon add-on per qualcuno da scrivere;) – Oded

+0

È possibile ottenere immagini dei layout di tastiera internazionali semplicemente utilizzando Google Immagini. Prova googling per [layout di tastiera francese] (http://www.google.com/images?hl=it&q=french+keyboard+layout), ad esempio. – Timwi

+0

Sì, ma ho bisogno di disegnarlo da zero con uno stile predefinito. Inoltre, se utilizzo i layout, avrei bisogno di memorizzare tutte le immagini, giusto? Devo farlo dinamicamente al runtime (quando viene avviata la finestra di dialogo Personalizza). –

risposta

8

Un tasto stampa genera un evento hardware che segnala un "codice di scansione" al sistema operativo Windows. Questo codice di scansione viene poi convertito in un "codice tasto virtuale", basato sul codice di scansione insieme ad altri fattori stato della tastiera (Caps Lock stato, Maiusc/Alt/Ctrl keystate, e anche un tasto qualsiasi morti in attesa ictus). Il valore VK convertito è quello che viene segnalato dall'evento KeyDown ecc.

La conversione dal codice di scansione al codice VK dipende dalla locale di input corrente. Semplicisticamente, la locale di input definisce una mappatura tra codici di scansione e codici di chiave virtuali. Vedi the MSDN documentation per una descrizione completa dell'input da tastiera.

Invertendo questo processo di ricerca, è possibile determinare il codice di scansione che corrisponde a ciascun codice tasto virtuale (ovviamente, lo stesso codice di scansione si associerà a più codici VK a causa di shift/ctrl/alt stato ecc.). L'API Win32 fornisce la funzione MapVirtualKeyEx per eseguire questa mappatura, utilizzando l'opzione MAPVK_VK_TO_VSC_EX. È possibile utilizzare questo per determinare quale codice di scansione genera il particolare codice VK.

Purtroppo, questo è quanto si può andare di programmazione - non v'è alcun modo per determinare la disposizione fisica della tastiera o la posizione della chiave per ogni codice di scansione. Tuttavia, la maggior parte delle tastiere fisiche sono cablate allo stesso modo, quindi (ad esempio) il tasto in alto a sinistra avrà lo stesso codice di scansione sulla maggior parte dei progetti di tastiera fisica. È possibile utilizzare questa convenzione presunta per dedurre la posizione fisica corrispondente a un codice di scansione, a seconda del layout di base della tastiera fisica (101 tasti, 102 tasti, ecc.). Non è garantito, ma è un'ipotesi abbastanza sicura.

Il codice seguente è un estratto da una grande libreria di tastiera di gestione che ho scritto (ho l'intenzione di open-source, ma non ho avuto il tempo). Il metodo inizializza una matrice (this._virtualKeyScanCodes) indicizzata dal codice VK per una determinata lingua di input (memorizzata in this._inputLanguage che è dichiarata come System.Windows.Forms.InputLanguage. È possibile utilizzare la matrice per determinare il codice di scansione che corrisponde al codice VK esaminando ad esempio this._virtualKeyScanCodes[VK_NUMPAD0] - se il codice di scansione è zero, quel VK non è disponibile sulla tastiera nella locale di input corrente, se è diverso da zero, è il codice di scansione da cui è possibile dedurre la chiave fisica.

Sfortunatamente, le cose sono leggermente più complicate di questo quando si entra nel regno delle chiavi morte (combinazioni di tasti multipli che producono caratteri accentati, per esempio). È tutto troppo complicato per entrare in questo momento, ma Michael S. Kaplan ha scritto una serie dettagliata di blog posts se vuoi approfondire ulteriormente. In bocca al lupo!

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

Edit: vedi anche this question che è simile alla tua.

+2

+1 Questo è meglio di quello di cui stavamo discutendo nei commenti sulla mia risposta :) – GWLlosa

2

Non esiste una classe .NET integrata che contenga i layout di tastiera. I layout della tastiera sono una funzione del sistema operativo, in genere Windows. Quando .NET viene coinvolto, il tasto premuto è stato convertito da un evento hardware a uno software. Se vuoi vedere questo in azione, trova 2 layout di tastiera in cui una chiave si è spostata tra di loro. Impostare un'app fittizia con un gestore di eventi sull'evento Key_Down e quindi notare che gli argomenti dell'evento sono identici; se hai premuto il tasto -, hai premuto il tasto - indipendentemente da dove si trova la chiave -.

+0

Grazie, ha senso. Ma come fanno questi per dire le tastiere cinesi e russe? Ti piace quale chiave restituisce Keys.A, ecc. Inoltre, come fanno alcune app a fare ciò dove sono in grado di ricreare il layout della tastiera che stai usando sullo schermo. La tastiera OSD di Windows lo fa? –

+0

Possono utilizzare l'OSD di Windows; dal momento che si tratta di un'utilità di sistema operativo, è infatti a conoscenza del layout; vedere http://www.microsoft.com/windowsxp/using/accessibility/osklayout.mspx – GWLlosa

+0

Grazie, ma come possono usarlo? Non è un'altra app come non sembra abbia un'API da collegare, giusto? –

Problemi correlati