ツリービュー


木構造の表示

コンピュータ科学の世界では、もはや常識の木構造
ファイルシステムはもちろん、XML やプログラミング言語も木構造で分析できます

木構造を GUI でグラフィカルに、構造的に表示する時は
ツリービュー コントロールが用意されているので、これを用います
エクスプローラ 等、様々なソフトウェアで使われている優秀なコントロールです

ツリービューを作成するには CreateWindowEx() 関数で
第2引数に WC_TREEVIEW ウィンドウクラスを指定します
ウィンドウスタイルは、必ず WS_CHILT を含むものとし
従来のスタイルに加え、以下の定数を組み合わせて指定することができます

定数解説
TVS_DISABLEDRAGDROP ドラッグアンドドロップ操作を無効にする
TVS_EDITLABELS 項目ラベルを編集可能にする
TVS_HASBUTTONS 親項目に + または - ボタンを表示する
ボタンを押すことで、子要素が展開されたり隠れたりする
TVS_HASLINES 項目の階層を示す線を表示する
TVS_LINESATROOT ルート項目間を結ぶ線を表示する
TVS_HASBUTTONS と一緒に用いなければならない
TVS_SHOWSELALWAYS 選択された項目の選択状態を
ツリービューコントロールがフォーカスを失っても維持する

リストビュー同様に、CreateWindowEx() が返したツリービューのハンドルを得たら
後は、メッセージを送信してくれるマクロを使って項目を追加します
ツリービューの項目には、すべて親子関係があることに注目する必要があります

項目を追加するには TreeView_InsertItem() マクロを使います

HTREEITEM TreeView_InsertItem(HWND hwndTV , LPTVINSERTSTRUCT lpis);

hwndTV には、ツリービューコントロールのハンドルを
lpis には、項目の情報を格納した TVINSERTSTRUCT 構造体のポインタを指定します
成功すれば、ツリービュー項目のハンドルが、失敗すれば NULL が返ります
ツリービューの項目は HTREEITEM という型です

TVINSERTSTRUCT は、次のように定義されています
typedef struct tagTVINSERTSTRUCT {
    HTREEITEM hParent;
    HTREEITEM hInsertAfter;
    TVITEM item;
} TVINSERTSTRUCT, FAR *LPTVINSERTSTRUCT;
hParent には、親となる項目のハンドルを指定します
ルート項目であれば NULL またはTVI_ROOT を指定してください
ツリービューには必ず1つのルート項目があり、ルートは常に1つでなければいけません

hInsertAfter 追加する項目の前の項目となるハンドルを指定します
このメンバには、以下の定数を指定することもできます

定数解説
TVI_FIRST リストの先頭に追加する
TVI_LAST リストの最後に項目を追加する
TVI_SORT アルファベット順に項目を追加する

item には、追加する項目の情報を格納した TVITEM 構造体です
この構造体は、次のように定義されています
typedef struct tagTVITEM{
    UINT      mask;
    HTREEITEM hItem;
    UINT      state;
    UINT      stateMask;
    LPTSTR    pszText;
    int       cchTextMax;
    int       iImage;
    int       iSelectedImage;
    int       cChildren;
    LPARAM    lParam;
} TVITEM, FAR *LPTVITEM;
mask には、構造体のどのメンバを使用するかを示すフラグを指定します
このメンバには、以下の定数を組み合わせて指定しなければいけません

定数解説
TVIF_CHILDREN nChildren メンバが有効
TVIF_HANDLE hItem メンバが有効
TVIF_IMAGE iImage メンバが有効
TVIF_PARAM lParam メンバが有効
TVIF_SELECTEDIMAGE iSelectedImage メンバが有効
TVIF_STATE state メンバと stateMask メンバが有効
TVIF_TEXT pszText メンバと cchTextMax メンバが有効

hItem は、この構造体が参照するツリービューの項目です
このメンバには、情報を取得する時に、対象となる項目のハンドルなどを指定します

