VC++の使い方

VC++の使い方 > リファレンス > GUIリファレンス > ツリービュー
このページの内容
ツリービューとは
スタイル
構造体
使い方 - 操作編
使い方 - イベント編
ありがたいスポンサー様
All About ソフトウエアエンジニア
ネットで8%割引!自動車保険はアメリカンホーム・ダイレクト
人生の「チャンス」と「ピンチ」にモビット!
保険料が一生上がらない、保険料最大50%割引の一生涯の医療保険!

ツリービューとは

ツリービューは、階層構造を表現するために用いられます。一番身近な例としては、インターネットエクスプローラの お気に入り でしょう。

最近では、設定ダイアログにもツリービューが使われるようになってきました。次の例は、の設定ダイアログです。左側にツリービューを配し、どのアイテムを選択するかによって、右側のダイアログが書き換わるようになっています。MS も昔はタブ方式が多かったのですが、VisualStudio.NET の設定ダイアログは、この形式をとっています。設定内容を階層的に捕らえることができるので、非常によくできたダイアログだと思います。

スタイル

TVS_CHECKBOXES
Version 4.70。チェックボックス付き。
TVS_DISABLEDRAGDROP ドラッグ&ドロップできなくする。TVN_BEGINDRAG が通知されないだけで、このフラグがたっていなくても、デフォルトではドラッグできない。
TVS_EDITLABELS
ラベルを編集できる。
TVS_FULLROWSELECT
Version 4.71。1行まるまる選択できる。TVS_HASLINES とは併用できない。
TVS_HASBUTTONS
+ - のボタンを表示する。
TVS_HASLINES
アイテムをつなぐラインを表示する。
TVS_LINESATROOT
ルート要素にラインを表示する。
TVS_SHOWSELALWAYS 選択要素を常に表示する。通常はつけた方が無難。
TVS_SINGLEEXPAND アイテムをクリックすればツリーが開く。
TVS_TRACKSELECT
アイテムの上にカーソルを持っていくと、色が変わる。

構造体

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, *LPTVITEM;

mask には、この構造体で有効な要素を指定します。

TVIF_CHILDREN cChildren が有効
TVIF_HANDLE hItem が有効
TVIF_IMAGE iImage が有効
TVIF_PARAM lParam が有効
TVIF_SELECTEDIMAGE iSelectedImage が有効
TVIF_STATE state が有効
TVIF_TEXT pszText が有効

状態

TVIS_BOLD 太字
TVIS_CUT カットされた状態
TVIS_DROPHILITED ドロップのためハイライトされている
TVIS_EXPANDED 開いた状態
TVIS_EXPANDEDONCE 一度開いた状態
TVIS_EXPANDPARTIAL 一部分開いた状態
TVIS_SELECTED 選択されている
TVIS_OVERLAYMASK
TVIS_STATEIMAGEMASK
TVIS_USERMASK

使い方 - 操作編

基本的に、SendMessage ですべての操作を行えるのですが、ソースが読みにくくなるのでマクロが定義されています。ここでは、そのマクロを使った方法を紹介します。SendMessage を使ってやりたい人は、MSDN のマクロのヘルプを見て、対応するメッセージを送ってください。

ツリービューにはハンドルと言う概念があります。ハンドルは、ツリービューの特定のアイテムを表すための指標として導入されています。なぜハンドルが導入されているかですが、リストボックスなどだと 何番目の要素であるかを示せばアイテムを特定できますが、ツリービューではそうも行かないからです。

注意点をあげておきます

アイテムを追加する

TVINSERTSTRUCT tvis;
tvis.hParent	= TVI_ROOT;
tvis.hInsertAfter	= TVI_SORT; // TVI_FIRST, TVI_LAST
tvis.item.mask	= TVIF_TEXT;
tvis.item.pszText	= "label here";
HTREEITEM hitem = TreeView_InsertItem(hwndTree, &tvis);
tvis.hParent
親アイテムのアイテムハンドル。ルートに追加する場合は TVI_ROOT
hInsertAfter
どの兄弟アイテムの次に追加するかをハンドルで指定します。ソートするなら TVI_SORT, 先頭なら TVI_FIRST, 末尾なら TVI_LAST。

item に関しては、TVITEM の解説を参照。

アイテムを削除する

BOOL b = TreeView_DeleteItem(hwndTree, hitem);

