こんにちは。
「+/-」ボタンの変更は出来ないようですので、カスタムドローを使って描くしか道は無いのかもしれません。
http://lhsp.s206.xrea.com/misc/customdraw.html
http://www.kumei.ne.jp/c_lang/sdk3/sdk_262.htm
ダイアログの戻り値はTRUE/FALSEなので、カスタムドローメッセージの戻り値の為に、
SetWindowLong(hDlg, DWL_MSGRESULT, lResult)
を必ず呼び出します。
取り合えず以下は「+/-」ボタンに見立てて「!アイコン」と「?アイコン」をイメージリストにしてから、子持ちのツリーアイテムの横に描かせています。
阿弥陀籤の様な線は自前で描こうとすると地獄そのものなので、割愛しています。参考程度に。
#include<windows.h>
#include<commctrl.h>
#include<tchar.h>
#include"resource.h"
#pragma comment(lib, "comctl32.lib")
#define ArrayCount(a) (sizeof(a)/sizeof(a[0]))
const LPCTSTR TSTR_IMGLIST = TEXT("image list");
typedef struct tagMessage
{
union
{
HWND hWnd;
HWND hDlg;
HWND hCtl;
};
UINT uMsg;
WPARAM wParam;
LPARAM lParam;
} Message;
static void TreeView_SetLParam(HWND hTV, HTREEITEM hTreeItem)
{
TVITEM item = {TVIF_PARAM};
item.lParam = (LPARAM)hTreeItem;
item.hItem = hTreeItem;
TreeView_SetItem(hTV, &item);
}
static HTREEITEM TreeView_Add(HWND hTV, HTREEITEM hTreeParent, LPCTSTR pszItem)
{
TVINSERTSTRUCT tvi = {0};
tvi.hParent = hTreeParent;
tvi.item.mask = TVIF_TEXT;
tvi.item.pszText = (LPTSTR)pszItem;
HTREEITEM hTreeItem = TreeView_InsertItem(hTV , &tvi);
::TreeView_SetLParam(hTV, hTreeItem);
return hTreeItem;
}
static void OnTVCustomDraw(Message& m, const LPNMTVCUSTOMDRAW p)
{
TCHAR sItem[256] = {0};
HIMAGELIST hImgList = (HIMAGELIST)::GetProp(m.hDlg, TSTR_IMGLIST);
HTREEITEM hTreeItem = (HTREEITEM)p->nmcd.lItemlParam;
//ツリーアイテムからアイテム情報を取り出す
TVITEM item = {TVIF_TEXT | TVIF_CHILDREN | TVIF_STATE};
item.hItem = hTreeItem;
item.pszText = sItem;
item.cchTextMax = ArrayCount(sItem);
TreeView_GetItem(p->nmcd.hdr.hwndFrom, &item);
const DWORD dwDTStyle = DT_LEFT | DT_VCENTER | DT_SINGLELINE;
//ツリーアイテム欄の高さ
const LONG lHeight = p->nmcd.rc.bottom - p->nmcd.rc.top;
RECT rcText = {(p->iLevel + 1) * lHeight, p->nmcd.rc.top, p->nmcd.rc.right, p->nmcd.rc.bottom};
//正確なテキスト領域を取る
::DrawText(p->nmcd.hdc, sItem, ::_tcslen(sItem), &rcText, dwDTStyle | DT_CALCRECT);
//テキスト領域を塗り潰す(クリックする度にフォーカスの残骸が残る為)
::FillRect(p->nmcd.hdc, &rcText, (HBRUSH)::GetStockObject(WHITE_BRUSH));
//テキストを書き込む
::DrawText(p->nmcd.hdc, sItem, ::_tcslen(sItem), &rcText, dwDTStyle);
//ツリーアイテムが子持ち
if(item.cChildren == 1)
{
//イメージリストの使用位置を決める
const INT nIndex = (item.state & TVIS_EXPANDED) ? 0 : 1;
::ImageList_Draw(hImgList, nIndex, p->nmcd.hdc, p->iLevel * lHeight, p->nmcd.rc.top, ILD_NORMAL);
}
//フォーカスを描く
if(p->nmcd.uItemState == (CDIS_FOCUS | CDIS_SELECTED))
::DrawFocusRect(p->nmcd.hdc, &rcText);
}
static LRESULT OnInitDialog(Message& m)
{
//イメージリストの準備
HIMAGELIST hImgList = ::ImageList_Create(14, 14, ILC_COLOR32 | ILC_MASK, 2, 1);
::ImageList_AddIcon(hImgList, ::LoadIcon(NULL, IDI_QUESTION));
::ImageList_AddIcon(hImgList, ::LoadIcon(NULL, IDI_ASTERISK));
::SetProp(m.hDlg, TSTR_IMGLIST, hImgList);
//適当にツリーを作る
HWND hTV = ::GetDlgItem(m.hDlg, IDC_TREE1);
HTREEITEM hTreeNest = NULL;
HTREEITEM hTreeItem = ::TreeView_Add(hTV, TVI_ROOT, TEXT("parent1"));
::TreeView_Add(hTV, hTreeItem, TEXT("item1"));
hTreeNest = ::TreeView_Add(hTV, hTreeItem, TEXT("item1"));
::TreeView_Add(hTV, hTreeNest, TEXT("item2"));
::TreeView_Add(hTV, hTreeItem, TEXT("item3"));
::TreeView_Add(hTV, TVI_ROOT, TEXT("parent2"));
hTreeNest = ::TreeView_Add(hTV, TVI_ROOT, TEXT("parent3"));
::TreeView_Add(hTV, hTreeNest, TEXT("item1"));
return 0;
}
static LRESULT OnNotify(Message& m)
{
LPNMTVCUSTOMDRAW p = (LPNMTVCUSTOMDRAW)m.lParam;
if(p->nmcd.hdr.idFrom != IDC_TREE1)
return FALSE;
if(p->nmcd.hdr.code != NM_CUSTOMDRAW)
return FALSE;
switch (p->nmcd.dwDrawStage)
{
case CDDS_PREPAINT:
::SetWindowLong(m.hDlg, DWL_MSGRESULT, CDRF_NOTIFYITEMDRAW);
return TRUE;
case CDDS_ITEMPREPAINT:
::OnTVCustomDraw(m, p);
::SetWindowLong(m.hDlg, DWL_MSGRESULT, CDRF_SKIPDEFAULT);
return TRUE;
}
return 0;
}
static LRESULT OnClose(Message& m)
{
HIMAGELIST hImgList = (HIMAGELIST)::GetProp(m.hDlg, TSTR_IMGLIST);
::ImageList_Destroy(hImgList);
::RemoveProp(m.hDlg, TSTR_IMGLIST);
::DestroyWindow(m.hDlg);
return 0;
}
int CALLBACK DlgProc(HWND hDlg, UINT uMsg, WPARAM wParam, LPARAM lParam)
{
Message m = {hDlg, uMsg, wParam, lParam};
switch(uMsg)
{
case WM_INITDIALOG:
::OnInitDialog(m);
break;
case WM_NOTIFY:
::OnNotify(m);
break;
case WM_CLOSE:
::OnClose(m);
break;
case WM_DESTROY:
PostQuitMessage(0);
break;
default:
return FALSE;
}
return TRUE;
}
int APIENTRY WinMain(HINSTANCE hInst, HINSTANCE hInstPrev, LPSTR lpCmdLine, int nCmdShow)
{
MSG msg;
::InitCommonControls();
HWND hWnd = ::CreateDialogParam(hInst, MAKEINTRESOURCE(IDD_DIALOG1), NULL, &::DlgProc, 0);
::ShowWindow(hWnd, nCmdShow);
while(::GetMessage(&msg, NULL, 0, 0) == TRUE)
{
::TranslateMessage(&msg);
::DispatchMessage(&msg);
}
return msg.wParam;
}
お礼
こんにちは。 カスタムドローについては自分も同じ結論に至りましたが、ではどうしたものかと悩んでいました。 非常に詳しいサンプルをありがとうございます。 未熟なためちょっとまだうまく動いてはくれませんが、いろいろ試したいと思います。 ありがとうこざいました。