このページの内容 |
---|
ツリービューとは |
スタイル |
構造体 |
使い方 - 操作編 |
使い方 - イベント編 |
ありがたいスポンサー様 |
---|
ツリービューは、階層構造を表現するために用いられます。一番身近な例としては、インターネットエクスプローラの お気に入り でしょう。
最近では、設定ダイアログにもツリービューが使われるようになってきました。次の例は、の設定ダイアログです。左側にツリービューを配し、どのアイテムを選択するかによって、右側のダイアログが書き換わるようになっています。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 |
![]() アイテムの上にカーソルを持っていくと、色が変わる。 |
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);
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; } } }なお、選択中のアイテムを取得するには、選択中のアイテムのハンドルを取得するを参照してくだいさい。