EternalWindows
エディットコントロール / 入力支援

エディットコントロールに特定の文字だけを入力してもらいたい場合、 そのような文字の情報をユーザーに表示できたら便利であるといえます。 こうした入力を支援するためのメッセージとして、 Windows XPからはEM_SETCUEBANNERとEM_SHOWBALLOONTIPが追加されているため、 今回はこの使い方に注目してみたいと思います。 commctrl.hのインクルードが別途必要になることに注意してください。

#include <windows.h>
#include <commctrl.h>

#define ID_BUTTON 100

LRESULT CALLBACK WindowProc(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam);

int WINAPI WinMain(HINSTANCE hinst, HINSTANCE hinstPrev, LPSTR lpszCmdLine, int nCmdShow)
{
	TCHAR      szAppName[] = TEXT("sample");
	HWND       hwnd;
	MSG        msg;
	WNDCLASSEX wc;

	wc.cbSize        = sizeof(WNDCLASSEX);
	wc.style         = 0;
	wc.lpfnWndProc   = WindowProc;
	wc.cbClsExtra    = 0;
	wc.cbWndExtra    = 0;
	wc.hInstance     = hinst;
	wc.hIcon         = (HICON)LoadImage(NULL, IDI_APPLICATION, IMAGE_ICON, 0, 0, LR_SHARED);
	wc.hCursor       = (HCURSOR)LoadImage(NULL, IDC_ARROW, IMAGE_CURSOR, 0, 0, LR_SHARED);
	wc.hbrBackground = (HBRUSH)GetStockObject(WHITE_BRUSH);
	wc.lpszMenuName  = NULL;
	wc.lpszClassName = szAppName;
	wc.hIconSm       = (HICON)LoadImage(NULL, IDI_APPLICATION, IMAGE_ICON, 0, 0, LR_SHARED);
	
	if (RegisterClassEx(&wc) == 0)
		return 0;

	hwnd = CreateWindowEx(0, szAppName, szAppName, WS_OVERLAPPEDWINDOW, CW_USEDEFAULT, CW_USEDEFAULT, CW_USEDEFAULT, CW_USEDEFAULT, NULL, NULL, hinst, NULL);
	if (hwnd == NULL)
		return 0;

	ShowWindow(hwnd, nCmdShow);
	UpdateWindow(hwnd);
	
	while (GetMessage(&msg, NULL, 0, 0) > 0) {
		TranslateMessage(&msg);
		DispatchMessage(&msg);
	}

	return (int)msg.wParam;
}