state は、項目の現在の状態を表す値であり
stateMask は、項目の有効な状態を示す値を指定します
これらメンバには、以下の定数の組み合わせを指定することができます

定数解説
TVIS_BOLD 項目がボールドである
TVIS_CUT 項目が、切り取り/貼り付け操作の一環として選択されている
TVIS_DROPPHILITED 項目が、ドラッグアンドドロップの対象として選択されている
TVIS_EXPANDED 項目の子項目リストが展開されている
TVIS_EXPANDEDONCE 項目の子項目リストが、少なくとも1度は展開されている
TVIS_FOCUSED 項目にフォーカスがある
TVIS_OVERLAYMASK 項目のオーバーレイイメージが描画時に含められる
TVIS_SELECTED 項目が選択されている
TVIS_STATEIMAGEMASK 項目の状態イメージが描画時に含められる
TVIS_USERMASK TVISSTATEIMAGEMAS と同じ

pszText には、項目のラベルを表す文字列へのポインタを指定します
情報を取得する場合には、文字列を格納するバッファへのポインタとなります
cchTextMax は、バッファのサイズをバイト単位で指定します
情報を設定する場合、このメンバは無視されます

iImage は、関連付けられたイメージリストのインデックスを指定します
これは、項目にアイコンを用いる時に、アイコンのインデックスを指定するメンバです
iSelectedImage には、項目が選択されている時に用いるアイコンとなる
イメージリストのインデックスを指定します

cChildren は、この項目に子項目があるかどうかを示すフラグを指定します
1つ以上の子項目がある場合は 1 を、そうでなければ 0 を指定します
親ウィンドウの責任で子項目を描画する場合、I_CHILDRENCALLBACK をここに指定します
そうすると、項目の操作に対して親ウィンドウの通知メッセージが送られるようになります
lParam には、項目に関連付けられた 32 ビット値を指定します

TVINSERTSTRUCT 構造体の hParent メンバは
ルート項目以外、必ず有効な親要素となる項目のハンドルを指定します
木構造で、全てのノードは親を持っているため、これは当然のことです
#include <windows.h>
#include <commctrl.h>
#define TITLE TEXT("Kitty on your lap")

LPTSTR strItem[] = {TEXT("RENA") , TEXT("YUKI") , TEXT("MIMI")};

LRESULT CALLBACK WndProc(HWND hWnd , UINT msg , WPARAM wp , LPARAM lp) {
	TVINSERTSTRUCT tvi;
	HTREEITEM hTreeItem;
	int iCount = 0;
	static HWND hTree;

	switch (msg) {
	case WM_DESTROY:
		PostQuitMessage(0);
		return 0;
	case WM_CREATE:
		InitCommonControls();

		hTree = CreateWindowEx(WS_EX_CLIENTEDGE , WC_TREEVIEW , 0 ,
			WS_CHILD | WS_VISIBLE | WS_BORDER | WS_VSCROLL |
			TVS_HASBUTTONS | TVS_HASLINES | TVS_LINESATROOT ,
			0 , 0 , 10 , 10 , hWnd , (HMENU)1 ,
			((LPCREATESTRUCT)lp)->hInstance , NULL
		);

		tvi.hParent = TVI_ROOT;
		tvi.hInsertAfter = TVI_SORT;
		tvi.item.mask = TVIF_TEXT;
		tvi.item.pszText = TITLE;
		hTreeItem = TreeView_InsertItem(hTree , &tvi);

		tvi.hParent = hTreeItem;
		for ( ; iCount < 3 ; iCount++) {
			tvi.item.pszText = strItem[iCount];
			TreeView_InsertItem(hTree , &tvi);
		}

		return 0;
	case WM_SIZE:
		MoveWindow(hTree , 0 , 0 , LOWORD(lp) , HIWORD(lp) , FALSE);
		return 0;
	}
	return DefWindowProc(hWnd , msg , wp , lp);
}

