第106章 リストビューの基礎


さて、今回はリストビューについて解説します。 これは、Windows95から新しく加わったコモンコントロールの一つです。



左の図のような感じになります。ウィンドウが小さいときはスクロールバーが 自動的に付けられます。



では、作り始める前にコモンコントロールの復習をしてみます。

1.commctrl.hをインクルードします。 2.comctl32.libをプロジェクトに参加させます。 3.使う前にInitCommonControls関数で初期化します。

これは、第57章を参照してください。

リストビューも例によってCreateWindowEx関数で作ります。
CreateWindowEx(0, WC_LISTVIEW, "", WS_CHILD | WS_VISIBLE | LVS_REPORT,...)
という感じになります。クラス名をWC_LISTVIEWにします。ウィンドウスタイルは WS_CHILDとWS_VISIBLEは必須ですね。そのほかにリストビュー特有のスタイルとして 「LVS_何とか」というのが付加されます。サンプルの図のようなスタイルは LVS_REPORTです。(HMENU)のところは子ウィンドウ識別子で適当な値にしておきます。 では、早速サンプルを見てみましょう。

// listvw01.cpp #define STRICT #define ID_LISTVIEW 100 #include <windows.h> #include <commctrl.h> LRESULT CALLBACK WndProc(HWND, UINT, WPARAM, LPARAM); BOOL InitApp(HINSTANCE); BOOL InitInstance(HINSTANCE, int); char szClassName[] = "listvw01"; //ウィンドウクラス HINSTANCE hInst; 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; }

いつもと同じですが、commctrl.hがインクルードされていることに注意してください。 また、インスタンスハンドルを保存するグローバル変数も用意しておきました。

//ウィンドウ・クラスの登録 BOOL 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 = NULL; //メニュー名 wc.lpszClassName = (LPCSTR)szClassName; wc.hIconSm = LoadIcon(NULL, IDI_APPLICATION); return (RegisterClassEx(&wc)); }

これは、いつもと同じです。

//ウィンドウの生成 BOOL InitInstance(HINSTANCE hInstance, int nCmdShow) { HWND hWnd; hInst = hInstance; hWnd = CreateWindow(szClassName, "猫でもわかるリストビュー",//タイトルバーにこの名前が表示されます 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; }

いつもとほぼ同じですが、インスタンスハンドルを保存していることに注意してください。

//ウィンドウプロシージャ LRESULT CALLBACK WndProc(HWND hWnd, UINT msg, WPARAM wp, LPARAM lp) { int id; static HWND hList; LV_COLUMN lvcol; LV_ITEM item; switch (msg) { case WM_CREATE: InitCommonControls(); hList = CreateWindowEx(0, WC_LISTVIEW, "", WS_CHILD | WS_VISIBLE | LVS_REPORT, 0, 0, 0, 0, hWnd, (HMENU)ID_LISTVIEW, hInst, NULL); lvcol.mask = LVCF_FMT | LVCF_WIDTH | LVCF_TEXT | LVCF_SUBITEM; lvcol.fmt = LVCFMT_LEFT; lvcol.cx = 100; lvcol.pszText = "名前"; lvcol.iSubItem = 0; ListView_InsertColumn(hList, 0, &lvcol); lvcol.cx = 200; lvcol.pszText = "住所"; lvcol.iSubItem = 1; ListView_InsertColumn(hList, 1, &lvcol); item.mask = LVIF_TEXT; item.pszText = "粂井康孝"; item.iItem = 0; item.iSubItem = 0; ListView_InsertItem(hList, &item); item.pszText = "北海道旭川市"; item.iItem = 0; item.iSubItem = 1; ListView_SetItem(hList, &item); item.pszText = "粂井ひとみ"; item.iItem = 1; item.iSubItem = 0; ListView_InsertItem(hList, &item); item.pszText = "東京都千代田区"; item.iItem = 1; item.iSubItem = 1; ListView_SetItem(hList, &item); item.pszText = "粂井志麻"; item.iItem = 2; item.iSubItem = 0; ListView_InsertItem(hList, &item); item.pszText = "北海道中富良野町"; item.iItem = 2; item.iSubItem = 1; ListView_SetItem(hList, &item); break; case WM_SIZE: MoveWindow(hList, 0, 0, LOWORD(lp), HIWORD(lp), TRUE); 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 0L; }

