コンボ ボックスをサブクラス化する方法
このトピックでは、コンボ ボックスをサブクラス化する方法について説明します。 C++ サンプル アプリケーションは、2 つのコンボ ボックスを含むツールバー ウィンドウを作成します。 コンボ ボックス内の編集コントロールをサブクラス化すると、ツールバー ウィンドウは TAB キー、ENTER キー、ESC キーを切り取り、それ以外の場合は無視されます。
知っておくべきこと
テクノロジ
前提条件
- C/C++
- Windows ユーザー インターフェイス プログラミング
手順
WM_CREATE メッセージの処理
アプリケーションは、WM_CREATE メッセージを処理して、子ウィンドウとして 2 つのコンボ ボックス コントロールを作成します。
// Create two combo box child windows.
dwBaseUnits = GetDialogBaseUnits();
hwndCombo1 = CreateWindow(L"COMBOBOX", L"",
CBS_DROPDOWN | WS_CHILD | WS_VISIBLE,
(6 * LOWORD(dwBaseUnits)) / 4,
(2 * HIWORD(dwBaseUnits)) / 8,
(100 * LOWORD(dwBaseUnits)) / 4,
(50 * HIWORD(dwBaseUnits)) / 8,
hwnd, NULL, NULL, NULL);
hwndCombo2 = CreateWindow(L"COMBOBOX", L"",
CBS_DROPDOWN | WS_CHILD | WS_VISIBLE,
(112 * LOWORD(dwBaseUnits)) / 4,
(2 * HIWORD(dwBaseUnits)) / 8,
(100 * LOWORD(dwBaseUnits)) / 4,
(50 * HIWORD(dwBaseUnits)) / 8,
hwnd, NULL, hInst, NULL);
その後、アプリケーションは各コンボ ボックスの編集コントロール (選択フィールド) をサブクラス化します。これは、単純なコンボ ボックスとドロップダウン コンボ ボックスの文字入力を受け取るためです。 最後に、ChildWindowFromPoint 関数を呼び出して、各編集コントロールへのハンドルを取得します。
編集コントロールをサブクラス化するために、アプリケーションは SetWindowLong 関数を呼び出し、クラス ウィンドウ プロシージャへのポインターを、アプリケーション定義の SubClassProc 関数へのポインターに置き換えます。 元のウィンドウ プロシージャへのポインターは、グローバル変数 lpfnEditWndProc に保存されます。
SubClassProc は TAB、ENTER、ESC キーをインターセプトし、アプリケーション定義メッセージ (WM_TAB、WM_ESC、およびWM_ENTER) を送信してツールバー ウィンドウに通知します。 SubClassProc は CallWindowProc 関数を使用して、ほとんどのメッセージを元のウィンドウ プロシージャ lpfnEditWndProc に渡します。
// Get the edit window handle to each combo box.
pt.x = 1;
pt.y = 1;
hwndEdit1 = ChildWindowFromPoint(hwndCombo1, pt);
hwndEdit2 = ChildWindowFromPoint(hwndCombo2, pt);
// Change the window procedure for both edit windows
// to the subclass procedure.
lpfnEditWndProc = (WNDPROC) SetWindowLongPtr(hwndEdit1,
GWLP_WNDPROC, (LONG_PTR) SubClassProc);
SetWindowLongPtr(hwndEdit2, GWLP_WNDPROC, (LONG_PTR) SubClassProc);
WM_SETFOCUS メッセージを処理する
ツールバー ウィンドウは、入力フォーカスを受け取ると、すぐにツールバーの最初のコンボ ボックスにフォーカスを設定します。 これを行うには、WM_SETFOCUS メッセージに応答して SetFocus 関数を呼び出します。
case WM_SETFOCUS:
SetFocus(hwndCombo1);
break;
アプリケーション定義メッセージの処理
SubClassProc 関数は、ユーザーがコンボ ボックスの Tab キー、Enter キー、または ESC キーを押したときに、アプリケーション定義のメッセージをツールバー ウィンドウに送信します。 TAB キーに対して WM_TAB メッセージが送信され、ESC キーに対して WM_ESC メッセージが送信され、ENTER キーに対して WM_ENTER メッセージが送信されます。
この例では、フォーカスをツールバーの次のコンボ ボックスに設定して、WM_TAB メッセージを処理します。 フォーカスをメインのアプリケーション ウィンドウに設定することで、WM_ESC メッセージを処理します。
case WM_TAB:
if (GetFocus() == hwndEdit1)
SetFocus(hwndCombo2);
else
SetFocus(hwndCombo1);
break;
case WM_ESC:
hwndCombo = GetFocus() == hwndEdit1 ? hwndCombo1 : hwndCombo2;
// Clear the current selection.
SendMessage(hwndCombo, CB_SETCURSEL,
(WPARAM) (-1), 0);
// Set the focus to the main window.
SetFocus(hwndMain);
break;
WM_ENTER メッセージに応答して、コンボ ボックスの現在の選択が有効であることを確認したら、フォーカスをメインのアプリケーション ウィンドウに設定します。 コンボ ボックスに現在の選択範囲がない場合、この例では、CB_FINDSTRINGEXACT メッセージを使用して、選択フィールドの内容と一致するリスト アイテムを検索します。 一致するものがある場合は、現在の選択を設定します。それ以外の場合は、新しいリスト アイテムが追加されます。
case WM_ENTER:
hwndCombo = GetFocus() == hwndEdit1 ? hwndCombo1 : hwndCombo2;
SetFocus(hwndMain);
// If there is no current selection, set one.
if (SendMessage(hwndCombo, CB_GETCURSEL, 0, 0)
== CB_ERR)
{
if (SendMessage(hwndCombo, WM_GETTEXT,
sizeof(achTemp), (LPARAM) achTemp) == 0)
break; // Empty selection field
dwIndex = SendMessage(hwndCombo,
CB_FINDSTRINGEXACT, (WPARAM) (-1),
(LPARAM) achTemp);
// Add the string, if necessary, and select it.
if (dwIndex == CB_ERR)
dwIndex = SendMessage(hwndCombo, CB_ADDSTRING,
0, (LPARAM) achTemp);
if (dwIndex != CB_ERR)
SendMessage(hwndCombo, CB_SETCURSEL,
dwIndex, 0);
}
break;
// .
// . Process additional messages.
// .
default:
return DefWindowProc(hwnd, msg, wParam, lParam);
コード例全体
ツールバーのウィンドウ プロシージャと、2 つのコンボ ボックスのサブクラス プロシージャを次に示します。
#define WM_TAB (WM_USER)
#define WM_ESC (WM_USER + 1)
#define WM_ENTER (WM_USER + 2)
// Global variables
HWND hwndMain;
WNDPROC lpfnEditWndProc; // Original wndproc for the combo box
// Prototypes
LRESULT CALLBACK SubClassProc( HWND, UINT, WPARAM, LPARAM );
/********************************************************
FUNCTION: ToolbarWindowProc
PURPOSE: Window procedure for the toolbar window
*********************************************************/
LRESULT CALLBACK ToolbarWindowProc(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam)
{
static HWND hwndEdit1;
static HWND hwndEdit2;
static HWND hwndCombo1;
static HWND hwndCombo2;
POINT pt;
DWORD dwBaseUnits;
HWND hwndCombo;
DWORD dwIndex;
char achTemp[256]; // Temporary buffer
switch (msg)
{
case WM_CREATE:
// Create two combo box child windows.
dwBaseUnits = GetDialogBaseUnits();
hwndCombo1 = CreateWindow(L"COMBOBOX", L"",
CBS_DROPDOWN | WS_CHILD | WS_VISIBLE,
(6 * LOWORD(dwBaseUnits)) / 4,
(2 * HIWORD(dwBaseUnits)) / 8,
(100 * LOWORD(dwBaseUnits)) / 4,
(50 * HIWORD(dwBaseUnits)) / 8,
hwnd, NULL, NULL, NULL);
hwndCombo2 = CreateWindow(L"COMBOBOX", L"",
CBS_DROPDOWN | WS_CHILD | WS_VISIBLE,
(112 * LOWORD(dwBaseUnits)) / 4,
(2 * HIWORD(dwBaseUnits)) / 8,
(100 * LOWORD(dwBaseUnits)) / 4,
(50 * HIWORD(dwBaseUnits)) / 8,
hwnd, NULL, hInst, NULL);
// Get the edit window handle to each combo box.
pt.x = 1;
pt.y = 1;
hwndEdit1 = ChildWindowFromPoint(hwndCombo1, pt);
hwndEdit2 = ChildWindowFromPoint(hwndCombo2, pt);
// Change the window procedure for both edit windows
// to the subclass procedure.
lpfnEditWndProc = (WNDPROC) SetWindowLongPtr(hwndEdit1,
GWLP_WNDPROC, (LONG_PTR) SubClassProc);
SetWindowLongPtr(hwndEdit2, GWLP_WNDPROC, (LONG_PTR) SubClassProc);
break;
case WM_SETFOCUS:
SetFocus(hwndCombo1);
break;
case WM_TAB:
if (GetFocus() == hwndEdit1)
SetFocus(hwndCombo2);
else
SetFocus(hwndCombo1);
break;
case WM_ESC:
hwndCombo = GetFocus() == hwndEdit1 ? hwndCombo1 : hwndCombo2;
// Clear the current selection.
SendMessage(hwndCombo, CB_SETCURSEL,
(WPARAM) (-1), 0);
// Set the focus to the main window.
SetFocus(hwndMain);
break;
case WM_ENTER:
hwndCombo = GetFocus() == hwndEdit1 ? hwndCombo1 : hwndCombo2;
SetFocus(hwndMain);
// If there is no current selection, set one.
if (SendMessage(hwndCombo, CB_GETCURSEL, 0, 0)
== CB_ERR)
{
if (SendMessage(hwndCombo, WM_GETTEXT,
sizeof(achTemp), (LPARAM) achTemp) == 0)
break; // Empty selection field
dwIndex = SendMessage(hwndCombo,
CB_FINDSTRINGEXACT, (WPARAM) (-1),
(LPARAM) achTemp);
// Add the string, if necessary, and select it.
if (dwIndex == CB_ERR)
dwIndex = SendMessage(hwndCombo, CB_ADDSTRING,
0, (LPARAM) achTemp);
if (dwIndex != CB_ERR)
SendMessage(hwndCombo, CB_SETCURSEL,
dwIndex, 0);
}
break;
// .
// . Process additional messages.
// .
default:
return DefWindowProc(hwnd, msg, wParam, lParam);
}
return 0;
}
/********************************************************
FUNCTION: SubClassProc
PURPOSE: Process TAB and ESCAPE keys, and pass all
other messages to the class window
procedure.
*********************************************************/
LRESULT CALLBACK SubClassProc(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam)
{
switch (msg)
{
case WM_KEYDOWN:
switch (wParam)
{
case VK_TAB:
SendMessage(hwndMain, WM_TAB, 0, 0);
return 0;
case VK_ESCAPE:
SendMessage(hwndMain, WM_ESC, 0, 0);
return 0;
case VK_RETURN:
SendMessage(hwndMain, WM_ENTER, 0, 0);
return 0;
}
break;
case WM_KEYUP:
case WM_CHAR:
switch (wParam)
{
case VK_TAB:
case VK_ESCAPE:
case VK_RETURN:
return 0;
}
}
// Call the original window procedure for default processing.
return CallWindowProc(lpfnEditWndProc, hwnd, msg, wParam, lParam);
}
関連トピック