EternalWindows
エディットコントロール / 複数行入力

今回は、複数行のエディットコントロールを作成する方法と、 特定の文字を選択する方法について取り上げます。 まず、次のエディットコントロールを見てください。

上図では、bという文字が現在選択されています。 このような選択をアプリケーションから行うには、EM_SETSELを送信します。

SendMessage(hwndEdit, EM_SETSEL, 1, 2);

EM_SETSELのWPARAMには、選択を開始する文字の位置を指定します。 文字の位置はゼロベースで表され、bという文字はこのエディットコントロールの1番目の文字なので、1を指定しています。 LPARAMは選択を終了する位置であり、上記では2を指定していることから、 2番目の文字であるcの前まで(つまりbだけ)が選択されることになります。 逆に、cも選択したい場合は、LPARAMに3を指定することになります。 WPARAMとLPARAMが同じ値である場合は、キャレットが指定位置に移動するだけで文字が選択されることはありません。

さて、それでは上記でeという文字を選択するにはどうすればよいでしょうか。 eという文字は4番目に存在しますから、WPARAMに4を指定すればよいように思えますが、実はそうではありません。 なぜなら、abcとdefの間には改行文字が存在しており、このような文字も文字数としてカウントされることになっているからです。 上図のエディットコントロールに設定されている文字列は、次のように表すことができます。

TEXT("abc\r\ndef")

改行コードは\rと\nで表すことができ、これらは文字数としてカウントされることになっています。 よって、eという文字は先頭から6番目に存在することになるため、 eという文字を選択する場合はWPARAMに6、LPARAMに7を指定することになります。 このように、エディットコントロールにおける文字の位置は、 先頭からの文字数によって表され、行や列という単位で表されないことに注意してください。

選択されている文字の位置を取得したい場合は、EM_GETSELを送信します。

DWORD dwStart, dwEnd;

SendMessage(hwndEdit, EM_GETSEL, (WPARAM)&dwStart, (LPARAM)&dwEnd);

dwStartに選択の開始位置が返り、dwEndに選択の終了位置が返ります。 両者の値が同一である場合は、文字が選択されていないことを意味します。 ちなみに、ここで取得した位置をEM_LINEFROMCHARに指定すると、 その文字が何行目に存在するかを特定できることになります。

今回のプログラムは、エディットコントロール上に入力された文字列の終端に文字を追加します。

#include <windows.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(WS_EX_CLIENTEDGE, TEXT("EDIT"), TEXT("abc\r\ndef"), WS_CHILD | WS_VISIBLE | ES_MULTILINE | ES_WANTRETURN | WS_VSCROLL, 30, 100, 150, 100, hwnd, (HMENU)1, ((LPCREATESTRUCT)lParam)->hInstance, NULL);		
		CreateWindowEx(0, TEXT("BUTTON"), TEXT("文字の挿入"), WS_CHILD | WS_VISIBLE, 30, 30, 100, 40, hwnd, (HMENU)ID_BUTTON, ((LPCREATESTRUCT)lParam)->hInstance, NULL);

		SetFocus(hwndEdit);
		return 0;

	case WM_COMMAND:
		if (LOWORD(wParam) == ID_BUTTON) {
			int nCount;

			nCount = GetWindowTextLength(hwndEdit);
			
			SendMessage(hwndEdit, EM_SETSEL, nCount, nCount);
			SendMessage(hwndEdit, EM_REPLACESEL, (WPARAM)0, (LPARAM)TEXT(";"));
		}

		return 0;

	case WM_DESTROY:
		PostQuitMessage(0);
		return 0;

	default:
		break;

	}

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

複数行のエディットコントロールを作成する場合は、ウインドウスタイルにES_MULTILINEを指定し、 Enterキーの押下を改行と見なすためにES_WANTRETURNを指定します。 また、スクロールバーを表示したい場合はWS_VSCROLLやWS_HSCROLLを指定することもできます。 拡張スタイルにWS_EX_CLIENTEDGEを指定するとウインドウに枠が表示されるようになります。

ボタンが押された場合は、文字列の終端に;が追加されることになります。 このためにはまず、キャレットを文字列の終端に移動しなければならないため、 GetWindowTextLengthで文字列の文字数を取得し、 これをEM_SETSELに指定します。 EM_REPLACESELは、現在のキャレットの位置にLPARAMの文字列を追加します。


戻る