int WINAPI WinMain(HINSTANCE hInstance , HINSTANCE hPrevInstance ,
			PSTR lpCmdLine , int nCmdShow ) {
	HWND hWnd;
	MSG msg;
	WNDCLASS winc;

	winc.style		= CS_HREDRAW | CS_VREDRAW;
	winc.lpfnWndProc	= WndProc;
	winc.cbClsExtra	= winc.cbWndExtra	= 0;
	winc.hInstance		= hInstance;
	winc.hIcon		= LoadIcon(NULL , IDI_APPLICATION);
	winc.hCursor		= LoadCursor(NULL , IDC_ARROW);
	winc.hbrBackground	= (HBRUSH)GetStockObject(WHITE_BRUSH);
	winc.lpszMenuName	= NULL;
	winc.lpszClassName	= TEXT("KITTY");

	if (!RegisterClass(&winc)) return -1;

	hWnd = CreateWindow(
			TEXT("KITTY") , TITLE ,
			WS_OVERLAPPEDWINDOW | WS_VISIBLE ,
			CW_USEDEFAULT , CW_USEDEFAULT ,
			CW_USEDEFAULT , CW_USEDEFAULT ,
			NULL , NULL , hInstance , NULL
	);
	if (hWnd == NULL) return -1;

	while(GetMessage(&msg , NULL , 0 , 0)) {
		TranslateMessage(&msg);
		DispatchMessage(&msg);
	}
	return msg.wParam;
}


このプログラムは、上の図のようなツリービューを表示します
ツリーは "Kitty on your lap" というルート項目があり、3つの子を持っています
もちろん、子項目も、さらに別の子を含むことができます

イメージリストをツリービューに設定することで、アイコンを表示することができます
イメージリストを関連付けるには、TreeView_SetImageList() を用います
HIMAGELIST TreeView_SetImageList(
	HWND hwndTV , HIMAGELIST himl ,
	INT iImage
);
hwndTV には、ツリービューのハンドルを
himl には、イメージリストのハンドルを指定します

iImage は、設定するイメージリストの種類を指定します
この引数には、次の定数のいずれかを指定できます

定数解説
TVSIL_NORMAL 標準イメージリスト
項目の選択状態と非選択状態のイメージを持つ
TVSIL_STATE 状態イメージリスト
ユーザー定義の特殊な状態のイメージを持つ

成功すれば、以前のイメージリストのハンドルが
失敗、またはイメージリストがない状態だった場合は NULL が返ります

イメージリストを設定すれば、あとは TVITEM 構造体の iImage メンバを設定し
項目をツリーに追加するだけです
そうすれば、項目に指定したアイコンが表示されることでしょう
#include <windows.h>
#include <commctrl.h>
#define TITLE TEXT("Kitty on your lap")

LPTSTR strItem[] = {TEXT("RENA") , TEXT("YUKI") , TEXT("MIMI")};