hitem に削除したいアイテムのハンドルを入れます。子供がいる場合は、子供もさくっと消えます。TVI_ROOT を指定すると、全部消えちゃいます。

アイテムを全て削除する

TreeView_DeleteAllItems(hwndTree);

アイテムを選択する

折りたたまれている場合は、開いてでも選択しようとする。

TreeView_Select(hwndTree, hItem, TVGN_CARET);

アイテムが表示されるようにする

ウインドウに表示されていないアイテムを表示する。折りたたまれている場合は、開いてでも表示しようとする。

TreeView_EnsureVisible(hwndTree, hItem);

選択中のアイテムのハンドルを取得する

HTREEITEM hItem = TreeView_GetSelection(hwndTree);

いろいろなアイテムのハンドルを取得する

// ルートアイテムのハンドルを取得する
HTREEITEM hroot = TreeView_GetRoot(hwndTree);

// hitem の子供のうち、先頭のハンドルを取得
HTREEITEM hchild = TreeView_GetChild(hwndTree, hitem);

// hitem の次の兄弟アイテムのハンドルを取得する
HTREEITEM hslib = TreeView_GetNextSibling(hwndTree, hitem);

// hitem の親を取得する
HTREEITEM hparent = TreeView_GetParent(hwndTree, hitem);

アイテムのハンドルから詳細情報を取得する

hitem をアイテムのハンドルとすると、

TVITEM ti;
ti.mask = TVIF_PARAM;
ti.hItem = hitem;
if(TreeView_GetItem(hwndTree, &item))
{
	// ti.lParam が取得できている
}

でよいでしょう。取得したいアイテムの情報を、ti.mask に指定します。

ちょっとややこしいのが、ラベル文字列を取得する場合です。あらかじめバッファを用意しておかないといけません。

TVITEM ti;
char pszBuf[256];
ti.mask = TVIF_TEXT ;
ti.hItem = hitem;
ti.pszText = pszBuf;
ti.cchTextMax = 256;

文字列の長さをあらかじめ知ることが出来ないので、完全に取得するためには次のようにしなければなりません。長ったらしいけど、要はバッファのサイズを少しずつ大きくしていってます。

DWORD dwSize = 64;
TVITEM ti;
char* pszBuf = NULL;
while(1)
{
	pszBuf = new char[dwSize];
	ti.mask = TVIF_TEXT ;
	ti.hItem = hitem;
	ti.pszText = pszBuf;
	ti.cchTextMax = dwSize;
	if(TreeView_GetItem(hwndTree, &ti))
	{
		ti.pszText[dwSize - 1] = '\0';
		if((UINT)lstrlen(ti.pszText) == dwSize -1)
		{
			dwSize *= 2;
			delete[] pszBuf;
			pszBuf = NULL;
		}
		else
		{
			// 取得できた時の処理
			break;
		}
	}
	else
	{
		// データ取得失敗。ハンドルがおかしい?
		break;
	}
}
if(pszBuf)
{
	delete[] pszBuf;
}

ツリーの開き具合を調整

TreeView_Expand(hwndTree, hitem, flag);
TVE_COLLAPSE 閉じるとき
TVE_EXPAND 開くとき
TVE_TOGGLE 閉じている場合は開く、開いている場合は閉じる

使い方 - イベント編

コモンコントロールでは、通知が WM_NOTIFY を介して行われます。WM_NOTIFY は lParam に NMHDR へのポインタが格納されています。メッセージによっては、NMHDR を内包するような構造体が渡されますが、NMHDR としてキャストしても問題ないようにできています。

typedef struct tagNMHDR{ 
    HWND hwndFrom; 
    UINT idFrom; 
    UINT code; 
} NMHDR; 

マウスのイベントを受け取りたい

NM_CLICK, NM_DBLCLK, NM_RCLICK, NM_RDBCLK を受け取ります。

case WM_NOTIFY:
{
	NMHDR* pnmhdr = (LPNMHDR)lParam;
	if(pnmhdr->idFrom == IDC_TREE)
	{
		switch(pnmhdr->code)
		{
			case NM_CLICK:
				// クリック時の処理
				break;
			case NM_RCLICK:
				break;
		}
	}
}
なお、選択中のアイテムを取得するには、選択中のアイテムのハンドルを取得するを参照してくだいさい。