LRESULT CALLBACK WindowProc(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
{
	static HWND hwndEdit = NULL;
	
	switch (uMsg) {
		
	case WM_CREATE:
		hwndEdit = CreateWindowEx(0, TEXT("EDIT"), TEXT(""), WS_CHILD | WS_VISIBLE | WS_BORDER, 30, 30, 180, 30, hwnd, (HMENU)1, ((LPCREATESTRUCT)lParam)->hInstance, NULL);		
		CreateWindowEx(0, TEXT("BUTTON"), TEXT("送信"), WS_CHILD | WS_VISIBLE, 220, 30, 60, 30, hwnd, (HMENU)ID_BUTTON, ((LPCREATESTRUCT)lParam)->hInstance, NULL);

		SetFocus(hwndEdit);
		SendMessage(hwndEdit, EM_SETCUEBANNER, 1, (LPARAM)TEXT("アルファベットの入力"));
		return 0;

	case WM_COMMAND:
		if (LOWORD(wParam) == ID_BUTTON) {
			int            i, nCount;
			WORD           wType;
			TCHAR          szBuf[256];
			EDITBALLOONTIP balloonTip;

			nCount = GetWindowTextLength(hwndEdit);
			GetWindowText(hwndEdit, szBuf, sizeof(szBuf) / sizeof(TCHAR));

			for (i = 0; i < nCount; i++) {
				GetStringTypeEx(LOCALE_SYSTEM_DEFAULT, CT_CTYPE3, &szBuf[i], sizeof(TCHAR), &wType);
				if (!(wType & C3_HALFWIDTH) || !(wType & C3_ALPHA)) {
					SendMessage(hwndEdit, EM_SETSEL, i, i + 1);
					
					balloonTip.cbStruct = sizeof(EDITBALLOONTIP);
					balloonTip.pszText  = L"アルファベットではありません";
					balloonTip.pszTitle = L"入力エラー";
					balloonTip.ttiIcon  = TTI_ERROR;

					SendMessage(hwndEdit, EM_SHOWBALLOONTIP, 0, (LPARAM)&balloonTip);
					return 0;
				}
			}

			MessageBox(NULL, szBuf, TEXT("OK"), MB_OK);
		}
		return 0;

	case WM_DESTROY:
		PostQuitMessage(0);
		return 0;

	default:
		break;

	}

	return DefWindowProc(hwnd, uMsg, wParam, lParam);
}

エディットコントロールにアルファベットのみが入力されることを望むために、 EM_SETCUEBANNERを送信しています。 このメッセージは、LPARAMに指定した文字列を薄い灰色でエディットコントロール上に表示し、 WPARAMの条件を満たした場合に文字列は自動的に消去されます。 WPARAMが0の場合は、フォーカスが割り当てられた場合に文字列が消去され、 1の場合はキー入力やマウスクリックが行われた場合に文字列が消去されます。

送信ボタンが押された場合は、入力された文字列にアルファベット以外の文字が含まれていないかを確認しています。 もし、含まれている場合はその文字を選択し、 バルーンチップを表示することでエラーを伝えます。

for (i = 0; i < nCount; i++) {
	GetStringTypeEx(LOCALE_SYSTEM_DEFAULT, CT_CTYPE3, &szBuf[i], sizeof(TCHAR), &wType);
	if (!(wType & C3_HALFWIDTH) || !(wType & C3_ALPHA)) {
		SendMessage(hwndEdit, EM_SETSEL, i, i + 1);
		
		balloonTip.cbStruct = sizeof(EDITBALLOONTIP);
		balloonTip.pszText  = L"アルファベットではありません";
		balloonTip.pszTitle = L"入力エラー";
		balloonTip.ttiIcon  = TTI_ERROR;

		SendMessage(hwndEdit, EM_SHOWBALLOONTIP, 0, (LPARAM)&balloonTip);
		return 0;
	}
}

nCountには、エディットコントロールに入力された文字列の文字数が格納されているため、 この数だけ文字のタイプを確認することになります。 GetStringTypeExは、第5引数に文字のタイプを返す関数であり、 これにC3_HALFWIDTH(半角)が含まれていない、またはC3_ALPHA(アルファベット)が含まれていない場合は、 その文字がアルファベット以外の文字列であることを意味します。 よって、まずEM_SETSELでその文字を選択し、 次にEDITBALLOONTIP構造体を初期化してEM_SHOWBALLOONTIPを送信します。 ttiIconに指定できる定数は、警告のアイコンを示すTTI_WARNINGや大きい警告のアイコンを示すTTI_WARNING_LARGEなどがあります。

文字の入力制限について

今回は、アルファベット以外の文字が入力されていないことを確認しましたが、 このように後から確認作業行うことは決して効率的であるとはいえません。 そもそも、最初からアルファベット以外の文字を入力できないようにしておけばよいように思えますが、 実はこれがそれほど容易なことではないのです。 エディットコントロールに対するキー入力は、 エディットコントロールをサブクラス化することによってWM_KEYDOWNから検出することができますが、 このメッセージはあくまで通知という位置づけであり、 キー入力をフィルタすることはできません。 このような場合は、SetWindowsHookExによるキーボードフックを利用することになるでしょう。

実を言うと、エディットコントロールはいくつかの入力制限を既定でサポートしています。 たとえば、ES_NUMBERをウインドウスタイルに指定すると数字しか入力できなくなりますし、 EM_LIMITTEXTを送信すれば入力できる文字数を制限できます。

hwndEdit = CreateWindowEx(0, TEXT("EDIT"), TEXT(""), WS_CHILD | WS_VISIBLE | WS_BORDER | ES_NUMBER, ...);
SendMessage(hwndEdit, EM_LIMITTEXT, 4, 0);

この例では、ウインドウスタイルにES_NUMBERを指定し、 さらにEM_LIMITTEXTのWPARAMに4を指定していることから、 エディットコントロールには4つの数字しか入力することはできません。 なお、入力され文字を隠蔽したい場合は、ウインドウスタイルにES_PASSWORDを指定することができます。



戻る