2010年5月17日星期一

第195章 拡張コンボボックスと通知メッセージ



今回は拡張コンボボックスと通知メッセージについて解説します。 と、言っても通知メッセージについては従来のコンボボックスの 通知メッセージと共通のものだけです。今回はダイアログボックス上の 拡張コンボボックスでアイテムが選択されたら、リアルタイムで 親ウィンドウのクライアント領域に「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)が来たら、strlParamの内容をコピーしています。 この自作メッセージはダイアログボックスのプロシージャで拡張コンボボックスの 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マクロとほとんど同じですが、引数がひとつ多いです。

dwInitParamlpDialogFuncに送られてきた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_COMMANDwParamLOWORDはコントロールのID を示しています。また、wParamHIWORDは通知メッセージです。

さて、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
当ホーム・ページの一部または全部を無断で複写、複製、 転載あるいはコンピュータ等のファイルに保存することを禁じます。

没有评论: