EternalWindows
リストビュー / 一覧表示

リストビューとは、特定の表示形式に基づいてアイテムを表示するコントールのことです。

上図は、一覧表示と呼ばれる最もシンプルなリストビューです。 一覧表示ではアイテムを下へ順に表示していき、ウインドウの下側まで到達した場合はアイテムを右側へと表示する特徴があります。

リストビューの操作は、メッセージとメッセージをラッピングしたマクロの両方で行うことができますが、 本章ではマクロを使用することにします。 リストビューにアイテムを追加するには、ListView_InsertItemを呼び出します。

int ListView_InsertItem(
  HWND hwnd,
  const LPLVITEM pitem
);

hwndは、リストビューのハンドルを指定します。 pitemは、LVITEM構造体のアドレスを指定します。

LVITEM構造体は、次のように定義されています。

typedef struct _LVITEM { 
    UINT mask; 
    int iItem; 
    int iSubItem; 
    UINT state; 
    UINT stateMask; 
    LPTSTR pszText; 
    int cchTextMax; 
    int iImage; 
    LPARAM lParam;
#if (_WIN32_IE >= 0x0300)
    int iIndent;
#endif
#if (_WIN32_WINNT >= 0x501)
    int iGroupId;
    UINT cColumns; // tile view columns
    PUINT puColumns;
#endif
#if (_WIN32_WINNT >= 0x0600)
    int* piColFmt;
    int iGroup;
#endif
} LVITEM, *LPLVITEM; 

maskは、使用するメンバを表す定数を指定します。 iItemは、アイテムのインデックスを指定します。 iSubItemは、サブアイテムのインデックスを指定します。 stateは、アイテムの状態を表す定数を指定します。 stateMaskは、アイテムの状態を表す定数を指定します。 pszTextは、アイテムのテキストを指定します。 cchTextMaxは、pszTextのサイズを指定します。 iImageは、イメージリストのインデックスを指定します。 lParamは、アプリケーション定義値を指定します。 iIndentは、アプリケーション定義値を指定します。 iGroupIdは、アイテムが属しているグループのIDを指定します。 cColumnsは、puColumnsの要素数を指定します。 puColumnsは、タイル表示におけるサブアイテムのインデックスを格納した配列を指定します。

リストビューに追加されたアイテムに関する情報を取得したい場合は、ListView_GetItemを呼び出します。

BOOL ListView_GetItem(
  HWND hwnd,
  LPLVITEM pitem
);

hwndは、リストビューのハンドルを指定します。 pitemは、LVITEM構造体のアドレスを指定します。

今回のプログラムは、リストビューを一覧表示で作成します。 アイテムが選択された場合は、そのアイテムの名前をウインドウタイトルに表示します。

#include <windows.h>
#include <commctrl.h>

#pragma comment (lib, "comctl32.lib")

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 hwndListView = NULL;
	
	switch (uMsg) {
		
	case WM_CREATE: {
		LVITEM               item;
		INITCOMMONCONTROLSEX ic;
		
		ic.dwSize = sizeof(INITCOMMONCONTROLSEX);
		ic.dwICC  = ICC_LISTVIEW_CLASSES;
		InitCommonControlsEx(&ic);
		
		hwndListView = CreateWindowEx(0, WC_LISTVIEW, TEXT(""), WS_CHILD | WS_VISIBLE | LVS_LIST, 0, 0, 0, 0, hwnd, (HMENU)1, ((LPCREATESTRUCT)lParam)->hInstance, NULL);
		
		item.mask     = LVIF_TEXT;
		item.iItem    = 0;
		item.iSubItem = 0;
		item.pszText  = TEXT("item1");
		ListView_InsertItem(hwndListView, &item);
		
		item.mask     = LVIF_TEXT;
		item.iItem    = 1;
		item.iSubItem = 0;
		item.pszText  = TEXT("item2");
		ListView_InsertItem(hwndListView, &item);

		return 0;
	}

	case WM_NOTIFY:
		if (((LPNMHDR)lParam)->code == LVN_ITEMCHANGED) {
			TCHAR  szBuf[256];
			LVITEM item;
			ULONG  uSelectState = LVIS_SELECTED | LVIS_FOCUSED;

			if (((LPNMLISTVIEW)lParam)->iItem == -1 || ((LPNMLISTVIEW)lParam)->uNewState != uSelectState)
				return 0;

			item.mask       = LVIF_TEXT;
			item.iItem      = ((LPNMLISTVIEW)lParam)->iItem;
			item.iSubItem   = 0;
			item.pszText    = szBuf;
			item.cchTextMax = sizeof(szBuf) / sizeof(TCHAR);
			ListView_GetItem(hwndListView, &item);
			
			SetWindowText(hwnd, szBuf);
		}

		return 0;

	case WM_SIZE:
		MoveWindow(hwndListView, 0, 0, LOWORD(lParam), HIWORD(lParam), TRUE);
		return 0;

	case WM_DESTROY:
		PostQuitMessage(0);
		return 0;

	default:
		break;

	}

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