LRESULT CALLBACK WndProc(HWND hWnd , UINT msg , WPARAM wp , LPARAM lp) {
	TVINSERTSTRUCT tvi;
	HTREEITEM hTreeItem;
	int iCount = 0;
	static HIMAGELIST hImg;
	static HWND hTree;

	switch (msg) {
	case WM_DESTROY:
		ImageList_Destroy(hImg);
		PostQuitMessage(0);
		return 0;
	case WM_CREATE:
		InitCommonControls();

		hImg = ImageList_Create(16 , 16 , ILC_COLOR4 |ILC_MASK , 2 , 1);
		ImageList_AddIcon(hImg , LoadIcon(NULL , IDI_QUESTION));
		ImageList_AddIcon(hImg , LoadIcon(NULL , IDI_ASTERISK));

		hTree = CreateWindowEx(WS_EX_CLIENTEDGE , WC_TREEVIEW , 0 ,
			WS_CHILD | WS_VISIBLE | WS_BORDER | WS_VSCROLL |
			TVS_HASBUTTONS | TVS_HASLINES | TVS_LINESATROOT ,
			0 , 0 , 10 , 10 , hWnd , (HMENU)1 ,
			((LPCREATESTRUCT)lp)->hInstance , NULL
		);

		TreeView_SetImageList(hTree , hImg , TVSIL_NORMAL);

		tvi.hParent = TVI_ROOT;
		tvi.hInsertAfter = TVI_SORT;
		tvi.item.mask = TVIF_TEXT | TVIF_IMAGE | TVIF_SELECTEDIMAGE;
		tvi.item.pszText = TITLE;
		tvi.item.iImage = 0;
		tvi.item.iSelectedImage = 1;
		hTreeItem = TreeView_InsertItem(hTree , &tvi);

		tvi.hParent = hTreeItem;
		for ( ; iCount < 3 ; iCount++) {
			tvi.item.pszText = strItem[iCount];
			TreeView_InsertItem(hTree , &tvi);
		}

		return 0;
	case WM_SIZE:
		MoveWindow(hTree , 0 , 0 , LOWORD(lp) , HIWORD(lp) , FALSE);
		return 0;
	}
	return DefWindowProc(hWnd , msg , wp , lp);
}

int WINAPI WinMain(HINSTANCE hInstance , HINSTANCE hPrevInstance ,
			PSTR lpCmdLine , int nCmdShow ) {
	HWND hWnd;
	MSG msg;
	WNDCLASS winc;

	winc.style		= CS_HREDRAW | CS_VREDRAW;
	winc.lpfnWndProc	= WndProc;
	winc.cbClsExtra	= winc.cbWndExtra	= 0;
	winc.hInstance		= hInstance;
	winc.hIcon		= LoadIcon(NULL , IDI_APPLICATION);
	winc.hCursor		= LoadCursor(NULL , IDC_ARROW);
	winc.hbrBackground	= (HBRUSH)GetStockObject(WHITE_BRUSH);
	winc.lpszMenuName	= NULL;
	winc.lpszClassName	= TEXT("KITTY");

	if (!RegisterClass(&winc)) return -1;

	hWnd = CreateWindow(
			TEXT("KITTY") , TITLE ,
			WS_OVERLAPPEDWINDOW | WS_VISIBLE ,
			CW_USEDEFAULT , CW_USEDEFAULT ,
			CW_USEDEFAULT , CW_USEDEFAULT ,
			NULL , NULL , hInstance , NULL
	);
	if (hWnd == NULL) return -1;

	while(GetMessage(&msg , NULL , 0 , 0)) {
		TranslateMessage(&msg);
		DispatchMessage(&msg);
	}
	return msg.wParam;
}


このツリービューの項目は、アイコンを含んでいます
選択されていない項目は疑問符のアイコンを、選択された項目は情報アイコンを表示します


TreeView_InsertItem()

HTREEITEM TreeView_InsertItem(HWND hwndTV , LPTVINSERTSTRUCT lpis);

ツリービュに新しい項目を追加します

hwndTV - ツリービューコントロールのハンドルを指定します
lpis - 項目の情報を格納した TVINSERTSTRUCT 構造体のポインタを指定します

戻り値 - ツリービュー項目のハンドル、失敗すれば NULL

TreeView_SetImageList()

HIMAGELIST TreeView_SetImageList(
	HWND hwndTV , HIMAGELIST himl ,
	INT iImage
);
ツリービューにイメージリストを関連付けます

hwndTV - ツリービューのハンドルを指定します
himl - イメージリストのハンドルを指定します
iImage - 設定するイメージリストの種類を指定します

戻り値 - 以前のイメージリストのハンドル、失敗すれば NULL

iImage には以下の定数のいずれかを指定することができます

定数解説
TVSIL_NORMAL 標準イメージリスト
項目の選択状態と非選択状態のイメージを持つ
TVSIL_STATE 状態イメージリスト
ユーザー定義の特殊な状態のイメージを持つ



前のページへ戻る次のページへ