C#에 DataGrid를 다루다 보면 cell BeginEdit(셀 에디트를 위해)를 호출 하게 되는데 , 이때 영어 같은 경우는 합성 문자가 아니기 때문에 상관이 없지만, 한글 같은 경우는 합성 문자이기때문에 첫번째 타이핑을 할때 IME_Composition 인 상태의 커서를 만들어야 한글 입력이 제대로 된다.
하지만 DataGrid(WPF)의 경우에는 딱히 지원이 없어 보인다. 또 나같은 경우는 한글키 입력 일경우에는 KEY_DOWN 메세지 자체를 프로그램에서 캐치 하지 못했다. 해서 전역 후킹을 통해서 이문제를 해결했다.
@소스코드
#region 전역 후킹
const int VK_PROCESSKEY = 0xE5;
const int WM_IME_COMPOSITION = 0x10F;
const int WM_IME_ENDCOMPOSITION = 0x10E;
const int KEYEVENTF_EXTENDEDKEY = 0x1;
const int KEYEVENTF_KEYUP = 0x2;
[DllImport("user32.dll")]
static extern bool keybd_event(byte bVk, byte bScan, uint dwFlags, int dwExtraInfo);
[DllImport("user32.dll")]
static extern IntPtr SetWindowsHookEx(int idHook, LowLevelKeyboardProc callback, IntPtr hInstance, uint threadId);
[DllImport("user32.dll")]
static extern bool UnhookWindowsHookEx(IntPtr hInstance);
[DllImport("user32.dll")]
static extern IntPtr CallNextHookEx(IntPtr idHook, int nCode, int wParam, IntPtr lParam);
[DllImport("kernel32.dll")]
static extern IntPtr LoadLibrary(string lpFileName);
private delegate IntPtr LowLevelKeyboardProc(int nCode, IntPtr wParam, IntPtr lParam);
const int WH_KEYBOARD_LL = 13; // Номер глобального LowLevel-хука на клавиатуру
const int WM_KEYDOWN = 0x100; // Сообщения нажатия клавиши
const int WM_IME_STARTCOMPOSITION = 0x010D;
#region VirtualKey
public enum VKeys : int
{
VK_LBUTTON = 0x01, //Left mouse button
VK_RBUTTON = 0x02, //Right mouse button
VK_CANCEL = 0x03, //Control-break processing
VK_MBUTTON = 0x04, //Middle mouse button (three-button mouse)
VK_BACK = 0x08, //BACKSPACE key
VK_TAB = 0x09, //TAB key
VK_CLEAR = 0x0C, //CLEAR key
VK_RETURN = 0x0D, //ENTER key
VK_SHIFT = 0x10, //SHIFT key
VK_CONTROL = 0x11, //CTRL key
VK_MENU = 0x12, //ALT key
VK_PAUSE = 0x13, //PAUSE key
VK_CAPITAL = 0x14, //CAPS LOCK key
VK_HANGUL = 0x15,
VK_ESCAPE = 0x1B, //ESC key
VK_SPACE = 0x20, //SPACEBAR
VK_PRIOR = 0x21, //PAGE UP key
VK_NEXT = 0x22, //PAGE DOWN key
VK_END = 0x23, //END key
VK_HOME = 0x24, //HOME key
VK_LEFT = 0x25, //LEFT ARROW key
VK_UP = 0x26, //UP ARROW key
VK_RIGHT = 0x27, //RIGHT ARROW key
VK_DOWN = 0x28, //DOWN ARROW key
VK_SELECT = 0x29, //SELECT key
VK_PRINT = 0x2A, //PRINT key
VK_EXECUTE = 0x2B, //EXECUTE key
VK_SNAPSHOT = 0x2C, //PRINT SCREEN key
VK_INSERT = 0x2D, //INS key
VK_DELETE = 0x2E, //DEL key
VK_HELP = 0x2F, //HELP key
VK_0 = 0x30, //0 key
VK_1 = 0x31, //1 key
VK_2 = 0x32, //2 key
VK_3 = 0x33, //3 key
VK_4 = 0x34, //4 key
VK_5 = 0x35, //5 key
VK_6 = 0x36, //6 key
VK_7 = 0x37, //7 key
VK_8 = 0x38, //8 key
VK_9 = 0x39, //9 key
VK_A = 0x41, //A key
VK_B = 0x42, //B key
VK_C = 0x43, //C key
VK_D = 0x44, //D key
VK_E = 0x45, //E key
VK_F = 0x46, //F key
VK_G = 0x47, //G key
VK_H = 0x48, //H key
VK_I = 0x49, //I key
VK_J = 0x4A, //J key
VK_K = 0x4B, //K key
VK_L = 0x4C, //L key
VK_M = 0x4D, //M key
VK_N = 0x4E, //N key
VK_O = 0x4F, //O key
VK_P = 0x50, //P key
VK_Q = 0x51, //Q key
VK_R = 0x52, //R key
VK_S = 0x53, //S key
VK_T = 0x54, //T key
VK_U = 0x55, //U key
VK_V = 0x56, //V key
VK_W = 0x57, //W key
VK_X = 0x58, //X key
VK_Y = 0x59, //Y key
VK_Z = 0x5A, //Z key
VK_NUMPAD0 = 0x60, //Numeric keypad 0 key
VK_NUMPAD1 = 0x61, //Numeric keypad 1 key
VK_NUMPAD2 = 0x62, //Numeric keypad 2 key
VK_NUMPAD3 = 0x63, //Numeric keypad 3 key
VK_NUMPAD4 = 0x64, //Numeric keypad 4 key
VK_NUMPAD5 = 0x65, //Numeric keypad 5 key
VK_NUMPAD6 = 0x66, //Numeric keypad 6 key
VK_NUMPAD7 = 0x67, //Numeric keypad 7 key
VK_NUMPAD8 = 0x68, //Numeric keypad 8 key
VK_NUMPAD9 = 0x69, //Numeric keypad 9 key
VK_SEPARATOR = 0x6C, //Separator key
VK_SUBTRACT = 0x6D, //Subtract key
VK_DECIMAL = 0x6E, //Decimal key
VK_DIVIDE = 0x6F, //Divide key
VK_F1 = 0x70, //F1 key
VK_F2 = 0x71, //F2 key
VK_F3 = 0x72, //F3 key
VK_F4 = 0x73, //F4 key
VK_F5 = 0x74, //F5 key
VK_F6 = 0x75, //F6 key
VK_F7 = 0x76, //F7 key
VK_F8 = 0x77, //F8 key
VK_F9 = 0x78, //F9 key
VK_F10 = 0x79, //F10 key
VK_F11 = 0x7A, //F11 key
VK_F12 = 0x7B, //F12 key
VK_SCROLL = 0x91, //SCROLL LOCK key
VK_LSHIFT = 0xA0, //Left SHIFT key
VK_RSHIFT = 0xA1, //Right SHIFT key
VK_LCONTROL = 0xA2, //Left CONTROL key
VK_RCONTROL = 0xA3, //Right CONTROL key
VK_LMENU = 0xA4, //Left MENU key
VK_RMENU = 0xA5, //Right MENU key
VK_PLAY = 0xFA, //Play key
VK_ZOOM = 0xFB, //Zoom key
}
#endregion
private LowLevelKeyboardProc _proc = hookProc;
private static IntPtr hhook = IntPtr.Zero;
public void SetHook()
{
IntPtr hInstance = LoadLibrary("User32");
hhook = SetWindowsHookEx(WH_KEYBOARD_LL, _proc, hInstance, 0);
}
public static void UnHook()
{
UnhookWindowsHookEx(hhook);
}
public static IntPtr hookProc(int code, IntPtr wParam, IntPtr lParam)
{
if (code >= 0 && wParam == (IntPtr)WM_KEYDOWN && CommonObj._keyCount <= 0)
{
int vkCode = Marshal.ReadInt32(lParam);
//방향키나 엔터키 텝키 일경우에는 후킹 하지 않는다
VKeys vk_value = (VKeys)vkCode;
switch (vk_value)
{
case VKeys.VK_RETURN:
case VKeys.VK_TAB:
case VKeys.VK_UP:
case VKeys.VK_DOWN:
case VKeys.VK_LEFT:
case VKeys.VK_RIGHT:
case VKeys.VK_LCONTROL:
case VKeys.VK_RCONTROL:
case VKeys.VK_DELETE:
case VKeys.VK_HANGUL:
case VKeys.VK_LSHIFT:
case VKeys.VK_RSHIFT:
return CallNextHookEx(hhook, code, (int)wParam, lParam);
}
//사용자의 DataGrid를 EditMode로 전환 한다.
_dataGrid.BeginEdit();
//후킹한 키보드를 강제로 입력 한다.
keybd_event((byte)Marshal.ReadByte(lParam), 0, KEYEVENTF_EXTENDEDKEY, 0);
return (IntPtr)1;
}
else
return CallNextHookEx(hhook, code, (int)wParam, lParam);
}
#endregion
SetHook() 을 호출하면 키보드 후킹이 시작되고, UnHook()를 호출하면 후킹이 종료 된다.
C#프로그램을 하다가 막히면 결국 WinApi를 사용해서 해결해야 하니, 아무래도 윈도우 Application 개발자는 C++(WinApi)을 기본적으로 알면 많은 도움이 될것 같다.