One of the things that PC games often need is a form of in-game keyboard text input. In my case, I needed a login screen that allows the user to type in their username and password before the game continues on. It turns out that this process is more complicated in XNA than I would have wished, so I post my solution here in hopes that it may help others with a similar need.
The first solution that comes to mind is to use the KeyboardState.GetPressedKeys() method to get all the keys currently pressed on the keyboard. Running this each frame will, in theory, enable us to get all the keys the player pressed, right? While this seems simple enough, this method will not work for several reasons. Firstly, GetPressedKeys() is not buffered, meaning if you type keys faster than the update rate of your game you will get missing keys. In simple terms, a user typing faster than 60wpm (if your game runs at 60fps) will have issues. Another issue is that the return array does not give any indication of the order in which keys are pressed, so that gives you no idea on how to construct the final string if two or more keys are pressed simultaneously. The biggest problem is that you have to do all the key to char translation yourself. GetPressedKeys does not return a nice char or string array that contains the actual value you typed. No, it returns an array of the Keys enumeration. This forces the programmer to either build a huge Dictionary that maps each Key to a character value, or build a huge switch(Keys) statement that checks through some 50+ odd cases and handles each one. But even with all this work this method will not function, because Keys does not differentiate between case, meaning you have to handle additional translation if you want the character to be lowercase. Add that to the fact that the Shift key combinations (e.g. Shift+1 = !) and localization are not supported leaves you in some seriously rough waters.
That method being wrought with issues, we have to look for another method to resolve the problem. The next logical solution is to hook into the KeyDown, KeyPress, and KeyUp events on the XNA form. Alas, this too is filled with issues. For one, the XNA form will never get the KeyDown or KeyPress events. It appears that the form eats them up before you can use them. The other issue is that the KeyUp event passes a Keys enum…which gives us the same translation and localization problems as using the KeyboardState.GetPressedKeys() method.
So…where can we go from here? After doing some research I heard mention of the IMessageFilter interface. This interface allows messages to be intercepted before they are dispatched to the form, meaning we can grab the KeyDown and KeyPressed messages before the form devours them. Better yet, the messages are not transformed into the Keys enum, thus we can get the true translated key (WM_CHAR) that the user pressed as a character.
There is another alternative that involves creating a low level keyboard hook using SetWindowsHookEx and grabbing the key events from there, but that involves more pInvoked native calls and more complexity in general then the solution I came up with.
The solution I ended up using involves a bit of unsafe code and a pInvoked call to the user32.dll function TranslateMessage. I would have preferred to avoid native or unsafe calls, but I couldn’t get anything to work without them. The basic rundown is this: there is a classes called KeyFilter that inherits from the IMessageFilter interface. This class watches for two Window Message Code‘s. The first, 0×0100, is a key event. If the message has this ID we use the Marshal class to grab us some unmanaged memory, store the message in a pointer, and pass the message to the unmanaged TranslateMessage function. This function will translate the message into the true character that was pressed and post the 0×0102 (WM_CHAR) message to the form’s message queue. The WM_CHAR message happens to be the second message that KeyFilter looks for. Stored in this message’s WParam is the transformed char we have been wanting all along! All that needs done is to convert the thing from an unsigned to a char with a simple cast and we can use it!
So that is basically the process necessary to get true Keyboard input into your XNA game. All that needs done now is to set up some events in a class that will fire when a new character is inbound. I wrote a sample that goes along with this post. You can find it here.