EternalWindows
コンボボックス / アイテムの削除

今回は、コンボボックスに追加されたアイテムを削除する方法について説明します。

#include <windows.h>

#define ID_BUTTON 100

WNDPROC g_lpfnDefComboBoxProc = NULL;

LRESULT CALLBACK NewComboBoxProc(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam);
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 hwndComboBox = NULL;

	switch (uMsg) {
		
	case WM_CREATE:
		CreateWindowEx(0, TEXT("BUTTON"), TEXT("全て削除"), WS_CHILD | WS_VISIBLE, 220, 30, 80, 25, hwnd, (HMENU)ID_BUTTON, ((LPCREATESTRUCT)lParam)->hInstance, NULL);
		hwndComboBox = CreateWindowEx(0, TEXT("COMBOBOX"), TEXT(""), WS_CHILD | WS_VISIBLE | CBS_DROPDOWNLIST, 30, 30, 170, 140, hwnd, (HMENU)1, ((LPCREATESTRUCT)lParam)->hInstance, NULL);
		SendMessage(hwndComboBox, CB_ADDSTRING, 0, (LPARAM)TEXT("item1"));
		SendMessage(hwndComboBox, CB_ADDSTRING, 0, (LPARAM)TEXT("item2"));
		SendMessage(hwndComboBox, CB_ADDSTRING, 0, (LPARAM)TEXT("item3"));

		g_lpfnDefComboBoxProc = (WNDPROC)GetWindowLongPtr(hwndComboBox, GWLP_WNDPROC);
		SetWindowLongPtr(hwndComboBox, GWLP_WNDPROC, (LONG_PTR)NewComboBoxProc);

		return 0;

	case WM_COMMAND:
		if (LOWORD(wParam) == ID_BUTTON)
			SendMessage(hwndComboBox, CB_RESETCONTENT, 0, 0);
		return 0;

	case WM_DESTROY:
		SetWindowLongPtr(hwndComboBox, GWLP_WNDPROC, (LONG_PTR)g_lpfnDefComboBoxProc);
		PostQuitMessage(0);
		return 0;

	default:
		break;

	}

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

LRESULT CALLBACK NewComboBoxProc(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
{
	switch (uMsg) {

	case WM_KEYDOWN: {
		if (wParam == VK_DELETE) {
			int  nIndex;
			BOOL bDropDownShow;
			
			bDropDownShow = (BOOL)SendMessage(hwnd, CB_GETDROPPEDSTATE, 0, 0);
			if (bDropDownShow) { 
				nIndex = (int)SendMessage(hwnd, CB_GETCURSEL, 0, 0);
				SendMessage(hwnd, CB_DELETESTRING, nIndex, 0);
			}
		}
		break;
	}

	default:
		break;

	}

	return CallWindowProc(g_lpfnDefComboBoxProc, hwnd, uMsg, wParam, lParam);
}

全て削除というボタンが押された場合は、コンボボックスの全てのアイテムが削除されます。 これは、CB_RESETCONTENTを送信すれば実現できます。 一方、コンボボックスをドロップダンで表示し、そこでDelキーを押した場合は選択されているアイテムだけが削除されます。 これは、CB_DELETESTRINGで実現されています。 コンボボックス上でDelキーを押しても、それは親ウインドウへWM_KEYDOWNとして通知されませんから、 コンボボックスをサブクラス化しています。

g_lpfnDefComboBoxProc = (WNDPROC)GetWindowLongPtr(hwndComboBox, GWLP_WNDPROC);
SetWindowLongPtr(hwndComboBox, GWLP_WNDPROC, (LONG_PTR)NewComboBoxProc);

まず、GetWindowLongPtrで変更前のコンボボックスのウインドウプロシージャを取得します。 これは、新しいウインドウプロシージャでデフォルト処理を行う場合に必要です。 次に、SetWindowLongPtrで新しいウインドウプロシージャを指定します。 これにより、コンボボックスへのメッセージはNewComboBoxProcに送られることになりますから、 ここでWM_KEYDOWNを検出すればよいことになります。

case WM_KEYDOWN: {
	if (wParam == VK_DELETE) {
		int  nIndex;
		BOOL bDropDownShow;
		
		bDropDownShow = (BOOL)SendMessage(hwnd, CB_GETDROPPEDSTATE, 0, 0);
		if (bDropDownShow) { 
			nIndex = (int)SendMessage(hwnd, CB_GETCURSEL, 0, 0);
			SendMessage(hwnd, CB_DELETESTRING, nIndex, 0);
		}
	}
	break;
}

wParamがVK_DELETEである場合は、Delキーが押されたことを意味します。 まず、CB_GETDROPPEDSTATEでドロップダウンが表示されているかを確認し、 表示されている場合はCB_GETCURSELで選択されているアイテムのインデックスを取得します。 そして、このインデックスをCB_DELETESTRINGに指定すれば、 選択されたアイテムがDelキーの押下で削除されたことになります。


戻る