EternalWindows
リストビュー / 詳細表示

今回は、リストビューを詳細表示します。 次に、詳細表示の例を示します。

詳細表示であるリストビューには列という概念があります。 これにより、一行の複数の列にアイテムを追加することができるため、 表を作成したい場合などに利用できます。

詳細表示における1つの列はカラムとして識別されるため、ListView_InsertColumnでカラムを追加することになります。

int ListView_InsertColumn(
  HWND hwnd,
  int iCol,
  const LPLVCOLUMN pcol
);

hwndは、リストビューのハンドルを指定します。 iColは、カラムのインデックスを指定します。 pcolは、LVCOLUMN構造体のアドレスを指定します。

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

typedef struct _LVCOLUMN { 
    UINT mask; 
    int fmt; 
    int cx; 
    LPTSTR pszText; 
    int cchTextMax; 
    int iSubItem; 
#if (_WIN32_IE >= 0x0300)
    int iImage;
    int iOrder;
#endif
#if (_WIN32_WINNT >= 0x0600)
    int cxMin;
    int cxDefault;
    int cxIdeal;
#endif
} LVCOLUMN, *LPLVCOLUMN;

maskは、使用するメンバを表す定数を指定します。 fmtは、カラムのフォーマットを定義する定数を指定します。 cxは、カラムの幅を指定します。 pszTextは、カラムのテキストを指定します。 cchTextMaxは、pszTextのサイズを指定します。 iSubItemは、カラムに関連付けられたインデックスを指定します。 iImageは、イメージリストのインデックスを指定します。 iOrderは、カラムのオフセットを指定します。 cxMinは、カラムの最小の幅を指定します。 cxDefaultは、カラムのデフォルト幅を指定します。 cxIdealは、カラムの理想的な幅を指定します。

カラムに関する情報を取得するには、ListView_GetColumnを呼び出します。

BOOL ListView_GetColumn(
  HWND hwnd,
  int iCol,
  LPLVCOLUMN pcol
);

hwndは、リストビューのハンドルを指定します。 iColは、カラムのインデックスを指定します。 pcolは、LVCOLUMN構造体のアドレスを指定します。

詳細表示のリストビューでは列が複数存在しますから、 2列目や3列目にアイテムを設定することができます。 こうしたアイテムはサブアイテムと呼ばれ、ListView_SetItemで設定します。

BOOL ListView_SetItem(
  HWND hwnd,
  const 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;
		LVCOLUMN             column;
		INITCOMMONCONTROLSEX ic;
		
		ic.dwSize = sizeof(INITCOMMONCONTROLSEX);
		ic.dwICC  = ICC_LISTVIEW_CLASSES;
		InitCommonControlsEx(&ic);
		
		hwndListView = CreateWindowEx(0, WC_LISTVIEW, TEXT(""), WS_CHILD | WS_VISIBLE | LVS_REPORT, 0, 0, 0, 0, hwnd, (HMENU)1, ((LPCREATESTRUCT)lParam)->hInstance, NULL);
		
		column.mask    = LVCF_WIDTH | LVCF_TEXT;
		column.cx      = 100;
		column.pszText = TEXT("col1");
		ListView_InsertColumn(hwndListView, 0, &column);
		
		column.mask    = LVCF_WIDTH | LVCF_TEXT;
		column.cx      = 100;
		column.pszText = TEXT("col2");
		ListView_InsertColumn(hwndListView, 1, &column);

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

		return 0;
	}

	case WM_NOTIFY:
		if (((LPNMHDR)lParam)->code == LVN_COLUMNCLICK) {
			TCHAR    szBuf[256];
			LVCOLUMN column;

			column.mask       = LVCF_TEXT;
			column.pszText    = szBuf;
			column.cchTextMax = sizeof(szBuf) / sizeof(TCHAR);
			ListView_GetColumn(hwndListView, ((LPNMLISTVIEW)lParam)->iSubItem, &column);

			MessageBox(NULL, szBuf, TEXT("OK"), MB_OK);
			
			return 0;
		}
		break;
	
	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);
}

