4. Просмотр деревьев

В операционной системе 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, рассмотренном нами в предыдущей главе.

4.1. Создание органа управления Tree View

Ниже мы перечислили основные действия, которое должно выполнить приложение для того чтобы создать орган управления Tree 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, все добавленные элементы будут отображаться в алфавитном порядке.

4.2. Обработка извещений

При необходимости приложение, создавшее орган управления 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.

4.3. Сообщения для органа управления Tree View

Перечислим сообщения, которое можно посылать окну органа управления 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 Сортировка вложенных элементов для заданного элемента дерева с использованием заданной функции сравнения

4.4. Приложение Some Books

Приложение Some Books создает дерево и отображает его в своем главном окне, как это показано на рис. 4.2.

Рис. 4.2. Просмотр дерева в главном окне приложения Some Books

Вы можете открывать или закрывать отдельные уровни иерархии при помощи кнопок с изображением символов "+" и "-", а также выбирать любые элементы дерева. При выборе вложенных элементов нижней иерархии изображение закрытой книги заменяется на изображение открытой книги.

Исходные тексты приложения 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

Функция WinMain в приложении Some Books полностью аналогична функции WinMain предыдущего приложения. Она сохраняет идентификатор приложения и проверяет, не было ли это приложение запущено ранее. Если было, то активизируется окно работающего приложения.

Далее функция регистрирует класс главного окна приложения, создает и отображает это окно, а затем запускает цикл обработки сообщений.

WndProc

Функция WndProc обрабатывает следующие сообщения: WM_CREATE, WM_DESTROY, WM_COMMAND, WM_SIZE. Как и в предыдущих наших приложениях, обработка выполняется с использованием макрокоманды HANDLE_MSG.

WndProc_OnCreate

Эта функция обрабатывает сообщение WM_CREATE, создавая и инициализируя орган управления Tree View. Размеры окна органа управления устанавливаются равными размерам внутренней области главного окна приложения и в дальнейшем изменяются соответствующим образом обработчиком сообщения WM_SIZE.

В процессе инициализации функция WndProc_OnCreate создает список изображений, которые будут использованы для элементов дерева, и добавляет в этот список три изображения. При этом в глобальные переменные idxBooks, idxBookClosed и idxBookOpened записываются номера изображений. После этого список изображений подключается к органу управления Tree View.

Далее функция WndProc_OnCreate заполняет дерево, добавляя в него корневой элемент и вложенные элементы. Для этого она вызывает функцию InsTreeItem, описанную ниже.

InsTreeItem

Функция InsTreeItem добавляет элементы к дереву, вызывая для этого макрокоманду TreeView_InsertItem. Перед этим она инициализирует структуру tvins типа TV_INSERTSTRUCT, записывая в нее информацию о добавляемом элементе и его расположении в иерархии существующих элементов дерева.

Для каждого элемента, в частности, определяется пара номеров изображений iImage и iSelectedImage, которые используются для отображения элемента, соответственно, в невыбранном и выбранном состоянии.

WndProc_OnDestroy

Функция WndProc_OnDestroy вызывается, когда пользователь завершает работу приложения. Она уничтожает окно органа управления Tree View и останавливает цикл обработки сообщений, вызывая функцию PostQuitMessage.

WndProc_OnCommand

Эта функция обрабатывает сообщение WM_COMMAND, поступающее от главного меню приложения.

WndProc_OnSize

Приложение изменяет размеры органа управления Tree View таким образом, чтобы они всегда соответствовали размерам внутренней области главного окна приложения Some Books. Для этого оно обрабатывает сообщение WM_SIZE. Изменения размеров окна органа управления выполняется функцией WndProc_OnSize.