メニューの「編集」「親項目の追加」で親項目を追加します。「編集」「項目の追加」で
項目を追加します。項目のレベルによってフォント、文字色、背景色が異なっている点に
注意してください。
新しく登場する構造体はNMTVCUSTOMDRAWです。
nmcdは、NMCUSTOMDRAW構造体です。typedef struct tagNMTVCUSTOMDRAW { NMCUSTOMDRAW nmcd; COLORREF clrText; COLORREF clrTextBk; #if (_WIN32_IE >= 0x0400) int iLevel; #endif } NMTVCUSTOMDRAW, *LPNMTVCUSTOMDRAW;
clrTextはテキスト色、clrTextBkは背景色です。
iLevel(comctl32.dllのバージョンが4.71以降)は、書こうとしているアイテムのレベルです。ルートアイテムが0となります。 その子供は1となります。
では、早速プログラムを見てみましょう。
普通のメニューとダイアログボックスのリソース・スクリプトです。// custom02.rcの一部 ///////////////////////////////////////////////////////////////////////////// // // Menu // MYMENU MENU DISCARDABLE BEGIN POPUP "ファイル(&F)" BEGIN MENUITEM "終了(&X)", IDM_END END POPUP "編集(&E)" BEGIN MENUITEM "親項目の追加(&P)", IDM_ADDPARENT MENUITEM "項目追加(&A)", IDM_ADDCHILD MENUITEM SEPARATOR MENUITEM "項目削除(&D)", IDM_DEL END END ///////////////////////////////////////////////////////////////////////////// // // Dialog // ADDITEM DIALOG DISCARDABLE 0, 0, 118, 53 STYLE DS_MODALFRAME | WS_POPUP | WS_CAPTION | WS_SYSMENU CAPTION "項目の追加" FONT 9, "MS Pゴシック" BEGIN EDITTEXT IDC_EDIT1,44,7,67,13,ES_AUTOHSCROLL DEFPUSHBUTTON "追 加",IDOK,7,32,50,14 PUSHBUTTON "キャンセル",IDCANCEL,61,32,50,14 LTEXT "項目名",IDC_STATIC,7,13,22,8 END
どうということもありませんが、コモンコントロールの準備を忘れないでください。// custom02.cpp #ifndef STRICT #define STRICT #endif #include <windows.h> #include <windowsx.h> #include <commctrl.h> #include "resource.h" #define ID_TREEVIEW 100 #define ITEMNAME_SIZE 64 LRESULT CALLBACK WndProc(HWND, UINT, WPARAM, LPARAM); LRESULT CALLBACK AddItemProc(HWND, UINT, WPARAM, LPARAM); ATOM InitApp(HINSTANCE); BOOL InitInstance(HINSTANCE, int); HWND MakeMyTree(HWND); char szClassName[] = "custom02"; //ウィンドウクラス
いつもと同じです。int WINAPI WinMain(HINSTANCE hCurInst, HINSTANCE hPrevInst, LPSTR lpsCmdLine, int nCmdShow) { MSG msg; if (!InitApp(hCurInst)) return FALSE; if (!InitInstance(hCurInst, nCmdShow)) return FALSE; while (GetMessage(&msg, NULL, 0, 0)) { TranslateMessage(&msg); DispatchMessage(&msg); } return msg.wParam; } //ウィンドウ・クラスの登録 ATOM InitApp(HINSTANCE hInst) { WNDCLASSEX wc; wc.cbSize = sizeof(WNDCLASSEX); wc.style = CS_HREDRAW | CS_VREDRAW; wc.lpfnWndProc = WndProc; //プロシージャ名 wc.cbClsExtra = 0; wc.cbWndExtra = 0; wc.hInstance = hInst;//インスタンス wc.hIcon = LoadIcon(NULL, IDI_APPLICATION); wc.hCursor = LoadCursor(NULL, IDC_ARROW); wc.hbrBackground = (HBRUSH)GetStockObject(WHITE_BRUSH); wc.lpszMenuName = "MYMENU"; //メニュー名 wc.lpszClassName = (LPCSTR)szClassName; wc.hIconSm = LoadIcon(NULL, IDI_APPLICATION); return (RegisterClassEx(&wc)); } //ウィンドウの生成 BOOL InitInstance(HINSTANCE hInst, int nCmdShow) { HWND hWnd; hWnd = CreateWindow(szClassName, "猫でもわかるカスタムドロー", //タイトルバーにこの名前が表示されます WS_OVERLAPPEDWINDOW, //ウィンドウの種類 CW_USEDEFAULT, //X座標 CW_USEDEFAULT, //Y座標 CW_USEDEFAULT, //幅 CW_USEDEFAULT, //高さ NULL, //親ウィンドウのハンドル、親を作るときはNULL NULL, //メニューハンドル、クラスメニューを使うときはNULL hInst, //インスタンスハンドル NULL); if (!hWnd) return FALSE; ShowWindow(hWnd, nCmdShow); UpdateWindow(hWnd); return TRUE; }
親ウィンドウのプロシージャです。//ウィンドウプロシージャ LRESULT CALLBACK WndProc(HWND hWnd, UINT msg, WPARAM wp, LPARAM lp) { int id; static HWND hTree; INITCOMMONCONTROLSEX ic; static HINSTANCE hInst; HTREEITEM hItem, hNewItem; TVINSERTSTRUCT ti; char szName[ITEMNAME_SIZE]; LPNMHDR lpnmhdr; LPNMTVCUSTOMDRAW lpnmcd; static HFONT hFont1, hFont2, hFont3, hFont4, hOrgFont; switch (msg) { case WM_CREATE: hInst = ((LPCREATESTRUCT)lp)->hInstance; ic.dwSize = sizeof(INITCOMMONCONTROLSEX); ic.dwICC = ICC_TREEVIEW_CLASSES; InitCommonControlsEx(&ic); hTree = MakeMyTree(hWnd); hOrgFont = CreateFont(16, 8, 0, 0, FW_NORMAL, FALSE, FALSE, 0, SHIFTJIS_CHARSET, OUT_DEFAULT_PRECIS, CLIP_DEFAULT_PRECIS, DEFAULT_QUALITY, DEFAULT_PITCH, "system"); hFont1 = CreateFont(16, 8, 0, 0, FW_NORMAL, FALSE, FALSE, 0, SHIFTJIS_CHARSET, OUT_DEFAULT_PRECIS, CLIP_DEFAULT_PRECIS, DEFAULT_QUALITY, DEFAULT_PITCH, "MS ゴシック"); hFont2 = CreateFont(16, 8, 0, 0, FW_NORMAL, FALSE, FALSE, 0, SHIFTJIS_CHARSET, OUT_DEFAULT_PRECIS, CLIP_DEFAULT_PRECIS, DEFAULT_QUALITY, DEFAULT_PITCH, "MS 明朝"); hFont3 = CreateFont(16, 8, 0, 0, FW_NORMAL, FALSE, FALSE, 0, SHIFTJIS_CHARSET, OUT_DEFAULT_PRECIS, CLIP_DEFAULT_PRECIS, DEFAULT_QUALITY, DEFAULT_PITCH, "DF行書体"); hFont4 = CreateFont(16, 8, 0, 0, FW_NORMAL, FALSE, FALSE, 0, SHIFTJIS_CHARSET, OUT_DEFAULT_PRECIS, CLIP_DEFAULT_PRECIS, DEFAULT_QUALITY, DEFAULT_PITCH, "ダサ字TT"); SendMessage(hTree, WM_SETFONT, (WPARAM)hOrgFont, MAKELPARAM(TRUE, 0)); break; case WM_SIZE: MoveWindow(hTree, 0, 0, LOWORD(lp), HIWORD(lp), TRUE); break; case WM_NOTIFY: lpnmhdr = (LPNMHDR)lp; if (lpnmhdr->hwndFrom == hTree && lpnmhdr->code == NM_CUSTOMDRAW) { lpnmcd = (LPNMTVCUSTOMDRAW)lp; if (lpnmcd->nmcd.dwDrawStage == CDDS_PREPAINT) { return CDRF_NOTIFYITEMDRAW; } if (lpnmcd->nmcd.dwDrawStage == CDDS_ITEMPREPAINT) { if (lpnmcd->iLevel == 0) { SelectObject(lpnmcd->nmcd.hdc, hFont1); lpnmcd->clrText = RGB(0, 0, 0); lpnmcd->clrTextBk = RGB(0, 255, 255); } if (lpnmcd->iLevel == 1) { SelectObject(lpnmcd->nmcd.hdc, hFont2); lpnmcd->clrText = RGB(255, 255, 255); lpnmcd->clrTextBk = RGB(0, 0, 0); } if (lpnmcd->iLevel == 2) { SelectObject(lpnmcd->nmcd.hdc, hFont3); lpnmcd->clrText = RGB(255, 0, 255); lpnmcd->clrTextBk = RGB(0, 255, 0); } if (lpnmcd->iLevel >= 3) { SelectObject(lpnmcd->nmcd.hdc, hFont4); lpnmcd->clrText = RGB(0, 0, 255); lpnmcd->clrTextBk = RGB(255, 0, 255); } return CDRF_NEWFONT; } } else return DefWindowProc(hWnd, msg, wp, lp); break; case WM_COMMAND: switch (LOWORD(wp)) { case IDM_END: SendMessage(hWnd, WM_CLOSE, 0, 0); break; case IDM_ADDPARENT: id = DialogBoxParam(hInst, "ADDITEM", hWnd, (DLGPROC)AddItemProc, (LPARAM)szName); if (id == IDOK) { memset(&ti, 0, sizeof(TVINSERTSTRUCT)); ti.hInsertAfter = TVI_LAST; ti.item.mask = TVIF_TEXT; ti.hParent = TVI_ROOT; ti.item.pszText = szName; TreeView_InsertItem(hTree, &ti); } break; case IDM_DEL: hItem = TreeView_GetSelection(hTree); if (hItem) TreeView_DeleteItem(hTree, hItem); break; case IDM_ADDCHILD: hItem = TreeView_GetSelection(hTree); if (hItem) { id = DialogBoxParam(hInst, "ADDITEM", hWnd, (DLGPROC)AddItemProc, (LPARAM)szName); if (id == IDOK) { memset(&ti, 0, sizeof(TVINSERTSTRUCT)); ti.hInsertAfter = TVI_LAST; ti.item.mask = TVIF_TEXT; ti.hParent = hItem; ti.item.pszText = szName;//szAddItem; hNewItem = TreeView_InsertItem(hTree, &ti); TreeView_EnsureVisible(hTree, hNewItem); } } break; } break; case WM_CLOSE: id = MessageBox(hWnd, "終了してもよいですか", "終了確認", MB_YESNO | MB_ICONQUESTION); if (id == IDYES) { DeleteObject(hOrgFont); DeleteObject(hFont1); DeleteObject(hFont2); DeleteObject(hFont3); DeleteObject(hFont4); DestroyWindow(hWnd); } break; case WM_DESTROY: PostQuitMessage(0); break; default: return (DefWindowProc(hWnd, msg, wp, lp)); } return 0; }
WM_CREATEメッセージが来たら、コモンコントロールの初期化をします。
次に自作関数MakeMyTreeを呼んでツリービューを作ります。
いくつかのフォントを用意します。この時フォントの高さ、幅を同じにしておきます。 ツリービューのフォントを"system"に設定しておきます。
WM_SIZEメッセージが来たらツリービューの大きさを調整します。
WM_NOTIFYメッセージが来たら、これがツリービューから来ていて、かつNM_CUSTOMDRAWで あれば、カスタムドローの処理をします。
アイテムのレベルに応じて、フォント、文字色、背景色を変えます。
メニューからIDM_ADDPARENTが選択されたらDialogBoxParam関数で項目名入力用の ダイアログボックスを出します。最後の引数に配列szNameの最初の文字のアドレスを指定します。 ダイアログボックスでOKボタンが押されて、戻ってきたらTreeView_InsertItemマクロで 親項目を挿入します。
メニューでIDM_DELが選択されたら、選択されている項目をTreeView_DeleteItemマクロで 削除します。
メニューでIDM_ADDCHILDが選択されたら、IDM_ADDPARENTの時と同じダイアログボックスを 出します。 あらかじめ、選択されているアイテムを調べておいてhParentを選択されているアイテムにしておきます。
ツリービューを作る関数です。第111章、 第200章などを参照してください。HWND MakeMyTree(HWND hWnd) { HWND hTree; HINSTANCE hInst; hInst = (HINSTANCE)GetWindowLong(hWnd, GWL_HINSTANCE); hTree = CreateWindowEx(0, //拡張スタイル WC_TREEVIEW, //ウィンドウクラス "", //ウィンドウの名前 WS_CHILD | WS_VISIBLE | WS_BORDER | TVS_HASBUTTONS | TVS_LINESATROOT | TVS_HASLINES,//ウィンドウスタイル 0, 0, 0, 0,//位置、大きさ hWnd,//親ウィンドウ (HMENU)ID_TREEVIEW,//ID hInst,//インスタンスハンドル NULL); return hTree; }
項目名入力用のダイアログボックスのプロシージャです。LRESULT CALLBACK AddItemProc(HWND hDlg, UINT msg, WPARAM wp, LPARAM lp) { static char *lpszItem; switch (msg) { case WM_INITDIALOG: lpszItem = (char *)lp; return TRUE; case WM_COMMAND: switch (LOWORD(wp)) { case IDOK: Edit_GetText(GetDlgItem(hDlg, IDC_EDIT1), lpszItem, ITEMNAME_SIZE); EndDialog(hDlg, IDOK); return TRUE; case IDCANCEL: EndDialog(hDlg, IDCANCEL); return FALSE; } return FALSE; } return FALSE; }
WM_INITDIALOGメッセージが来たらwParamを調べて、charへのポインタに保存しておきます。 これは、呼び出し側の szName[ITEMNAME_SIZE]の最初の要素のアドレスになります。
OKボタンが押されたらエディットコントロールの文字列を取得してダイアログを閉じます。
さて、今回も前章と似たようなプログラムでした。カスタムドローの所をいろいろ変えて、 どのようなことが起こるか研究してみてください。
Update 27/Mar/2000 By Y.Kumei