詳細表示のリストビューを作成するには、ウインドウスタイルにLVS_REPORTを指定します。 LVCOLUMN構造体のmaskにはLVCF_WIDTHとLVCF_TEXTを指定し、 cxとpszTextが考慮されるようにしておきます。 ListView_InsertColumnの第2引数はカラムのインデックスであるため、 2回目の呼び出しではインデックスを1つカウントすることになります。 アイテムの追加に関しては、1列目のアイテムはListView_InsertItemで追加しますが、 2列目のサブアイテムについてはListView_SetItemで追加します。 このとき、サブアイテムのインデックスであるiSubItemを1つカウントしておきます。

カラムがクリックされた場合は、通知コードがLVN_COLUMNCLICKであるWM_NOTIFYが送られます。 lParamにはNMLISTVIEW構造体のアドレスが格納されており、 iSubItemからクリックされたカラムのインデックスを特定することができます。 今回はカラムのテキストを取得するため、LVCOLUMNのmaskにLVCF_TEXTを指定してListView_GetColumnを呼び出します。

ラベルの編集

ListView_InsertItemで追加されたアイテムは、動的にラベル(テキスト)を変更することができるようになっています。 ラベルの編集が有効になっているアイテム上でクリックを2回行うと(ダブルクリックではない)、 エディットコントロールが表示されるため、そこに変更したいテキストを入力することになります。 次に、ラベルの編集を有効にする例を示します。

#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;
	static HWND hwndEdit = NULL;
	
	switch (uMsg) {
		
	case WM_CREATE: {
		LVITEM               item;
		LVCOLUMN             column;
		INITCOMMONCONTROLSEX ic;
		
		ic.dwSize = sizeof(INITCOMMONCONTROLSEX);
		ic.dwICC  = ICC_LISTVIEW_CLASSES;
		InitCommonControlsEx(&ic);
		
		hwndListView = CreateWindowEx(0, WC_LISTVIEW, TEXT(""), WS_CHILD | WS_VISIBLE | LVS_REPORT | LVS_EDITLABELS, 0, 0, 0, 0, hwnd, (HMENU)1, ((LPCREATESTRUCT)lParam)->hInstance, NULL);
		
		column.mask    = LVCF_WIDTH | LVCF_TEXT;
		column.cx      = 100;
		column.pszText = TEXT("col1");
		ListView_InsertColumn(hwndListView, 0, &column);
		
		column.mask    = LVCF_WIDTH | LVCF_TEXT;
		column.cx      = 100;
		column.pszText = TEXT("col2");
		ListView_InsertColumn(hwndListView, 1, &column);

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

		return 0;
	}

	case WM_NOTIFY: {
		NMLVDISPINFO *lpDispInfo = (NMLVDISPINFO *)lParam;

		if (lpDispInfo->hdr.code == LVN_BEGINLABELEDIT)
			hwndEdit = ListView_GetEditControl(hwndListView);
		else if (lpDispInfo->hdr.code == LVN_ENDLABELEDIT) {
			TCHAR szBuf[256];
			GetWindowText(hwndEdit, szBuf, sizeof(szBuf) / sizeof(TCHAR));
			ListView_SetItemText(hwndListView, lpDispInfo->item.iItem, 0, szBuf);
		}
		else
			;

		break;
	}
	
	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);
}

ウインドウスタイルにLVS_EDITLABELSを指定するとラベルの編集が有効になります。 この状態でアイテムを2回クリックすると通知コードがLVN_BEGINLABELEDITであるWM_NOTIFYが送られ、 ListView_GetEditControlを呼び出すことでエディットコントロールのハンドルを取得することができます。 ユーザーがエディットコントロール上で文字列を確定した場合はLVN_ENDLABELEDITが送られ、 先に取得したエディットコントロールに対してGetWindowTextを呼び出せば、 エディットコントロールに入力された文字列を取得することができます。 後はこれをListView_SetItemTextに指定すれば、アイテムのテキストは変更されることになります。



戻る