リストビューを作成する場合は、事前にInitCommonControlsExを呼び出しておく必要があります。 このとき、dwICCメンバにはICC_LISTVIEW_CLASSESを指定します。 CreateWindowExでリストビューを作成するには、 第2引数にリストビューのウインドウクラスを示すWC_LISTVIEWを指定し、 第3引数にリストビュー専用のウインドウスタイルを指定します。 LVS_LISTは、リストビューをリスト表示することを意味します。 アイテムを追加するコードは、次のようになっています。

item.mask     = LVIF_TEXT;
item.iItem    = 0;
item.iSubItem = 0;
item.pszText  = TEXT("item1");
ListView_InsertItem(hwndListView, &item);

item.mask     = LVIF_TEXT;
item.iItem    = 1;
item.iSubItem = 0;
item.pszText  = TEXT("item2");
ListView_InsertItem(hwndListView, &item);

アイテムを追加する場合は、pszTextにアイテムのテキストを指定します。 また、pszTextメンバを使用する場合は、maskにLVIF_TEXTを指定します。 iItemはアイテムのインデックスであり、これは0から始まります。 よって、2回目のListView_InsertItemでは、iItemを1つカウントしています。 maskの値に関係なく、iItemとiSubItemは必ず初期化するべきといえます。

リストビューのアイテムの状態が変更された場合は、通知コードがLVN_ITEMCHANGEDであるWM_NOTIFYが送られます。

case WM_NOTIFY:
	if (((LPNMHDR)lParam)->code == LVN_ITEMCHANGED) {
		TCHAR  szBuf[256];
		LVITEM item;
		ULONG  uSelectState = LVIS_SELECTED | LVIS_FOCUSED;

		if (((LPNMLISTVIEW)lParam)->iItem == -1 || ((LPNMLISTVIEW)lParam)->uNewState != uSelectState)
			return 0;

		item.mask       = LVIF_TEXT;
		item.iItem      = ((LPNMLISTVIEW)lParam)->iItem;
		item.iSubItem   = 0;
		item.pszText    = szBuf;
		item.cchTextMax = sizeof(szBuf) / sizeof(TCHAR);
		ListView_GetItem(hwndListView, &item);
		
		SetWindowText(hwnd, szBuf);
	}

	return 0;

LVN_ITEMCHANGEDでは、lParamにNMLISTVIEW構造体のアドレスが格納されています。 iItemが-1である場合は有効なアイテムを指していないことを意味するため、処理を続行しないようにします。 また、今回はアイテムが選択状態になったときだけを特定したいため、 アイテムの状態(uNewState)が選択状態になってない場合も処理を続行しません。 アイテムの名前を取得するには、maskにLVIF_TEXTを指定してListView_GetItemを呼び出します。 LVIF_TEXTを指定すれば、pszTextに文字列を受け取るバッファを指定することができます。 iItemは対象となるアイテムのインデックスを指定しますが、これはNMLISTVIEW構造体から参照できます。 なお、アイテムの選択はNM_CLICKという通知コードでも検出できますが、 これはクリックによるアイテムの選択を検出するだけです。 つまり、キーによるアイテムの選択を検出しないことに注意してください。

アイテムの状態

リストビューのアイテムが選択された場合は、アイテムが選択状態となりハイライト表示されるようになります。 このような現在選択されているアイテムを特定したい場合は、次のコードを実行します。

int   i = -1;
TCHAR szBuf[256];

for (;;) {
	i = ListView_GetNextItem(hwndListView, i, LVNI_ALL | LVNI_SELECTED);
	if (i == -1)
		break;
	
	ListView_GetItemText(hwndListView, i, 0, szBuf, sizeof(szBuf) / sizeof(TCHAR));
	MessageBox(NULL, szBuf, TEXT("OK"), MB_OK);
}

ListView_GetNextItemの第2引数には検索を開始するアイテムのインデックスであり、 -1を指定した場合は最初のアイテムから検索されます。 第3引数のLVNI_ALLは第2引数のアイテムから検索することを意味し、 LVNI_SELECTEDは選択状態のアイテムを検索することを意味します。 戻り値は、指定した状態に一致したアイテムのインデックスですが、 -1の場合はアイテムが見つからなかったことを意味します。 上記では見つかったアイテムのテキストを取得するということで、 ListView_GetItemText(ListView_GetItemでも可)を呼び出しています。

先の処理ではListView_GetNextItemを使用していましたが、 ListView_GetItemStateでアイテムの状態を確認する方法もあります。

int   i;
int   nCount = ListView_GetItemCount(hwndListView);
TCHAR szBuf[256];

for (i = 0; i < nCount; i++) {
	if (ListView_GetItemState(hwndListView, i, LVIS_SELECTED)) {
		ListView_GetItemText(hwndListView, i, 0, szBuf, sizeof(szBuf) / sizeof(TCHAR));
		MessageBox(NULL, szBuf, TEXT("OK"), MB_OK);
	}
}

ListView_GetItemCountを呼び出せば、リストビューに追加されているアイテムの数を取得できます。 後は各アイテムに対してListView_GetItemStateを呼び出せば、 アイテムが第3引数の状態であるかを確認することができます。



戻る