今回は拡張コンボボックスと通知メッセージについて解説します。 と、言っても通知メッセージについては従来のコンボボックスの 通知メッセージと共通のものだけです。今回はダイアログボックス上の 拡張コンボボックスでアイテムが選択されたら、リアルタイムで 親ウィンドウのクライアント領域に「XXが選択されています。」と 表示させます。さらに、グローバル変数をひとつも使わないで プログラムを組んでみます。
今回作るプログラムのダイアログボックスは左の図のような 感じになります。イメージは選択されていてもいなくても同じです。
まず、左の図のようなビットマップ(mybmp.bmp)を用意します。 ひとつのイメージは16*15にしてあります。マスクしたいところを 「赤,RGB(255, 0, 0)」で塗りつぶしてあります。
では、プログラムをみてみましょう。
// comboex2.rcの一部
/////////////////////////////////////////////////////////////////////////////
//
// Menu
//
MYMENU MENU DISCARDABLE
BEGIN
POPUP "ファイル(&F)"
BEGIN
MENUITEM "終了(&X)", IDM_END
END
POPUP "オプション(&O)"
BEGIN
MENUITEM "ダイアログを出す(&D)", IDM_DLG
END
END
/////////////////////////////////////////////////////////////////////////////
//
// Dialog
//
MYDLG DIALOG DISCARDABLE 0, 0, 115, 49
STYLE DS_MODALFRAME | DS_CENTER | WS_POPUP | WS_CAPTION | WS_SYSMENU
CAPTION "拡張コンボボックス"
FONT 11, "MS Pゴシック"
BEGIN
DEFPUSHBUTTON "閉じる",IDOK,32,28,50,14
CONTROL "",IDC_COMBOBOXEX1,"ComboBoxEx32",CBS_DROPDOWN |
CBS_SORT | WS_VSCROLL | WS_TABSTOP,7,7,101,44
END
/////////////////////////////////////////////////////////////////////////////
//
// Bitmap
//
MYBMP BITMAP DISCARDABLE "mybmp.bmp"
今回は拡張コンボボックスをリソース・エディタに作らせています。 ダイアログボックスのフォントの大きさは11ポイントにしておいて下さい。 (デフォルトでは9ポイント。これではイメージの一部がコンボボックスの エディットコントロール部に表示されない。) 先ほどのmybmp.bmpは"MYBMP"という名前のリソースにしています。
// comboex2.cpp
#ifndef STRICT
#define STRICT
#endif
#define WM_MYMSG WM_USER
#include
#include
#include "resource.h"
LRESULT CALLBACK WndProc(HWND, UINT, WPARAM, LPARAM);
LRESULT CALLBACK MyDlgProc(HWND, UINT, WPARAM, LPARAM);
ATOM InitApp(HINSTANCE);
BOOL InitInstance(HINSTANCE, int);
void SetMyComboEx(HWND);
typedef struct {
HWND hWnd;
HINSTANCE hInst;
} MYDATA;
いつもと似ていますが、グローバル変数がひとつもないことに注意して下さい。
また自作メッセージWM_MYMSGを定義している点に注意して下さい。 さらに、自作のMYDATA型を定義している点にも注意して下さい。
int WINAPI WinMain(HINSTANCE hCurInst, HINSTANCE hPrevInst,
LPSTR lpsCmdLine, int nCmdShow)
{
MSG msg;
if (!InitApp(hCurInst))
return FALSE;
if (!InitInstance(hCurInst, nCmdShow))
return FALSE;
while (GetMessage(&msg, NULL, 0, 0)) {
TranslateMessage(&msg);
DispatchMessage(&msg);
}
return msg.wParam;
}
これは、いつもと同じです。
//ウィンドウ・クラスの登録
ATOM InitApp(HINSTANCE hInst)
{
WNDCLASSEX wc;
wc.cbSize = sizeof(WNDCLASSEX);
wc.style = CS_HREDRAW | CS_VREDRAW;
wc.lpfnWndProc = WndProc; //プロシージャ名
wc.cbClsExtra = 0;
wc.cbWndExtra = 0;
wc.hInstance = hInst;//インスタンス
wc.hIcon = LoadIcon(NULL, IDI_APPLICATION);
wc.hCursor = LoadCursor(NULL, IDC_ARROW);
wc.hbrBackground = (HBRUSH)GetStockObject(WHITE_BRUSH);
wc.lpszMenuName = "MYMENU"; //メニュー名
wc.lpszClassName = "comboex2";
wc.hIconSm = LoadIcon(NULL, IDI_APPLICATION);
return (RegisterClassEx(&wc));
}
lpszClassNameメンバのところがいつもと違います。
//ウィンドウの生成
BOOL InitInstance(HINSTANCE hInst, int nCmdShow)
{
HWND hWnd;
hWnd = CreateWindow("comboex2",
"猫でもわかる拡張コンボボックス", //タイトルバーにこの名前が表示されます
WS_OVERLAPPEDWINDOW, //ウィンドウの種類
CW_USEDEFAULT, //X座標
CW_USEDEFAULT, //Y座標
CW_USEDEFAULT, //幅
CW_USEDEFAULT, //高さ
NULL, //親ウィンドウのハンドル、親を作るときはNULL
NULL, //メニューハンドル、クラスメニューを使うときはNULL
hInst, //インスタンスハンドル
NULL);
if (!hWnd)
return FALSE;
ShowWindow(hWnd, nCmdShow);
UpdateWindow(hWnd);
return TRUE;
}
CreateWindow関数の第1引数はInitApp関数の所で使ったクラス名と同じにしください。
//ウィンドウプロシージャ
LRESULT CALLBACK WndProc(HWND hWnd, UINT msg, WPARAM wp, LPARAM lp)
{
int id;
INITCOMMONCONTROLSEX ic;
LPCREATESTRUCT pcs;
static MYDATA md;
HDC hdc;
PAINTSTRUCT ps;
static TCHAR str[256];
switch (msg) {
case WM_MYMSG:
wsprintf(str, "「%s」が選択されています。", (LPTSTR)lp);
break;
case WM_CREATE:
ic.dwSize = sizeof(INITCOMMONCONTROLSEX);
ic.dwICC = ICC_USEREX_CLASSES;
InitCommonControlsEx(&ic);
pcs = (LPCREATESTRUCT)lp;
md.hInst = pcs->hInstance;
md.hWnd = hWnd;
break;
case WM_PAINT:
hdc = BeginPaint(hWnd, &ps);
TextOut(hdc, 10, 10, str, strlen(str));
EndPaint(hWnd, &ps);
break;
case WM_COMMAND:
switch (LOWORD(wp)) {
case IDM_END:
SendMessage(hWnd, WM_CLOSE, 0, 0);
break;
case IDM_DLG:
DialogBoxParam(md.hInst, "MYDLG", hWnd, (DLGPROC)MyDlgProc, (LPARAM)&md);
break;
}
break;
case WM_CLOSE:
id = MessageBox(hWnd,
"終了してもよいですか",
"終了確認",
MB_YESNO | MB_ICONQUESTION);
if (id == IDYES) {
DestroyWindow(hWnd);
}
break;
case WM_DESTROY:
PostQuitMessage(0);
break;
default:
return (DefWindowProc(hWnd, msg, wp, lp));
}
return 0;
}
自作メッセージ(WM_MYMSG)が来たら、strにlParamの内容をコピーしています。 この自作メッセージはダイアログボックスのプロシージャで拡張コンボボックスの CBN_SELCHANGE通知メッセージを捕まえた時に発信されます(後述)。
WM_CREATEメッセージが来たら、コモンコントロールを初期化します。
次にCREATESTRUCT構造体から現在のインスタンスハンドルを取得します。 この方法はすでに解説してあるので、 忘れた人は第19章を参照して下さい。
次に自作データ型MYDATAの変数mdに親ウィンドウのハンドルと、インスタンス ハンドルをセットします。この構造体のアドレスをあとでダイアログボックスに 渡します。(こんなことをしなくてもダイアログボックスのプロシージャで 取得できるのですが、今回はわざわざめんどうな方法を使っています。)
WM_PAINTメッセージが来たらクライアント領域にstrの内容を描画します。
メニューからIDM_DLG(「ダイアログを出す」)が選択されたら DialogBoxParam関数でダイアログボックスを出します。
int DialogBoxParam(
HINSTANCE hInstance, // アプリケーションのインスタンス
LPCTSTR lpTemplateName, // ダイアログボックステンプレートの名前
HWND hWndParent, // 親ウィンドウ
DLGPROC lpDialogFunc, // ダイアログボックスのプロシージャのアドレス
LPARAM dwInitParam // ダイアログボックスに渡すデータ
);
DialogBoxマクロとほとんど同じですが、引数がひとつ多いです。
dwInitParamはlpDialogFuncに送られてきたWM_INITDIALOGメッセージ のlParamとなります。
// ダイアログボックスのプロシージャ
LRESULT CALLBACK MyDlgProc(HWND hDlg, UINT msg, WPARAM wp, LPARAM lp)
{
static int nSelected;
static HWND hCombo, hParent;
static HIMAGELIST hImg;
HINSTANCE hInst;
static MYDATA *pmd;
COMBOBOXEXITEM ci;
TCHAR szText[256];
switch (msg) {
case WM_INITDIALOG:
pmd = (MYDATA *)lp;
hInst = pmd->hInst;
hParent = pmd->hWnd;
hCombo = GetDlgItem(hDlg, IDC_COMBOBOXEX1);
hImg = ImageList_LoadBitmap(hInst, "MYBMP", 16, 15, RGB(255, 0, 0));
SendMessage(hCombo, CBEM_SETIMAGELIST, 0, (LPARAM)hImg);
SetMyComboEx(hCombo);
SendMessage(hCombo, CB_SETCURSEL, (WPARAM)nSelected, 0);
return TRUE;
case WM_COMMAND:
switch (LOWORD(wp)) {
case IDOK:
case IDCANCEL:
nSelected = SendMessage(hCombo, CB_GETCURSEL, 0, 0);
ImageList_Destroy(hImg);
EndDialog(hDlg, IDOK);
return TRUE;
case IDC_COMBOBOXEX1:
switch (HIWORD(wp)) {
case CBN_SELCHANGE:
ci.mask = CBEIF_TEXT;
ci.pszText = szText;
ci.cchTextMax = sizeof(szText);
ci.iItem = -1;
SendMessage(hCombo, CBEM_GETITEM, 0, (LPARAM)&ci);
SendMessage(hParent, WM_MYMSG, 0, (LPARAM)szText);
InvalidateRect(hParent, NULL, TRUE);
return TRUE;
}
return FALSE;
}
return FALSE;
}
return FALSE;
}
WM_INITDIALOGが来たら、lParamよりインスタンスハンドルと 親ウィンドウのハンドルを取得します。このデータは DialogBoxParam関数から送られてきたものです。
次にダイアログボックス上の拡張コンボボックスの ハンドルを取得します。
ImageList_LoadBitmapマクロの最後の引数はRGB(255, 0, 0)にして下さい。 今回は赤色でマスクしているのでこうなります。
次にCB_SETCURSELメッセージで拡張コンボボックスのエディット部分に 選択されているアイテムを表示されます。まだ、何も選択されていない 場合はnSelectedが0になっているので、最初のアイテムが表示されます。
「閉じる」(IDOK)ボタンか、「バッテン印」(IDCANCEL)が押された時は CB_GETCURSELメッセージを使って選択されているアイテムのインデックスを 取得してnSelectedにコピーしておきます。
コンボボックスのCBN_SELCHANGE通知メッセージは選択が変化した時 WM_COMMANDの形でやってきます。 (すでに第183章で使用しています。) WM_COMMANDのwParamのLOWORDはコントロールのID を示しています。また、wParamのHIWORDは通知メッセージです。
さて、CBN_SELCHANGE通知メッセージを捕まえたら、コンボボックスの アイテムを取得する必要があります。これは、COMBOBOXEXITEM構造体に 必要なメンバをセットしてCBEM_GETITEMメッセージを送ることことに より実現します。この場合必要なのはテキストのみなので COMBOBOXEXITEM構造体のmaskメンバはCBEIF_TEXTとなります。 取得したテキストを格納するバッファのアドレスをpszTextメンバに セットします。また、この場合cchTextMaxメンバを設定しなくては いけません。(拡張コンボボックスにアイテムを挿入する時は不要。) エディットコントロールのアイテムを取得する時はiItemメンバを-1に します。
また、この時親ウィンドウに自作メッセージ(WM_MYMSG)を送ります。 lParamに取得した文字列のアドレスを指定します。これで、親ウィンドウは 新しい文字列を知ることができます。さらに、InvalidateRect関数で 親ウィンドウにWM_PAINTメッセージを送ってクライアント領域を 再描画させます。
void SetMyComboEx(HWND hCombo)
{
COMBOBOXEXITEM ci;
memset(&ci, 0, sizeof(COMBOBOXEXITEM));
ci.mask = CBEIF_TEXT | CBEIF_IMAGE | CBEIF_SELECTEDIMAGE | CBEIF_INDENT;
ci.pszText = "丸";
ci.iItem = 0;
ci.iIndent = 0;
ci.iImage = 0;
ci.iSelectedImage = 0;
SendMessage(hCombo, CBEM_INSERTITEM, 0, (LPARAM)&ci);
ci.pszText = "三角";
ci.iItem = 1;
ci.iIndent = 1;
ci.iImage = 1;
ci.iSelectedImage = 1;
SendMessage(hCombo, CBEM_INSERTITEM, 0, (LPARAM)&ci);
ci.pszText = "バツ";
ci.iItem = 2;
ci.iIndent = 2;
ci.iImage = 2;
ci.iSelectedImage = 2;
SendMessage(hCombo, CBEM_INSERTITEM, 0, (LPARAM)&ci);
ci.pszText = "丸2";
ci.iItem = 3;
ci.iIndent = 0;
ci.iImage = 0;
ci.iSelectedImage = 0;
SendMessage(hCombo, CBEM_INSERTITEM, 0, (LPARAM)&ci);
ci.pszText = "三角2";
ci.iItem = 4;
ci.iIndent = 1;
ci.iImage = 1;
ci.iSelectedImage = 1;
SendMessage(hCombo, CBEM_INSERTITEM, 0, (LPARAM)&ci);
ci.pszText = "バツ2";
ci.iItem = 5;
ci.iIndent = 2;
ci.iImage = 2;
ci.iSelectedImage = 2;
SendMessage(hCombo, CBEM_INSERTITEM, 0, (LPARAM)&ci);
return;
}
拡張コンボボックスにアイテムを挿入する関数です。 特に説明は不要ですね。
今回はちょっとめんどうなことをやりました。 ウィンドウ間のデータやメッセージのやり取りを工夫するのは SDKプログラミングの最も面白いところです。
[SDK第2部 Index] [総合Index] [Previous Chapter] [Next Chapter]
Update 17/Apr/1999 By Y.Kumei
当ホーム・ページの一部または全部を無断で複写、複製、 転載あるいはコンピュータ等のファイルに保存することを禁じます。
没有评论:
发表评论