В операционной системе Microsoft Windows 95 имеется мощное средство, предназначенное для просмотра и редактирования иерархических структур данных. Это орган управления Tree View (рис. 4.1).
Рис. 4.1. Использование органа управления Tree View для просмотра ресурсов приложения в среде Microsoft Visual C++
С помощью такого органа управления вы можете просматривать иерархическую структуру каталогов, пространство имен Microsoft Windows 95 или любую другую информацию, структуру которой можно представить в виде дерева.
В этой главе мы расскажем о том, как можно создать и использовать такой орган управления. Мы приведем исходные тексты приложения Some Books, которое демонстрирует основные возможности органа управления Tree View . Более сложные (и более трудные для изучения) примеры вы найдете в SDK.
Заметим, что приемы работы с органом Tree View во многом напоминают приемы работы с органом управления List View, рассмотренном нами в предыдущей главе.
Ниже мы перечислили основные действия, которое должно выполнить приложение для того чтобы создать орган управления Tree View:
Окно органа управления Tree View создается функцией CreateWindowEx на базе класса окна WC_TREEVIEW:
hwndTree = CreateWindowEx(0L, WC_TREEVIEW, "", WS_VISIBLE | WS_CHILD | WS_BORDER | TVS_HASLINES | TVS_HASBUTTONS | TVS_LINESATROOT, 0, 0, rc.right - rc.left, rc.bottom - rc.top, hWnd, (HMENU) IDC_TREEVIEW, hInst, NULL);
Перед вызовом этой функции (как и для других рассмотренных в этой книге органов управления) вы должны загрузить в память DLL-библиотеку COMCTL32.DLL . Это можно сделать при помощи функции InitCommonControls , вызвав ее без параметров.
Для окна органа управления Tree View вы можете
использовать следующие дополнительные стили
окна:
Стиль |
Описание |
TVS_HASBUTTONS |
Если указан стиль TVS_HASBUTTONS, создается
кнопка небольших размеров, внутри которой может
отображаться символ "+" или "-". Эта
кнопка используется для того чтобы раскрыть или
закрыть список вложенных элементов |
TVS_HASLINES |
Для отображения иерархии элементов
используются пунктирные линии |
TVS_LINESATROOT |
Указанные выше линии используются и для
присоединения корневого элемента дерева. Этот
стиль должен использоваться совместно со стилем
TVS_HASLINES |
TVS_EDITLABELS |
Этот стиль позволяет пользователю
редактировать название элемента списка |
TVS_SHOWSELALWAYS |
Выбранные элементы списка отображаются
с выделением даже в том случае, когда орган
управления Tree View не активен |
TVS_DISABLEDROP |
Если указан этот стиль, орган управления
Tree View не посылает родительскому окну извещение
TVN_BEGINDRAG |
Дополнительные стили окна могут комбинироваться между собой и со стандартными стилями окна при помощи логической операции ИЛИ.
С помощью пары функций GetWindowLong и SetWindowLong приложение может изменить стиль созданного ранее окна органа управления Tree View (можно использовать тот же способ, что и для органа управления List View).
Для органа управления Tree View вы должны создать список изображений, которые будут использованы для отображения состояния элементов дерева.
Заметим, что каждый элемент может находиться в выбранном или невыбранном состоянии. Соответственно, для отображения этих состояний вы можете использовать разные изображения.
Список изображений создается рассмотренной нами ранее функцией ImageList_Create :
HIMAGELIST WINAPI ImageList_Create( int cx, // ширина изображения int cy, // высота изображения UINT flags, // тип изображения int cInitial, // первоначальное количество изображений int cGrow); // количество изображений, на которое // увеличится размер списка при добавлении новых изображений
Вот, например, как создается список из трех изображений размером 33 х 33 пиксела в нашем приложении Some Books, исходные тексты которого мы приведем немного позже:
himl = ImageList_Create(33, 33, FALSE, 3, 0);
Обычно в список изображений для органа управления Tree View добавляют не пиктограммы, имеющие квадратную форму и относительно большой размер, и битовые изображения. Это можно сделать при помощи макрокоманды ImageList_Add :
hBmp = LoadBitmap(hInst, MAKEINTRESOURCE(IDB_BOOKS)); idxBooks = ImageList_Add(himl, hBmp, NULL); hBmp = LoadBitmap(hInst, MAKEINTRESOURCE(IDB_BOOK_CLOSED)); idxBookClosed = ImageList_Add(himl, hBmp, NULL); hBmp = LoadBitmap(hInst, MAKEINTRESOURCE(IDB_BOOK_OPENED)); idxBookOpened = ImageList_Add(himl, hBmp, NULL);
Эта макрокоманда возвращает номера добавленных изображений, которые потребуются в дальнейшем при добавлении элементов в дерево.
Созданный список изображений подключается к органу управления Tree View при помощи макрокоманды TreeView_SetImageList :
HIMAGELIST TreeView_SetImageList( HWND hwnd, // идентификатор окна органа Tree View HIMAGELIST himl, // идентификатор подключаемого списка int iImageTree); // тип изображений в списке
Первые два параметра макрокоманды задают, соответственно, идентификатор окна органа Tree View и идентификатор подключаемого списка, который мы только что создали и заполнили.
Параметр iImageTree может иметь следующие значения:
Значение |
Описание |
TVSIL_NORMAL |
Обычные список изображений для
отображения выбранного и невыбранного состояния
элементов дерева |
TVSIL_STATE |
Список содержит изображения, которые
используются для отображения состояния
элементов дерева (из-за недостатка места в нашей
книге не рассматривается) |
Ниже приведена строка исходного текста приложения, подключающая список изображений himl к органу управления hwndTree:
TreeView_SetImageList(hwndTree, himl, TVSIL_NORMAL);
Вставка элементов в дерево выполняется при помощи макрокоманды TreeView_InsertItem , которая посылает органу управления Tree View сообщение TVM_INSERTITEM :
HTREEITEM TreeView_InsertItem( HWND hwnd, // идентификатор окна органа Tree View TV_INSERTSTRUCT lpis); // указатель на структуру TV_INSERTSTRUCT
Через параметр hwnd Этой макрокоманде необходимо передать идентификатор созданного органа управления Tree View, а через параметр lpis - адрес структуры типа TV_INSERTSTRUCT . Эта структура и указатель на нее определены так:
typedef struct _TV_INSERTSTRUCT { HTREEITEM hParent; // идентификатор родительского элемента HTREEITEM hInsertAfter; // идентификатор элемента, после // которого будет выполнена вставка TV_ITEM item; // информация о вставляемом элементе } TV_INSERTSTRUCT, FAR *LPTV_INSERTSTRUCT;
Если вставляется корневой элемент, поле hParent должно содержать значение TVI_ROOT или NULL. Если же вставляется вложенный элемент, в это поле необходимо записать идентификатор родительского элемента (т. е. элемента, расположенного выше по иерархии).
В поле hInsertAfter следует записать идентификатор
элемента, после которого будет вставлен данный
элемент или одно из следующих значений:
Значение |
Расположение вставляемого элемента |
TVI_FIRST |
В начале списка |
TVI_LAST |
В конце списка |
TVI_SORT |
По алфавитному порядку |
Структура item содержит информацию о вставляемом элементе. Соответствующий тип TV_ITEM определен следующим образом:
typedef struct _TV_ITEM { UINT mask // маска использования полей структуры TV_ITEM HTREEITEM hItem; // номер элемента UINT state; // текущее состояние элемента UINT stateMask; // маска состояния элемента LPSTR pszText; // адрес текстового буфера int cchTextMax; // размер текстового буфера int iImage; // номер изображения невыбранного элемента int iSelectedImage;// номер изображения выбранного элемента int cChildren; // флаг дочерних элементов LPARAM lParam;// 32-битное значение, связанное с элементом } TV_ITEM, FAR *LPTV_ITEM;
Назначение большинства полей такое же, как и одноименных полей структуры LV_ITEM.
Поле маски mask определяет, какие из полей
структуры TV_ITEM будут использованы при добавлении
элементов. Возможны следующие значения масок (их
можно объединять при помощи логической операции
ИЛИ):
Маска |
Заполненное поле структуры TV_ITEM |
TVIF_TEXT |
pszText |
TVIF_IMAGE |
iImage |
TVIF_SELECTEDIMAGE |
iSelectedImage |
TVIF_PARAM |
lParam |
TVIF_CHILDREN |
cChildren |
TVIF_HANDLE |
hItem |
TVIF_STATE |
state |
В поля iImage и iSelectedImage следует записать номера изображений из списка, которые будут использованы для элемента, соответственно, в выбранном и невыбранном состоянии.
Поле cChildren содержит флаг, который показывает, есть ли у данного элемента связанные с ним дочерние элементы. Если есть, то в этом поле должно быть записано значение 1, в противном случае - 0. Если же в поле cChildren записать значение I_CHILDRENCALLBACK, приложение должно обрабатывать извещения TVN_GETDISPINFO и TVN_SETDISPINFO (аналогичные извещениям LVN_GETDISPINFO и LVN_SETDISPINFO, которые посылаются органу управления List View).
Для удобства в приложении Some Books мы добавляем элементы дерева при помощи функции InsTreeItem, исходный текст которой приведен ниже:
HTREEITEM InsTreeItem(HTREEITEM hParent, LPSTR szText, HTREEITEM hAfter, int iImage, int iSelectedImage) { TV_INSERTSTRUCT tvins; HTREEITEM hItem; memset(&tvins, 0, sizeof(tvins)); tvins.item.mask = TVIF_TEXT | TVIF_IMAGE | TVIF_SELECTEDIMAGE | TVIF_PARAM; tvins.item.pszText = szText; tvins.item.cchTextMax = lstrlen(szText); tvins.item.iImage = iImage; tvins.item.iSelectedImage = iSelectedImage; tvins.hInsertAfter = hAfter; tvins.hParent = hParent; hItem = TreeView_InsertItem(hwndTree, &tvins); return hItem; }
Процедура заполнения дерева в приложении Some Books выглядит следующим образом.
Вначале мы вставляем корневой элемент, которому соответствует строка szBooks ("Книги издательства АО 'Диалог-МИФИ'"):
hItem = InsTreeItem((HTREEITEM)TVI_ROOT, szBooks, (HTREEITEM)TVI_FIRST, idxBooks, idxBooks);
Этот элемент является корневым, поэтому параметр hParent имеет значение TVI_ROOT. Вставляемый элемент будет расположен первым по порядку, так как для параметра hAfter задано значение TVI_FIRST.
Далее мы вставляем два элемента, расположенные ниже по иерархии (первый уровень вложенности). Это текстовые строки szBSPSeries ("Библиотека системного программиста, (C) Фролов А.В., Фролов Г.В., 1991-1995 г.") и szStepSeries ("Персональный компьютер. Шаг за шагом, (C) Фролов А.В., Фролов Г.В., 1994-1995 г."):
hBSPItem = InsTreeItem(hItem, szBSPSeries, (HTREEITEM)TVI_SORT, idxBooks, idxBooks); hStepItem = InsTreeItem(hItem, szStepSeries, (HTREEITEM)TVI_SORT, idxBooks, idxBooks);
Затем для каждого элемента первого уровня вставляем по два элемента второго уровня:
InsTreeItem(hBSPItem, rgSPLBookInfo[0].szBookName, (HTREEITEM)TVI_SORT, idxBookClosed, idxBookOpened); InsTreeItem(hBSPItem, rgSPLBookInfo[1].szBookName, (HTREEITEM)TVI_SORT, idxBookClosed, idxBookOpened); InsTreeItem(hStepItem, rgStepBookInfo[0].szBookName, (HTREEITEM)TVI_SORT, idxBookClosed, idxBookOpened); InsTreeItem(hStepItem, rgStepBookInfo[1].szBookName, (HTREEITEM)TVI_SORT, idxBookClosed, idxBookOpened);
Так как в параметре hAfter указана константа TVI_SORT, все добавленные элементы будут отображаться в алфавитном порядке.
При необходимости приложение, создавшее орган управления Tree View, может обрабатывать сообщение WM_NOTIFY , поступающее в функцию родительского окна.
Код извещения передается через поле code структуры NMHDR . Напомним, что адрес этой структуры находится в параметре lParam сообщения WM_NOTIFY .
Родительское окно может получить следующие
коды извещений:
Код извещения |
Описание |
TVN_BEGINDRAG |
Начало операции переноса "drag and drop"
при помощи левой клавиши мыши |
TVN_BEGINLABELEDIT |
Начало операции редактирования
названия элемента |
TVN_BEGINRDRAG |
Начало операции переноса "drag and drop"
с использованием правой клавиши мыши |
TVN_DELETEITEM |
Удаление определенного элемента дерева |
TVN_ENDLABELEDIT |
Завершение операции редактирования
названия элемента |
TVN_GETDISPINFO |
Орган управления запрашивает
информацию, необходимую для отображения
элемента |
TVN_ITEMEXPANDED |
Произошло расширение списка дочерних
(вложенных) элементов |
TVN_ITEMEXPANDING |
С помощью этого извещения родительскому
окну предоставляется возможность отменить
начатое расширение списка дочерних элементов |
TVN_KEYDOWN |
Была нажата клавиша |
TVN_SETDISPINFO |
Родительское окно должно обновить
информацию об элементах дерева, которую оно
хранит в своих структурах данных |
Более подробную информацию об использовании этих извещений вы сможете найти в справочной системе SDK.
Перечислим сообщения, которое можно посылать
окну органа управления Tree View.
Сообщение |
Описание |
TVM_CREATEDRAGIMAGE |
Создание изображения, необходимого для
выполнения операции перемещения "drag and drop" |
TVM_DELETEITEM |
Удаление конкретного элемента из дерева
|
TVM_EDITLABEL |
Начать процесс редактирования имени
элемента |
TVM_ENSUREVISIBLE |
Размещение элемента в окне просмотра
таким образом, чтобы он были виден полностью или
по крайней мере частично. При необходимости
добавляются полоса просмотра |
TVM_EXPAND |
Расширение или сокращение списка
элементов |
TVM_GETCOUNT |
Определение количества элементов в
дереве |
TVM_GETEDITCONTROL |
Определение идентификатора редактора,
который применяется для редактирования названия
элемента |
TVM_GETIMAGELIST |
Получение идентификатора списка
изображения |
TVM_GETINDENT |
Определение размера сдвига вложенных
элементов (в пикселах) |
TVM_GETISEARCHSTRING |
Получение инкрементальной строки
поиска |
TVM_GETITEM |
Получение всех или некоторых атрибутов
элемента дерева |
TVM_GETITEMRECT |
Определение границ, занимаемых
элементом в окне просмотра. С помощью этого
сообщения можно определить, виден ли указанный
элемент в окне органа управления Tree View |
TVM_GETNEXTITEM |
С помощью этого сообщения можно
получить элемент, расположенный в соответствии с
заданным критерием (следующий, следующий или
предыдущий по иерархии, корневой, первый видимый
и т. д.) |
TVM_GETVISIBLECOUNT |
Определение количества элементов,
которые видны в окне органа управления Tree View
полностью |
TVM_HITTEST |
Определение элемента, расположенного в
данной позиции |
TVM_INSERTITEM |
Добавление элемента |
TVM_SELECTITEM |
Выделение заданного элемента списка,
сдвиг элементов до тех пор пока заданный элемент
не станет видимым или перерисовка элемента в
стиле, обозначающем цель операции перемещения
"drag and drop" |
TVM_SETIMAGELIST |
Подключение списка изображений к органу
управления Tree View |
TVM_SETINDENT |
Установка размера сдвига вложенных
элементов (в пикселах) |
TVM_SETITEM |
Установка всех или некоторых атрибутов
заданного элемента дерева |
TVM_SORTCHILDREN |
Сортировка вложенных элементов для
заданного элемента дерева |
TVM_SORTCHILDRENCB |
Сортировка вложенных элементов для
заданного элемента дерева с использованием
заданной функции сравнения |
Приложение Some Books создает дерево и отображает его в своем главном окне, как это показано на рис. 4.2.
Рис. 4.2. Просмотр дерева в главном окне приложения Some Books
Вы можете открывать или закрывать отдельные уровни иерархии при помощи кнопок с изображением символов "+" и "-", а также выбирать любые элементы дерева. При выборе вложенных элементов нижней иерархии изображение закрытой книги заменяется на изображение открытой книги.
Все функции приложения Some Books определены в файле somebook.c (листинг 4.1).
Листинг 4.1. Файл somebook\somebook.c
#define STRICT #include <windows.h> #include <windowsx.h> #include <commctrl.h> #include "resource.h" #include "afxres.h" #include "somebook.h" // Корневой элемент дерева char szBooks[] = "Книги издательства АО 'Диалог-МИФИ'"; // Вложенные элементы первого уровня char szBSPSeries[] = "Библиотека системного программиста, " "(C) Фролов А.В., Фролов Г.В., 1991-1995 г."; char szStepSeries[] = "Персональный компьютер. Шаг за шагом, " "(C) Фролов А.В., Фролов Г.В., 1994-1995 г."; typedef struct tagBOOKINFO { char szBookName[256]; UINT nVolumeNumber; } BOOKINFO; // Вложенные элементы второго уровня BOOKINFO rgSPLBookInfo[]= { {"Microsoft Windows 3.1 для программиста", 11}, {"Графический интерфейс GDI в Microsoft Windows ", 14}, }; BOOKINFO rgStepBookInfo[]= { {"Операционная система Microsoft Windows. " "Руководство пользователя", 2}, {"Локальные сети персональных компьютеров", 3}, }; // Номера изображений для органа управления Tree View int idxBooks, idxBookClosed, idxBookOpened; HINSTANCE hInst; char szAppName[] = "SomeBookApp"; char szAppTitle[] = "Some programmer and user books"; HWND hwndTree; // ----------------------------------------------------- // Функция WinMain // ----------------------------------------------------- int APIENTRY WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpCmdLine, int nCmdShow) { WNDCLASSEX wc; HWND hWnd; MSG msg; hInst = hInstance; // Преверяем, не было ли это приложение запущено ранее hWnd = FindWindow(szAppName, NULL); if(hWnd) { if(IsIconic(hWnd)) ShowWindow(hWnd, SW_RESTORE); SetForegroundWindow(hWnd); return FALSE; } // Регистрируем класс окна memset(&wc, 0, sizeof(wc)); wc.cbSize = sizeof(WNDCLASSEX); wc.hIconSm = LoadImage(hInst, MAKEINTRESOURCE(IDI_APPICONSM), IMAGE_ICON, 16, 16, 0); wc.style = 0; wc.lpfnWndProc = (WNDPROC)WndProc; wc.cbClsExtra = 0; wc.cbWndExtra = 0; wc.hInstance = hInst; wc.hIcon = LoadImage(hInst, MAKEINTRESOURCE(IDI_APPICON), IMAGE_ICON, 32, 32, 0); wc.hCursor = LoadCursor(NULL, IDC_ARROW); wc.hbrBackground = (HBRUSH) (COLOR_WINDOW + 1); wc.lpszMenuName = MAKEINTRESOURCE(IDR_APPMENU); wc.lpszClassName = szAppName; if(!RegisterClassEx(&wc)) if(!RegisterClass((LPWNDCLASS)&wc.style)) return FALSE; // Создаем главное окно приложения hWnd = CreateWindow(szAppName, szAppTitle, WS_OVERLAPPEDWINDOW,CW_USEDEFAULT, 0, CW_USEDEFAULT, 0, NULL, NULL, hInst, NULL); if(!hWnd) return(FALSE); // Отображаем окно и запускаем цикл обработки сообщений ShowWindow(hWnd, nCmdShow); UpdateWindow(hWnd); while(GetMessage (&msg, NULL, 0, 0)) { TranslateMessage(&msg); DispatchMessage(&msg); } return msg.wParam; } // ----------------------------------------------------- // Функция WndProc // ----------------------------------------------------- LRESULT WINAPI WndProc(HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam) { switch(msg) { HANDLE_MSG(hWnd, WM_CREATE, WndProc_OnCreate); HANDLE_MSG(hWnd, WM_DESTROY, WndProc_OnDestroy); HANDLE_MSG(hWnd, WM_COMMAND, WndProc_OnCommand); HANDLE_MSG(hWnd, WM_SIZE, WndProc_OnSize); default: return(DefWindowProc(hWnd, msg, wParam, lParam)); } } // ----------------------------------------------------- // Функция WndProc_OnCreate // ----------------------------------------------------- BOOL WndProc_OnCreate(HWND hWnd, LPCREATESTRUCT lpCreateStruct) { RECT rc; HIMAGELIST himl; HBITMAP hBmp; HTREEITEM hRootItem; HTREEITEM hBSPItem; HTREEITEM hStepItem; // Определяем размеры внутренней области главного окна GetClientRect(hWnd, &rc); // Инициализируем библиотеку стандартных органов управления InitCommonControls(); // Создаем орган управления Tree View hwndTree = CreateWindowEx(0L, WC_TREEVIEW, "", WS_VISIBLE | WS_CHILD | WS_BORDER | TVS_HASLINES | TVS_HASBUTTONS | TVS_LINESATROOT, 0, 0, rc.right - rc.left, rc.bottom - rc.top, hWnd, (HMENU) IDC_TREEVIEW, hInst, NULL); if(hwndTree == NULL) return FALSE; // Создаем список изображений himl = ImageList_Create(33, 33, FALSE, 3, 0); // Добавляем изображения в список hBmp = LoadBitmap(hInst, MAKEINTRESOURCE(IDB_BOOKS)); idxBooks = ImageList_Add(himl, hBmp, NULL); hBmp = LoadBitmap(hInst, MAKEINTRESOURCE(IDB_BOOK_CLOSED)); idxBookClosed = ImageList_Add(himl, hBmp, NULL); hBmp = LoadBitmap(hInst, MAKEINTRESOURCE(IDB_BOOK_OPENED)); idxBookOpened = ImageList_Add(himl, hBmp, NULL); // Добавляем список изображений TreeView_SetImageList(hwndTree, himl, TVSIL_NORMAL); // Вставляем корневой элемент hRootItem = InsTreeItem((HTREEITEM)TVI_ROOT, szBooks, (HTREEITEM)TVI_FIRST, idxBooks, idxBooks); // Вставляем вложенные элементы первого уровня hBSPItem = InsTreeItem(hRootItem, szBSPSeries, (HTREEITEM)TVI_SORT, idxBooks, idxBooks); hStepItem = InsTreeItem(hRootItem, szStepSeries, (HTREEITEM)TVI_SORT, idxBooks, idxBooks); // Вставляем вложенные элементы второго уровня InsTreeItem(hBSPItem, rgSPLBookInfo[0].szBookName, (HTREEITEM)TVI_SORT, idxBookClosed, idxBookOpened); InsTreeItem(hBSPItem, rgSPLBookInfo[1].szBookName, (HTREEITEM)TVI_SORT, idxBookClosed, idxBookOpened); InsTreeItem(hStepItem, rgStepBookInfo[0].szBookName, (HTREEITEM)TVI_SORT, idxBookClosed, idxBookOpened); InsTreeItem(hStepItem, rgStepBookInfo[1].szBookName, (HTREEITEM)TVI_SORT, idxBookClosed, idxBookOpened); return TRUE; } // ----------------------------------------------------- // Функция InsTreeItem // ----------------------------------------------------- HTREEITEM InsTreeItem(HTREEITEM hParent, LPSTR szText, HTREEITEM hAfter, int iImage, int iSelectedImage) { TV_INSERTSTRUCT tvins; HTREEITEM hItem; memset(&tvins, 0, sizeof(tvins)); tvins.item.mask = TVIF_TEXT | TVIF_IMAGE | TVIF_SELECTEDIMAGE | TVIF_PARAM; tvins.item.pszText = szText; tvins.item.cchTextMax = lstrlen(szText); tvins.item.iImage = iImage; tvins.item.iSelectedImage = iSelectedImage; tvins.hInsertAfter = hAfter; tvins.hParent = hParent; hItem = TreeView_InsertItem(hwndTree, &tvins); return hItem; } // ----------------------------------------------------- // Функция WndProc_OnDestroy // ----------------------------------------------------- #pragma warning(disable: 4098) void WndProc_OnDestroy(HWND hWnd) { DestroyWindow(hwndTree); PostQuitMessage(0); return 0L; } // ----------------------------------------------------- // Функция WndProc_OnCommand // ----------------------------------------------------- #pragma warning(disable: 4098) void WndProc_OnCommand(HWND hWnd, int id, HWND hwndCtl, UINT codeNotify) { DWORD dwStyle = 0; switch (id) { case ID_FILE_EXIT: PostQuitMessage(0); return 0L; break; case ID_HELP_ABOUT: break; default: break; } return FORWARD_WM_COMMAND(hWnd, id, hwndCtl, codeNotify, DefWindowProc); } // ----------------------------------------------------- // Функция WndProc_OnSize // ----------------------------------------------------- #pragma warning(disable: 4098) void WndProc_OnSize(HWND hwnd, UINT state, int cx, int cy) { MoveWindow(hwndTree, 0, 0, cx, cy, TRUE); return FORWARD_WM_SIZE(hwnd, state, cx, cy, DefWindowProc); }
Файл somebook.h (листинг 4.2) содержит писание функций и определение константы IDC_TREEVIEW (идентификатор органа управления Tree View).
Листинг 4.2. Файл somebook\somebook.h
// ----------------------------------------------------- // Описание функций // ----------------------------------------------------- LRESULT WINAPI WndProc(HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam); BOOL WndProc_OnCreate(HWND hWnd, LPCREATESTRUCT lpCreateStruct); void WndProc_OnDestroy(HWND hWnd); void WndProc_OnCommand(HWND hWnd, int id, HWND hwndCtl, UINT codeNotify); LRESULT WndProc_OnNotify(HWND hWnd, int idFrom, NMHDR FAR * pnmhdr); void WndProc_OnSize(HWND hwnd, UINT state, int cx, int cy); void WndProc_OnDrawItem(HWND hwnd, const DRAWITEMSTRUCT * lpDrawItem); HTREEITEM InsTreeItem(HTREEITEM hParent, LPSTR szText, HTREEITEM hAfter, int iImage, int iSelectedImage); #define IDC_TREEVIEW 1235
В файле resource.h (который создается автоматически системой Microsoft Visual C++) находятся определения констант для работы с ресурсами приложения Some Books. Этот файл представлен в листинге 4.3.
Листинг 4.3. Файл somebook\resource.h
//{{NO_DEPENDENCIES}} // Microsoft Visual C++ generated include file. // Used by somebook.rc // #define IDR_APPMENU 102 #define IDI_APPICON 103 #define IDI_APPICONSM 104 #define IDB_BOOKS 115 #define IDB_BOOK_CLOSED 118 #define IDB_BOOK_OPENED 120 #define ID_FILE_EXIT 40001 #define ID_HELP_ABOUT 40003 // Next default values for new objects // #ifdef APSTUDIO_INVOKED #ifndef APSTUDIO_READONLY_SYMBOLS #define _APS_NEXT_RESOURCE_VALUE 121 #define _APS_NEXT_COMMAND_VALUE 40002 #define _APS_NEXT_CONTROL_VALUE 1000 #define _APS_NEXT_SYMED_VALUE 101 #endif #endif
Файл somebook.rc (листинг 4.4) содержит определение ресурсов приложения Some Books. Он создается автоматически.
Листинг 4.4. Файл somebook\somebook.rc
//Microsoft Visual C++ generated resource script. #include "resource.h" #define APSTUDIO_READONLY_SYMBOLS ////////////////////////////////////////////////////////////// // Generated from the TEXTINCLUDE 2 resource. // #include "afxres.h" ////////////////////////////////////////////////////////////// #undef APSTUDIO_READONLY_SYMBOLS ////////////////////////////////////////////////////////////// // Menu // IDR_APPMENU MENU DISCARDABLE BEGIN POPUP "&File" BEGIN MENUITEM "E&xit", ID_FILE_EXIT END END #ifdef APSTUDIO_INVOKED ////////////////////////////////////////////////////////////// // TEXTINCLUDE // 1 TEXTINCLUDE DISCARDABLE BEGIN "resource.h\0" END 2 TEXTINCLUDE DISCARDABLE BEGIN "#include ""afxres.h""\r\n" "\0" END 3 TEXTINCLUDE DISCARDABLE BEGIN "\r\n" "\0" END ////////////////////////////////////////////////////////////// #endif // APSTUDIO_INVOKED ////////////////////////////////////////////////////////////// // Icon // IDI_APPICON ICON DISCARDABLE "sbook.ico" IDI_APPICONSM ICON DISCARDABLE "sbooksm.ico" ////////////////////////////////////////////////////////////// // Bitmap // IDB_BOOKS BITMAP DISCARDABLE "books.bmp" IDB_BOOK_CLOSED BITMAP DISCARDABLE "closed.bmp" IDB_BOOK_OPENED BITMAP DISCARDABLE "opened.bmp" ////////////////////////////////////////////////////////////// // String Table STRINGTABLE DISCARDABLE BEGIN ID_FILE_EXIT "Quits the application" END #ifndef APSTUDIO_INVOKED ////////////////////////////////////////////////////////////// // Generated from the TEXTINCLUDE 3 resource. // ////////////////////////////////////////////////////////////// #endif // not APSTUDIO_INVOKED
В этом разделе мы кратко опишем глобальные переменные и функции приложения Some Books.
В массиве szBooks находится текстовая строка, которая отображается как корневой элемент дерева (название библиотеки книг - "Книги издательства АО 'Диалог-МИФИ'").
Массивы szBSPSeries и szStepSeries содержат два вложенных элемента, которые являются дочерними для корневого элемента (названия двух серий книг, которые издаются в АО 'Диалог-МИФИ').
И, наконец, массивы rgSPLBookInfo и rgStepBookInfo хранят вложенные элементы самого нижнего уровня (названия некоторых книг, входящих в эти две серии). Эти элементы являются дочерними для элементов, заданных массивами szBSPSeries и szStepSeries.
В переменных idxBooks, idxBookClosed и idxBookOpened находятся номера изображений. Изображение с номером idxBooks используется для корневого элемента и элементов первого уровня вложенности. Изображения idxBookClosed и idxBookOpened (закрытая и раскрытая книга) нужны для элементов нижнего уровня иерархии (названий книг).
В переменной hInst хранится идентификатор приложения, полученный функцией WinMain. Строчные массивы szAppName и szAppTitle хранят, соответственно, имя и заголовок приложения.
Переменная hwndTree используется для хранения идентификатора созданного органа управления Tree View.
Функция WinMain в приложении Some Books полностью аналогична функции WinMain предыдущего приложения. Она сохраняет идентификатор приложения и проверяет, не было ли это приложение запущено ранее. Если было, то активизируется окно работающего приложения.
Далее функция регистрирует класс главного окна приложения, создает и отображает это окно, а затем запускает цикл обработки сообщений.
Функция WndProc обрабатывает следующие сообщения: WM_CREATE, WM_DESTROY, WM_COMMAND, WM_SIZE. Как и в предыдущих наших приложениях, обработка выполняется с использованием макрокоманды HANDLE_MSG.
Эта функция обрабатывает сообщение WM_CREATE, создавая и инициализируя орган управления Tree View. Размеры окна органа управления устанавливаются равными размерам внутренней области главного окна приложения и в дальнейшем изменяются соответствующим образом обработчиком сообщения WM_SIZE.
В процессе инициализации функция WndProc_OnCreate создает список изображений, которые будут использованы для элементов дерева, и добавляет в этот список три изображения. При этом в глобальные переменные idxBooks, idxBookClosed и idxBookOpened записываются номера изображений. После этого список изображений подключается к органу управления Tree View.
Далее функция WndProc_OnCreate заполняет дерево, добавляя в него корневой элемент и вложенные элементы. Для этого она вызывает функцию InsTreeItem, описанную ниже.
Функция InsTreeItem добавляет элементы к дереву, вызывая для этого макрокоманду TreeView_InsertItem. Перед этим она инициализирует структуру tvins типа TV_INSERTSTRUCT, записывая в нее информацию о добавляемом элементе и его расположении в иерархии существующих элементов дерева.
Для каждого элемента, в частности, определяется пара номеров изображений iImage и iSelectedImage, которые используются для отображения элемента, соответственно, в невыбранном и выбранном состоянии.
Функция WndProc_OnDestroy вызывается, когда пользователь завершает работу приложения. Она уничтожает окно органа управления Tree View и останавливает цикл обработки сообщений, вызывая функцию PostQuitMessage.
Эта функция обрабатывает сообщение WM_COMMAND, поступающее от главного меню приложения.
Приложение изменяет размеры органа управления Tree View таким образом, чтобы они всегда соответствовали размерам внутренней области главного окна приложения Some Books. Для этого оно обрабатывает сообщение WM_SIZE. Изменения размеров окна органа управления выполняется функцией WndProc_OnSize.