殆どすべてがWM_CREATEのところでまかなっています。 InitCommonControls関数でコモンコントロールを初期化して、 CreateWindowEx関数でリストビューをつくります。 親ウィンドウのクライアント領域いっぱいに貼り付けるのですが この段階では大きさは幅、高さとも0にしてあります。 (どうせWM_SIZEメッセージが来るので、その時大きさを調節するから) また、後の章で解説しますがリストビューには表示モードというのが あってLVS_ICON, LVS_LIST, LVS_REPORT, LVS_SMALLICONで表します。 さて、リストビューのウィンドウを作ったらこれに、カラム(列)を 挿入します。

int ListView_InsertColumn( HWND hwnd, int iCol, const LV_COLUMN FAR *pcol );

これはマクロです。
hwndはリストビューコントロールのハンドルです。
iColは新しいカラムのインデックスを指定します。
pcolは新しいカラムについて設定してあるLV_COLUMN構造体へのポインタです。

typedef struct _LV_COLUMN { UINT mask; int fmt; int cx; LPTSTR pszText; int cchTextMax; int iSubItem; } LV_COLUMN;

maskは有効な情報のメンバを指定します。たとえば
LVCF_FMTならはfmtメンバが有効という意味です。 他にLVCF_SUBITEM, LVCF_TEXT, LVCF_WIDTHがあります。

fmtは列の配置を指定します。しかし、最初の列は常に左寄りです。 LVCFMT_LEFT, LVCFMT_RIGHT, LVCFMT_CENTERのいずれかを指定します。

cxは列の幅を指定します。

pszTextには列見出しを指定します。

cchTextMaxにはpszTextのバッファサイズです。(ListView_GetColumnマクロ などを使って列の情報を取得するとき使用)

iSubItemは列と関連づけられたサブ項目のインデックス。

int ListView_InsertItem( HWND hwnd, const LV_ITEM FAR *pitem );

新しい項目を追加するマクロです。 サブ項目を追加するのには 使えません。また後述のLV_ITEM構造体のiSubItemメンバは0で なくてはいけません。

hwndはリストビューコントロールのハンドルです。

pitemはLV_ITEM構造体へのポインタです。

typedef struct _LV_ITEM { UINT mask; int iItem; int iSubItem; UINT state; UINT stateMask; LPTSTR pszText; int cchTextMax; int iImage; LPARAM lParam; } LV_ITEM;

maskはLVIF_IMAGEとあればiImageメンバが有効またはデータを設定する 必要があることを示します。LVIF_PARAM, LVIF_STATE, LVIF_TEXTも同じです。 LVIF_DI_SETITEMはLVN_GETDISPINFOメッセージでのみ設定できます。

iItemは参照する項目です。

iSubItemはサブ項目です。

stateは項目の現在の状態です。

stateMaskは項目の有効な状態です。

他のメンバについては必要が生じたとき説明します。

さて項目とサブ項目についてちょっとわかりにくいと思います。 ずーと上の方のサンプルの画像を見てください。 「粂井康孝」「粂井ひとみ」などの氏名は項目に相当します。 また、その氏名にたいして住所がサブ項目になるわけです。 サブ項目はListView_InsertItemマクロは使えないので ListView_SetItemマクロを使います。

BOOL ListView_SetItem( HWND hwnd, const LV_ITEM FAR *pitem );

さて、今回出てきたマクロはcommctrl.hで定義されています。 興味のある人はのぞいてみてください。 たとえば

#define ListView_InsertItem(hwnd, pitem) \ (int)SNDMSG((hwnd), LVM_INSERTITEM, 0, (LPARAM)(const LV_ITEM FAR*)(pitem))

という具合です。SNDMSGは予想がつくと思いますが

#define SNDMSG SendMessage

というように定義されています。

さて、WM_SIZEメッセージのところではMoveWindow関数を使って リストビューの大きさを親のクライアント領域の大きさと同じにしています。


[SDK第2部 Index] [総合Index] [Previous Chapter] [Next Chapter]

Update Feb/07/1998 By Y.Kumei
当ホーム・ページの一部または全部を無断で複写、複製、 転載あるいはコンピュータ等のファイルに保存することを禁じます。