Для того чтобы ваше приложение соответствовало современным стандартам и было удобно в работе, к его главному окну желательно добавить такие органы управления, как Toolbar и Statusbar. Из предыдущей главы вы узнали, как они выглядят. Теперь пришло время убедиться в том, что при использовании программного интерфейса Microsoft Windows 95 добавление органов управления Toolbar и Statusbar не является такой сложной задачей, как это было при работе с 16-разрядным API Microsoft Windows версии 3.1.
Из чего "сделаны" новые органы управления?
Как и большинство других объектов в Microsoft Windows, органы управления создаются на базе предопределенных классов окон. Для них внутри Microsoft Windows имеются функции окон, реализующие все возможности и определяющие внешний вид органов управления.
Конкретнее: все, необходимое для создания органов управления в среде Microsoft Windows 95 и Microsoft Windows NT, расположено в библиотеке динамической загрузки COMCTL32.DLL .
Создать орган управления не сложнее, чем создать обыкновенное окно. Более того, для этого можно использовать привычные вам функции CreateWindow или CreateWindowEx, указывая зарезервированные имена классов. Например, Toolbar и Statusbar создаются на базе классов, соответственно, TOOLBARCLASSNAME и STATUSCLASSNAME .
Дополнительно вы можете указывать различные стили окна, определяющие расположение и поведение органов управления.
В программном интерфейсе Win32 имеются специальные функции, облегчающие создание новых органов управления. Например, вы можете создать Toolbar с помощью функции CreateToolbarEx , а Statusbar - с помощью функции CreateStatusWindow .
После того как орган управления будет создан, вы получите его идентификатор. Пользуясь этим идентификатором, вы сможете управлять органом, посылая ему сообщения функцией SendMessage. Например, вы можете добавлять кнопки в Toolbar или удалять их оттуда, блокировать или разблокировать кнопки, отображать текст в одной из областей окна Statusbar и т. д.
Когда приложение создает орган управления, оно сообщает соответствующей функции идентификатор родительского окна (т. е. окна, которое создает орган управления). Когда пользователь работает с органом управления, последний посылает в родительское окно извещения в виде сообщений WM_COMMAND или WM_NOTIFY .
Так как орган Toolbar обычно дублирует самые нужные строки главного меню приложения, сообщения WM_COMMAND, создаваемые этим органом, ничем не отличаются от аналогичных сообщений, поступающих от главного меню приложения. Разумеется, вы сами определяете коды сообщений WM_COMMAND для каждой кнопки органа Toolbar.
Сообщение WM_NOTIFY посылается родительскому окну для извещения о том, что пользователь выполняет какие-либо действия с органом управления, например, добавляет в него новую кнопку либо удаляет ненужную.
Поэтому все, что приложение должно сделать для использования органа управления, - это создать его, вызвав одну из перечисленных ранее функций, и обеспечить обработку нескольких сообщений. А это, согласитесь, совсем не сложно.
Для начала займемся изображениями, которые нарисованы на кнопках Toolbar . Затем мы расскажем вам о том, как создать окно органа управления Toolbar.
В предыдущих томах "Библиотеки системного программиста" мы создавали Toolbar средствами Win16. Тогда для каждой кнопки мы готовили три различных изображения - для отжатого, нажатого и заблокированного состояния. Если кнопок много, рисовать приходится долго.
Теперь вам нужно только одно изображение, правда, в нем должны располагаться рисунки для всех кнопок. В качестве примера лучше всего взять файл TOOLBAR.BMP, который создается автоматически генератором приложений AppWizard из Microsoft Visual C++ (рис. 2.1).
Рис. 2.1. Изображение стандартных кнопок для органа управления Toolbar , которое создается генератором приложений AppWizard
Вы можете создать свои изображения, взяв этот файл за основу, изменив существующие пиктограммы или добавив новые. Для этого можно воспользоваться, например, приложением Paint, которое входит в состав Microsoft Windows 95. Можно также создать новый bmp-файл, который должен быть 16-цветным. Другое условие - все пиктограммы в этом изображении должны быть одного размера.
В любом случае вы должны добавить созданное изображение в файл ресурсов приложения, снабдив его идентификатором, например:
IDB_TBBITMAP BITMAP DISCARDABLE "toolbar.bmp"
Если вы пользуетесь Microsoft Visual C++, то эта среда разработки приложений внесет все необходимые изменения в rc-файл автоматически, как только вы определите соответствующий ресурс.
Далее вы должны создать массив структур TBBUTTON , который описывает кнопки и разделители между группами кнопок. В этом массиве необходимо зарезервировать по одной структуре для каждой кнопки и для каждого разделителя групп кнопок.
В файле commctrl.h находится такое определение структуры TBBUTTON:
typedef struct _TBBUTTON { int iBitmap; int idCommand; BYTE fsState; BYTE fsStyle; DWORD dwData; int iString; } TBBUTTON, NEAR * PTBBUTTON, FAR * LPTBBUTTON; typedef const TBBUTTON FAR * LPCTBBUTTON;
В поле iBitmap каждой структуры массива необходимо записать номер кнопки (нумерация начинается с нуля). Для разделителя в этом поле следует указать нулевое значение.
В поле idCommand вы должны записать идентификатор, который будет передаваться родительскому окну с сообщением WM_COMMAND, когда пользователь нажмет соответствующую кнопку. Если элемент массива структур TBBUTTON описывает разделитель группы кнопок, в поле idCommand вам нужно записать нулевое значение.
Поле fsState должно содержать флаг исходного
состояния кнопки:
Флаг |
Описание |
TBSTATE_ENABLED |
Кнопка находится в разблокированном
состоянии. Если этот флаг не установлен, кнопка
заблокирована и отображается серым цветом |
TBSTATE_CHECKED |
Кнопка изображается в нажатом
состоянии. Этот флаг используется для кнопок с
фиксацией |
TBSTATE_HIDDEN |
Скрытая кнопка, не отображается |
TBSTATE_INDETERMINATE |
Кнопка отображается серым цветом |
TBSTATE_PRESSED |
Кнопка изображается в нажатом состоянии
|
TBSTATE_WRAP |
Кнопки, расположенные после той, что
имеет флаг TBSTATE_WRAP, отображаются на новой строке.
Таким образом, вы можете создать многострочный
Toolbar . Этот флаг можно указывать только тогда,
когда установлен флаг TBSTATE_ENABLED |
Теперь вам не нужно готовить отдельные изображения для разных состояний кнопок, так как при необходимости Toolbar изменяет внешний вид кнопок автоматически.
После того как Toolbar будет создан, приложение может изменять состояние кнопки, посылая окну Toolbar сообщение TB_SETSTATE. Позже мы перечислим сообщения, которые можно посылать окну Toolbar.
Теперь займемся полем fsStyle. Как видно из
названия, в это поле нужно записать стиль кнопки.
Здесь вы можете использовать комбинацию
следующих стилей:
Стиль кнопки |
Описание |
TBSTYLE_BUTTON |
Стандартная кнопка |
TBSTYLE_CHECK |
Кнопка с фиксацией. Эта кнопка
"залипает", когда пользователь нажимает на
нее. Для того чтобы вернуть кнопку в исходное
состояние, необходимо нажать на нее еще раз |
TBSTYLE_CHECKGROUP |
Кнопка с фиксацией, которая остается
нажатой до тех пор, пока нажата другая кнопка из
этой же группы |
TBSTYLE_GROUP |
Стандартная кнопка, которая остается
нажатой до тех пор, пока нажата другая кнопка из
этой же группы |
TBSTYLE_SEP |
Разделитель между группами кнопок |
Через поле dwData можно передать дополнительные данные, которые будут хранится в описании кнопки и использоваться при необходимости. Вы можете записать в это поле нулевое значение.
И, наконец, в поле iString можно записать номер текстовой строки, которую необходимо написать на поверхности кнопки. Для добавления таких строк к внутреннему списку Toolbar необходимо послать сообщение TB_ADDSTRING , передав вместе с ним адрес буфера с текстовыми строками. Все строки в этом буфере должны быть закрыты двоичным нулем, а последняя - двумя двоичными нулями.
Если текстовые строки не используются, в поле iString следует записать нулевой значение.
Вот образец подготовленного массива структур TBBUTTON, описывающий восемь кнопок и три разделителя между ними:
TBBUTTON tbButtons[] = { { 0, ID_FILE_NEW, TBSTATE_ENABLED, TBSTYLE_BUTTON, 0L, 0}, { 1, ID_FILE_OPEN, TBSTATE_ENABLED, TBSTYLE_BUTTON, 0L, 0}, { 2, ID_FILE_SAVE, TBSTATE_ENABLED, TBSTYLE_BUTTON, 0L, 0}, { 0, 0, TBSTATE_ENABLED, TBSTYLE_SEP, 0L, 0}, { 3, ID_EDIT_CUT, TBSTATE_ENABLED, TBSTYLE_BUTTON, 0L, 0}, { 4, ID_EDIT_COPY, TBSTATE_ENABLED, TBSTYLE_BUTTON, 0L, 0}, { 5, ID_EDIT_PASTE, TBSTATE_ENABLED, TBSTYLE_BUTTON, 0L, 0}, { 0, 0, TBSTATE_ENABLED, TBSTYLE_SEP, 0L, 0}, { 6, ID_FILE_PRINT, TBSTATE_ENABLED, TBSTYLE_BUTTON, 0L, 0}, { 0, 0, TBSTATE_ENABLED, TBSTYLE_SEP, 0L, 0}, { 7, ID_HELP_ABOUT, TBSTATE_ENABLED, TBSTYLE_BUTTON, 0L, 0} };
Теперь, когда вы подготовили файл изображения кнопок, создали для него идентификатор в файле ресурсов приложения и подготовили массив структур TBBITMAP , описывающий кнопки, можно создавать окно Toolbar. Проще всего это сделать при помощи специально предназначенной для этого функции CreateToolbarEx:
HWND CreateToolbarEx( HWND hwnd, // идентификатор родительского окна DWORD ws, // стили окна Toolbar UINT wID, // идентификатор органа Toolbar int nBitmaps,// количество пиктограмм с изображением кнопок HINSTANCE hBMInst, // идентификатор приложения UINT wBMID, // идентификатор битового изображения кнопок LPCTBBUTTON lpButtons, // адрес описания кнопок int iNumButtons, // количество кнопок int dxButton, // ширина кнопок (в пикселах) int dyButton, // высота кнопок int dxBitmap,// ширина пиктограмм, нарисованных на кнопках int dyBitmap,// высота пиктограмм, нарисованных на кнопках UINT uStructSize // размер структуры в байтах );
Как можно узнать из документации, эта функция создает окно Toolbar и добавляет в него кнопки, описанные в массиве структур TBBITMAP. При этом ей также нужно указать идентификатор изображения кнопок, а также другие параметры, перечисленные выше.
При удачном завершении функция CreateToolbarEx возвращает идентификатор созданного органа управления Toolbar , который можно будет использовать для посылки сообщений. Если же Toolbar по каким-либо причинам создать так и не удалось, функция возвращает значение NULL.
Перед первым вызовом этой функции следует вызвать функцию InitCommonControls, которая не имеет параметров, не возвращает никакого значения и служит для инициализации библиотеки стандартных органов управления.
Вот пример применения этой функции в приложении Smart Application, исходные тексты которого будут приведены позже:
hwndTb = CreateToolbarEx(hWnd, WS_CHILD | WS_BORDER | WS_VISIBLE | TBSTYLE_TOOLTIPS | CCS_ADJUSTABLE, IDT_TOOLBAR, // идентификатор органа Toolbar 8, // количество пиктограмм hInst, // идентификатор приложения IDB_TBBITMAP, // идентификатор битового изображения кнопок (LPCTBBUTTON)&tbButtons, // адрес описания кнопок 11, // количество кнопок 16, 16, // ширина и высота кнопок 16, 16, // ширина и высота пиктограмм sizeof(TBBUTTON)); // размер структуры в байтах
В качестве первого параметра мы передаем функции идентификатор главного окна приложения. Это окно будет получать от органа Toolbar извещения в виде сообщений WM_COMMAND и WM_NOTIFY.
Параметр ws определяет стили окна Toolbar . Так как это окно всегда является дочерним по отношению к создавшему его окну, необходимо использовать стиль WS_CHILD. Для того чтобы окно Toolbar имело рамку и было видимым, мы указываем стили WS_BORDER и WS_VISIBLE. Если нужно чтобы пользователь мог изменять внешний вид Toolbar, необходимо использовать стиль CCS_ADJUSTABLE.
Кроме того, для органа управления Toolbar вы можете
задать следующие стили:
Стиль Toolbar |
Описание |
TBSTYLE_TOOLTIP |
Вывод краткого описания кнопки в окне
органа управления Tool Tip |
TBSTYLE_ALTDRAG |
Если не указан стиль TBSTYLE_ALTDRAG, то
пользователь может передвигать кнопки по
поверхности Toolbar левой клавишей мыши при нажатой
клавише <Shift>. Если же этот стиль указан, для
перемещения кнопок используется клавиша <Alt>.
В любом случае кнопки можно передвигать только
тогда, когда указан стиль CCS_ADJUSTABLE |
TBSTYLE_WRAPABLE |
Окно Toolbar может состоять из нескольких
строк. Новые строки создаются в том случае, если
все кнопки не помещаются в одной строке |
Ниже мы перечислим стили, которые можно
использовать не только для Toolbar , но и для других
стандартных органов управления:
Стиль |
Описание |
CCS_ADJUSTABLE |
Если указан этот стиль, пользователь
может изменять конфигурацию органа управления |
CCS_BOTTOM |
Орган управления должен быть расположен
в нижней части внутренней области окна |
CCS_TOP |
Орган управления должен быть расположен
в верхней части внутренней области окна |
CCS_NODIVIDER |
В верхней части органа управления не
надо рисовать разделительную линию шириной 2
пиксела |
CCS_NOHILITE |
В верхней части органа управления не
надо рисовать выделяющую линию шириной 1 пиксел |
CCS_NOMOVEY |
В ответ на сообщение WM_SIZE орган
управления будет изменять свои горизонтальные
размеры и будет передвигаться по горизонтали,
однако при этом его вертикальные размеры
останутся прежними |
CCS_NOPARENTALIGN |
Орган управления не будет автоматически
перемещаться в верхнюю или нижнюю часть
родительского окна |
CCS_NORESIZE |
При установке начальных размеров не
будут использоваться размеры, заданные по
умолчанию. Приложение должно задать размеры
органа управления явным образом |
Как мы уже говорили, вы можете создать Toolbar при помощи функции CreateWindowEx. Однако в этом случае вам придется добавлять к Toolbar изображение кнопок и сами кнопки, посылая ему сообщения TB_ADDBITMAP и TB_ADDBUTTONS.
Ниже приведен фрагмент исходного текста приложения, создающего Toolbar при помощи функции CreateWindowEx:
TBADDBITMAP tbab; hwndTb = CreateWindowEx(0, TOOLBARCLASSNAME, (LPSTR)NULL, WS_CHILD | WS_BORDER | WS_VISIBLE | TBSTYLE_TOOLTIPS | CCS_ADJUSTABLE, 0, 0, 0, 0, hWnd, (HMENU)IDT_TOOLBAR, // идентификатор органа Toolbar hInst, // идентификатор приложения NULL); if(hwndTb == NULL) return FALSE; SendMessage(hwndTb, TB_BUTTONSTRUCTSIZE, (WPARAM) sizeof(TBBUTTON), 0); tbab.hInst = hInst; tbab.nID = IDB_TBBITMAP; SendMessage(hwndTb, TB_ADDBITMAP, (WPARAM)8, (LPARAM)&tbab); SendMessage(hwndTb, TB_ADDBUTTONS, (WPARAM)11, (LPARAM)&tbButtons);
Сразу после создания окна Toolbar мы посылаем ему сообщение TB_BUTTONSTRUCTSIZE, указывая в нем размер структуры TBBUTTON.
Затем мы заполняем структуру TBADDBITMAP:
typedef struct { HINSTANCE hInst; UINT nID; } TBADDBITMAP, *LPTBADDBITMAP;
В поле hInst этой структуры мы записываем идентификатор приложения, в поле nID - идентификатор изображения кнопок, определенного в ресурсах приложения.
Адрес заполненной структуры типа TBADDBITMAP и количество изображений мы передаем в качестве параметров сообщения TB_ADDBITMAP.
После этого мы добавляем в Toolbar кнопки, передавая ему сообщение TB_ADDBUTTONS. Через параметры этого сообщения мы передаем количество кнопок (с учетом разделителей) и адрес заполненной структуры TBBUTTON, описывающей кнопки.
Орган управления Toolbar посылает в родительское окно сообщения WM_COMMAND и WM_NOTIFY. О том, как обрабатывать первое из них, вы знаете из предыдущих томов "Библиотеки системного программиста", посвященных программированию для Microsoft Windows. Остановимся на обработке нового для вас сообщения WM_NOTIFY .
Через параметр wParam этого сообщения передается идентификатор органа управления. Если извещение пришло от Toolbar , то это должен быть идентификатор Toolbar.
Параметр lParam содержит указатель на структуру типа NMHDR или на структуру большего размера, содержащую в своем начале структуру NMHDR :
typedef struct tagNMHDR { HWND hwndFrom; UINT idFrom; UINT code; } NMHDR;
В этой структуре поле hwndFrom содержит идентификатор окна, приславшего сообщение, поле idFrom - идентификатор органа управления, приславшего сообщение, а поле code - код извещения.
Органы управления могут присылать следующие
коды извещения:
Код извещения |
Описание |
NM_CLICK |
Пользователь сделал щелчок левой
клавишей мыши внутри органа управления |
NM_DBLCLICK |
Пользователь сделал двойной щелчок
левой клавишей мыши внутри органа управления |
NM_RCLICK |
Пользователь сделал щелчок правой
клавишей мыши внутри органа управления |
NM_RDBLCLICK |
Пользователь сделал двойной щелчок
правой клавишей мыши внутри органа управления |
NM_RETURN |
Когда орган управления имел фокус ввода,
пользователь нажал клавишу <Enter> |
NM_KILLFOCUS |
Орган управления потерял фокус ввода |
NM_SETFOCUS |
Орган управления получил фокус ввода |
Перечисленные выше коды извещений используются всеми стандартными органами управления. Что же касается органа управления Toolbar , до для него имеются некоторые добавления.
Во-первых, параметр lParam сообщения WM_NOTIFY содержит указатель на структуру TBNOTIFY (содержащую в самом начале только что описанную структуру NMHDR ):
typedef struct { NMHDR hdr; int iItem; TBBUTTON tbButton; int cchText; LPTSTR pszText; } TBNOTIFY, FAR * LPTBNOTIFY;
В этой структуре поле iItem содержит номер кнопки, от которой пришло извещение (напомним, что нумерация кнопок начинается с нуля).
Структура tbButton типа TBBUTTON содержит описание кнопки. Мы уже рассказывали вам об этой структуре.
В поле cchText находится длина текстовой строки, соответствующей кнопке. Адрес этой строки передается через параметр pszText.
Во-вторых, для органа управления Toolbar
определены дополнительные коды извещений:
Код извещения Toolbar |
Описание |
TBN_BEGINADJUST |
Пользователь приступил к настройке Toolbar |
TBN_BEGINDRAG |
Пользователь начал передвигать кнопку
по поверхности Toolbar |
TBN_ENDADJUST |
Пользователь закончил настройку Toolbar |
TBN_ENDDRAG |
Пользователь закончил передвижение
кнопки по поверхности Toolbar |
TBN_CUSTHELP |
Пользователь нажал кнопку "Help" в
диалоговой панели, которая была вызвана для
настройки Toolbar . Позже мы расскажем об этой
диалоговой панели |
TBN_GETBUTTONINFO |
Это извещение окно Toolbar посылает в том
случае, если ему нужна информация об одной из
кнопок |
TBN_QUERYDELETE |
С помощью этого извещения окно Toolbar
запрашивает возможность удаления кнопки в
процессе настройки |
TBN_QUERYINSERT |
С помощью этого извещения окно Toolbar
запрашивает возможность вставки новой кнопки
слева от указанной в процессе настройки |
TBN_RESET |
Пользователь нажал кнопку "Reset" в
диалоговой панели, которая была вызвана для
настройки Toolbar |
TBN_TOOLBARCHANGE |
Пользователь изменил внешний вид Toolbar в
результате выполнения настройки |
Ниже мы привели фрагмент кода из приложения Smart Application, обрабатывающий извещения от органа управления Toolbar :
LRESULT WndProc_OnNotify(HWND hWnd, int idFrom, NMHDR* pnmhdr) { LPTOOLTIPTEXT lpToolTipText; LPTBNOTIFY lptbn; int nItem; static CHAR szBuf[128]; switch(pnmhdr->code) { case TTN_NEEDTEXT: lpToolTipText = (LPTOOLTIPTEXT)pnmhdr; LoadString(hInst, lpToolTipText->hdr.idFrom, szBuf, sizeof(szBuf)); lpToolTipText->lpszText = szBuf; break; case TBN_GETBUTTONINFO: lptbn = (LPTBNOTIFY)pnmhdr; nItem = lptbn->iItem; lptbn->tbButton.iBitmap = tbButtons[nItem].iBitmap; lptbn->tbButton.idCommand = tbButtons[nItem].idCommand; lptbn->tbButton.fsState = tbButtons[nItem].fsState; lptbn->tbButton.fsStyle = tbButtons[nItem].fsStyle; lptbn->tbButton.dwData = tbButtons[nItem].dwData; lptbn->tbButton.iString = tbButtons[nItem].iString; return((nItem < sizeof(tbButtons)/sizeof(tbButtons[0]))? TRUE : FALSE); break; case TBN_QUERYDELETE: lptbn = (LPTBNOTIFY)pnmhdr; nItem = lptbn->iItem; return (nItem == 0)? FALSE : TRUE; break; case TBN_QUERYINSERT: lptbn = (LPTBNOTIFY)pnmhdr; nItem = lptbn->iItem; return (nItem == 0)? FALSE : TRUE; break; case TBN_TOOLBARCHANGE: SendMessage(hwndTb, TB_AUTOSIZE, 0L, 0L); return TRUE; break; default: break; } return FALSE; }
Рассмотрим особенности обработки отдельных извещений.
Если при создании Toolbar был указан стиль TBSTYLE_TOOLTIP , пользователь может получить краткую справку о назначении любой кнопки Toolbar, просто установив на нее курсор мыши и подождав примерно одну секунду. При этом рядом с кнопкой появляется небольшое временное окно с описанием назначения кнопки.
Для того чтобы использовать эту возможность, в файле ресурсов приложения вы должны подготовить таблицу строк, содержащих краткое описание назначения кнопок. Лучше подготовить эту таблицу для всех строк меню, а не только для тех, которые дублируются кнопками Toolbar .
Вот пример такой таблицы (мы привели сокращенный вариант из приложения Smart Application):
STRINGTABLE DISCARDABLE BEGIN ID_FILE_NEW "Creates a new document" ID_FILE_OPEN "Open an existing document" ID_FILE_CLOSE "Closes the active document" ID_FILE_SAVE "Save the active document" ID_FILE_PRINT "Prints the active document" END STRINGTABLE DISCARDABLE BEGIN ID_FILE_EXIT "Exit application" ID_FILE_SAVEAS "Saves the active document under" " a different name" ID_HELP_ABOUT "Displays information about application" END
Как вы увидите в дальнейшем, эта таблица пригодится нам для органа управления Statusbar.
Когда пользователь устанавливает курсор мыши на кнопку Toolbar и оставляет его в покое, окно, создавшее Toolbar, получает извещение TTN_NEEDTEXT.
Обработчик извещения TTN_NEEDTEXT должен извлечь идентификатор кнопки, загрузить из ресурсов приложения соответствующую текстовую строку и записать ее адрес в поле lpszText:
case TTN_NEEDTEXT: lpToolTipText = (LPTOOLTIPTEXT)pnmhdr; LoadString(hInst, lpToolTipText->hdr.idFrom, szBuf, sizeof(szBuf)); lpToolTipText->lpszText = szBuf; break;
Вот и все, что нужно для того чтобы предоставить пользователю возможность определять назначение кнопок Toolbar с помощью органа управления Tool Tip.
Это извещение преследует двоякую цель. Во-первых, с его помощью родительское окно может узнать, что пользователь внес изменения в Toolbar . Во-вторых, при помощи этого извещения функция окна органа управления Toolbar может получить информацию о любой кнопке Toolbar (если, конечно, приложение предоставит такую информацию).
Следующий фрагмент кода возвращает функции окна Toolbar сведения о кнопке, номер которой передается вместе с извещением:
case TBN_GETBUTTONINFO: lptbn = (LPTBNOTIFY)pnmhdr; nItem = lptbn->iItem; lptbn->tbButton.iBitmap = [nItem].iBitmap; lptbn->tbButton.idCommand = tbButtons[nItem].idCommand; lptbn->tbButton.fsState = tbButtons[nItem].fsState; lptbn->tbButton.fsStyle = tbButtons[nItem].fsStyle; lptbn->tbButton.dwData = tbButtons[nItem].dwData; lptbn->tbButton.iString = tbButtons[nItem].iString; return((nItem < sizeof(tbButtons)/sizeof(tbButtons[0]))? TRUE : FALSE); break;
Номер кнопки берется из поля iItem описанной ранее структуры TBNOTIFY, а информация о кнопке - из структуры tbButtons типа TBBUTTON, которая была подготовлена еще до создания Toolbar .
Обратите внимание на то, что обработчик извещения TBN_GETBUTTONINFO возвращает TRUE, если номер кнопки не вышел за пределы возможных значений индекса массива структур tbButtons. В противном случае возвращается значение FALSE.
Когда пользователь сделает двойной щелчок левой клавишей мыши по окну Toolbar для того чтобы вызвать диалоговую панель настройки этого органа управления, функция окна Toolbar будет присылать извещения TBN_GETBUTTONINFO до тех пор, пока соответствующий обработчик не вернет значение FALSE. При этом номер кнопки будет все время возрастать на единицу.
В документации SDK сказано, что обработчик извещения TBN_GETBUTTONINFO должен возвращать TRUE, если он предоставил информацию о нужной кнопке и FALSE в противном случае. Вы, конечно, можете возвращать значение FALSE для любого номера кнопки, не предоставляя никакой информации, однако при этом диалоговая панель настройки Toolbar будет работать неправильно. В частности, с ее помощью вы сможете удалить любую кнопку, но вернуть ее обратно будет уже невозможно.
В процессе настройки органа управления Toolbar пользователь может удалить ненужные ему кнопки. Однако для некоторых кнопок вы можете запретить такую операцию, обеспечив специальную обработку извещения TBN_QUERYDELETE.
Если обработчик извещения вернет значение TRUE, пользователь сможет удалить кнопку, номер которой передается вместе с извещением в поле iItem. Если же обработчик вернет значение FALSE, кнопка останется на своем месте.
Ниже мы привели обработчик извещения TBN_QUERYDELETE, который запрещает удаление кнопки с номером ноль, но разрешает удаление любых других кнопок:
case TBN_QUERYDELETE: lptbn = (LPTBNOTIFY)pnmhdr; nItem = lptbn->; return (nItem == 0)? FALSE : TRUE; break;
Извещение TBN_QUERYINSERT обрабатывается аналогично предыдущему.
Если обработчик этого извещения вернет значение TRUE, пользователь сможет вставить новую кнопку слева от той, номер которой передается в поле iItem. В противном случае вставка новой кнопки в данной позиции будет невозможна.
Извещение TBN_TOOLBARCHANGE посылается родительскому окну, создавшему Toolbar , когда пользователь изменил внешний вид этого органа управления.
В ответ родительское окно обычно посылает органу Toolbar сообщение TB_AUTOSIZE, которое вызывает автоматическое изменение размеров Toolbar:
case TBN_TOOLBARCHANGE: SendMessage(hwndTb, TB_AUTOSIZE, 0L, 0L); return TRUE; break;
Это извещение поступает в родительское окно, когда пользователь приступил к настройке Toolbar . В ответ на него обработчик может вернуть любое значение, так как оно все равно будет проигнорировано.
Когда пользователь закончил настройку Toolbar , родительское окно получает извещение TBN_ENDADJUST. Соответствующий обработчик может вернуть любое значение.
Если пользователь начал передвигать кнопку по поверхности Toolbar , родительское окно получает извещение TBN_BEGINDRAG. Так же как и в предыдущих двух случаях, обработчик извещения может вернуть любое значение.
Когда пользователь закончит передвижение кнопки по поверхности Toolbar , родительское окно получит извещение TBN_ENDDRAG. В ответ оно может вернуть любое значение.
Если родительское окно получило извещение TBN_RESET, это означает, что пользователь нажал кнопку "Reset" в диалоговой панели, которая была вызвана для настройки Toolbar . Обработчик этого извещения (если он предусмотрен), должен восстановить исходный вид органа управления Toolbar и вернуть любое значение.
Посылая органу Toolbar сообщение TB_SAVERESTORE вы можете сохранить в регистрационной базе данных и восстановить состояние Toolbar.
Извещение TBN_CUSTHELP передается родительскому окну, когда пользователь нажал кнопку "Help" в диалоговой панели, которая была вызвана для настройки Toolbar .
Обработчик этого извещения должен отобразить на экране соответствующий раздел справочной системы и вернуть любое значение.
Посылая сообщения органу Toolbar , приложение может управлять кнопками, получать информацию о состоянии кнопок, вызывать на экран диалоговую панель для настройки Toolbar и выполнять множество других действий. Ниже мы приведем краткое описание сообщений, предназначенных для Toolbar. Полную информацию вы сможете найти в справочной системе SDK для Win32.
Добавление одного или нескольких изображений к списку изображений органа управления Toolbar .
Параметры сообщения:
wParam = (WPARAM) nButtons; // количество добавляемых // изображений lParam = (LPARAM) (LPTBADDBITMAP) lptbab; // указатель на // структуру TBADDBITMAP
Как мы уже говорили, для Toolbar вы должны подготовить изображение, содержащее несколько картинок, которые будут нарисованы на кнопках. Если вы создаете Toolbar функцией CreateWindowEx, для добавления изображений необходимо послать созданному окну сообщение TB_ADDBITMAP:
tbab.hInst = hInst; tbab.nID = IDB_TBBITMAP; SendMessage(hwndTb, TB_ADDBITMAP, (WPARAM)8, (LPARAM)&tbab);
Добавление кнопок к органу управления Toolbar .
wParam = (WPARAM) (UINT) uNumButtons; // количество кнопок lParam = (LPARAM) (LPTBBUTTON) lpButtons; // указатель на // заполненный массив структур TBBUTTON
Это сообщение используется в процессе создания Toolbar , так же как и предыдущее:
SendMessage(hwndTb, TB_ADDBUTTONS, (WPARAM)11, (LPARAM)&tbButtons);
Количество кнопок задается с учетом промежутков между группами кнопок. Каждый промежуток при этом считается за одну кнопку, хотя вы можете создавать промежутки размером в несколько кнопок (например, чтобы разместить там другие органы управления).
Добавление новых текстовых строк в список органа управления Toolbar .
wParam = (WPARAM) (HINSTANCE) hinst; // идентификатор модуля lParam = (LPARAM) MAKELONG(idString, 0); // идентификатор // строки
Вы можете добавлять текстовые строки не только из ресурсов приложения, но и из массива в оперативной памяти. В этом случае параметр hinst должен быть равен нулю, а через параметр lParam необходимо передать адрес массива строк. Каждая строка в массиве должна заканчиваться двоичным нулем, а последняя - двумя нулями.
Параметры этого сообщения должны быть равны нулю. Оно посылается органу управления Toolbar сразу после создания, а также при изменении количества или расположения кнопок.
После посылки этого сообщения органу управления Toolbar функция SendMessage возвращает количество кнопок, добавленных в него с момента создания. Параметры сообщения должны быть равны нулю.
С помощью этого сообщения приложение, создавшее орган управления Toolbar , должно сообщить последнему размер структуры TBBUTTON.
wParam = (WPARAM) cb; // размер структуры TBBUTTON
Значение параметра lParam должно быть равно нулю.
Сообщение TB_BUTTONSTRUCTSIZE следует послать органу Toolbar сразу после его создания функцией CreateWindowEx:
SendMessage(hwndTb, TB_BUTTONSTRUCTSIZE, (WPARAM) sizeof(TBBUTTON), 0);
Замена изображения на поверхности кнопки.
wParam = (WPARAM) idButton; // идентификатор кнопки lParam = (LPARAM) MAKELPARAM(iBitmap, 0); // номер нового // изображения
С помощью этого сообщения можно переключать кнопку из отжатого состояния в нажатое и обратно.
wParam = (WPARAM) idButton; // идентификатор кнопки lParam = (LPARAM) MAKELONG(fCheck, 0); // флаг
Если значение флага равно TRUE, кнопка переводится в нажатое состояние, если FALSE - в отжатое.
Определение номера кнопки по ее идентификатору (нумерация кнопок начинается с нуля).
wParam = (WPARAM) idButton; // идентификатор кнопки
Значение параметра lParam должно быть равно нулю.
Номер кнопки возвращается функцией SendMessage после посылки с ее помощью сообщения TB_COMMANDTOINDEX.
Если окну Toolbar послать сообщение TB_CUSTOMIZE и если при создании Toolbar был указан стиль CCS_ADJUSTABLE, на экране появляется диалоговая панель настройки. С помощью этой панели (которая будет описана ниже) пользователь может изменять внешний вид органа управления Toolbar.
Параметры сообщения должны быть равны нулю.
С помощью этого сообщения приложение может удалить кнопку из окна Toolbar . Порядковый номер удаляемой кнопки передается через параметр wParam:
wParam = (WPARAM) iButton;
Параметр lParam должен быть равен нулю.
Отметим, что вы можете определить порядковый номер кнопки по ее идентификатору с помощью сообщения TB_COMMANDTOINDEX, описанного выше.
С помощью сообщения TB_ENABLEBUTTON приложение может заблокировать или разблокировать кнопку.
wParam = (WPARAM) idButton; // идентификатор кнопки lParam = (LPARAM) MAKELONG(fEnable, 0); // флаг блокировки
Если флаг равен TRUE, кнопка разблокируется, если FALSE - блокируется.
Определение номера изображения по идентификатору кнопки.
wParam = (WPARAM) idButton; // идентификатор кнопки
Параметр lParam должен быть равен нулю.
Порядковый номер изображения возвращается функцией SendMessage.
С помощью этого сообщения можно определить способность монитора отображать картинки большого размера. Это возможно в том случае, если в одном логическом дюйме контекста отображения экрана помещается не менее 120 пикселов.
Если монитор может отображать большие картинки, в возвращаемом функцией SendMessage значении установлен флаг TBBF_LARGE.
Получение информации о кнопке, заданной своим номером.
wParam = (WPARAM) iButton; // номер кнопки lParam = (LPARAM) (LPTBBUTTON) lpButton; // адрес структуры // TBBUTTON
Получение текстовой строки, связанной с кнопкой, которая задана своим номером.
wParam = (WPARAM) idButton; // номер кнопки lParam = (LPARAM) (LPSTR) lpszText; // адрес буфера для строки
Определение координат области, занимаемой кнопкой, которая задана своим номером.
wParam = (WPARAM) iButton; // номер кнопки lParam = (LPARAM) (LPRECT) lprc; // адрес структуры RECT
Определение количества строк в органе управления Toolbar , имеющим стиль TBSTYLE_WRAPABLE.
Параметры сообщения должны быть равны нулю.
Определение состояния кнопки, заданной своим идентификатором.
wParam = (WPARAM) idButton; // идентификатор кнопки
Приведем список возможных состояний кнопки.
Состояние |
Описание |
TBSTATE_CHECKED |
Кнопка имеет стиль BSTYLE_CHECKED и нажата |
TBSTATE_ENABLED |
Кнопка разблокирована и может быть
нажата пользователем |
TBSTATE_HIDDEN |
Кнопка скрыта |
TBSTATE_INDETERMINATE |
Кнопка находится в неопределенном
состоянии и отображается серым цветом |
TBSTATE_PRESSED |
Кнопка нажата |
TBSTATE_WRAP |
После этой кнопки начинается новая
строка органа управления Toolbar |
Определение идентификатора органа управления Tool Tip, связанного с данным органом управления Toolbar .
Параметры сообщения должны быть равны нулю.
С помощью этого сообщения можно отобразить или скрыть кнопку, заданную своим идентификатором.
wParam = (WPARAM) idButton; // идентификатор кнопки lParam = (LPARAM) MAKELONG(fShow, 0); // флаг
Если флаг равен TRUE, кнопка становится скрытой, если FALSE - отображается.
С помощью этого сообщения можно установить или отменить для кнопки неопределенное состояние, когда кнопка отображается серым цветом.
wParam = (WPARAM) idButton; // идентификатор кнопки lParam = (LPARAM) MAKELONG(fIndeterminate, 0); // флаг
Если флаг равен TRUE, неопределенное состояние устанавливается, если FALSE - отменяется.
Вставка кнопки в Toolbar .
wParam = (WPARAM) iButton; // номер кнопки lParam = (LPARAM)(LPTBBUTTON)lpButton; // указатель на // структуру TBBUTTON
Если кнопка была вставлена успешно, функция SendMessage вернет значение TRUE, в противном случае - FALSE.
С помощью этого сообщения приложение может проверить состояние кнопки - нажата кнопка или нет.
wParam = (WPARAM) idButton; // идентификатор кнопки
Значение параметра lParam должно быть равно нулю.
Если кнопка нажата, функция SendMessage вернет значение TRUE, в противном случае - FALSE.
С помощью этого сообщения приложение может проверить состояние блокировки кнопки - заблокирована кнопка или нет.
wParam = (WPARAM) idButton; // идентификатор кнопки
Значение параметра lParam должно быть равно нулю.
Если кнопка разблокирована, функция SendMessage вернет значение TRUE, в противном случае - FALSE.
С помощью этого сообщения приложение может проверить, скрыта кнопка или нет.
wParam = (WPARAM) idButton; // идентификатор кнопки
Значение параметра lParam должно быть равно нулю.
Если кнопка скрыта, функция SendMessage вернет значение TRUE, в противном случае - FALSE.
С помощью этого сообщения приложение может проверить, находится ли кнопка в неопределенном состоянии, когда она отображается серым цветом.
wParam = (WPARAM) idButton; // идентификатор кнопки
Значение параметра lParam должно быть равно нулю.
Если кнопка находится в неопределенном состоянии, функция SendMessage вернет значение TRUE, в противном случае - FALSE.
С помощью этого сообщения приложение может проверить, находится ли кнопка в нажатом состоянии.
wParam = (WPARAM) idButton; // идентификатор кнопки
Значение параметра lParam должно быть равно нулю.
Если кнопка находится в нажатом состоянии, функция SendMessage вернет значение TRUE, в противном случае - FALSE.
С помощью этого сообщения приложение может установить кнопку в нажатое или отжатое состояние.
wParam = (WPARAM) idButton; // идентификатор кнопки lParam = (LPARAM) MAKELONG(fPress, 0); // флаг
Для того чтобы перевести кнопку в нажатое состояние, необходимо установить значение флага fPress равным TRUE, а для того чтобы перевести кнопку в отжатое состояние - равным FALSE.
Если состояние кнопки было изменено успешно, функция SendMessage вернет значение TRUE, в противном случае - FALSE.
Посылая органу управления Toolbar сообщение TB_SAVERESTORE, приложение может сохранить или восстановить состояние Toolbar. Для хранения состояния Toolbar используется системная регистрационная база данных.
wParam = (WPARAM) (BOOL) fSave; // флаг lParam = (LPARAM) (TBSAVEPARAMS *)ptbsp; // указатель на // структуру TBSAVEPARAMS
Если значение флага fSave равно TRUE, будет выполнено сохранение состояния Toolbar , а если FALSE - восстановление.
Через параметр ptbsp передается указатель на структуру типа TBSAVEPARAMS , приведенную ниже:
typedef struct { HKEY hkr; // идентификатор ключа регистрации LPCTSTR pszSubKey; // имя ключа LPCTSTR pszValueName; // значение ключа } TBSAVEPARAMS;
Работа с системной регистрационной базой данных Microsoft Windows 95 будет описана в одном из следующих томов "Библиотеки системного программиста".
С помощью сообщения TB_SETBITMAPSIZE приложение может установить размер картинки, изображенной на поверхности кнопки.
lParam = (LPARAM) MAKELONG(dxBitmap, dyBitmap); // размеры
Значение параметра wParam должно быть равно нулю.
Через параметры dxBitmap и dyBitmap передается, соответственно, ширина и высота изображения.
С помощью сообщения TB_SETBUTTONSIZE приложение может установить размер кнопки.
Param = (LPARAM) MAKELONG(dxButton, dyButton); // размеры
Значение параметра wParam должно быть равно нулю.
Через параметры dxButton и dyButton передается, соответственно, ширина и высота кнопки.
С помощью этого сообщения можно присвоить кнопке с заданным номером командный идентификатор.
wParam = (WPARAM) (UINT) index; // номер кнопки lParam = (WPARAM) (UINT) cmdId; // идентификатор
С помощью сообщения TB_SETPARENT приложение может назначить для органа управления Toolbar родительское окно.
wParam = (WPARAM) (HWND) hwndParent; // идентификатор // родительского окна
Значение параметра lParam должно быть равно нулю.
Установка количества строк кнопок в органе управления Toolbar .
wParam = (WPARAM) MAKEWPARAM(cRows, fLarger); lParam = (LPARAM) (LPRECT) lprc;
Через параметр cRows передается количество строк. Минимальное значение параметра равно одной строке, максимальное - количеству кнопок в окне Toolbar .
Если параметр fLarger равен TRUE, при недостатке места для размещения всех кнопок в cRows строках будет создана дополнительная строка. Если же параметр fLarger равен FALSE, дополнительная строка не создается.
Перед тем как послать окну Toolbar сообщение TB_SETROWS, вы должны подготовить структуру типа RECT. Ее адрес нужно передать через параметр lprc. После обработки сообщения в эту структуру будут записаны новые размеры окна Toolbar.
Установка кнопки с заданным идентификатором в новое состояние.
wParam = (WPARAM) idButton; // идентификатор кнопки lParam = (LPARAM) MAKELONG(fState, 0); // состояние кнопки
Список возможных состояний кнопки приведен выше в описании сообщения TB_GETSTATE.
С помощью сообщения TB_SETTOOLTIPS приложение может назначить для Toolbar орган управления Tool Tip.
wParam = (WPARAM)(HWND)hwndToolTip; // идентификатор Tool Tip
Значение параметра lParam должно быть равно нулю.
Заметим, что при создании Toolbar со стилем TBSTYLE_TOOLTIPS для него автоматически создается орган Tool Tip , поэтому вам не нужно посылать окну Toolbar дополнительное сообщение TB_SETTOOLTIPS. Однако если вы посылаете такое сообщение, в Tool Tip будут зарегистрированы только те кнопки, которые были добавлены в Toolbar до момента посылки сообщения TB_SETTOOLTIPS.
Процедура создания органа управления Statusbar намного проще процедуры создания Toolbar . Вы можете создать Statusbar либо с помощью функции CreateWindowEx на основе класса окна STATUSCLASSNAME , либо воспользоваться специально предназначенной для этого функцией CreateStatusWindow.
В первом случае вы можете создать Statusbar, например, так:
hwndSb = CreateWindowEx( 0L, // расширенный стиль окна STATUSCLASSNAME, // класс окна для Statusbar "", // заголовок окна отсутствует WS_CHILD | WS_BORDER | // стиль окна WS_VISIBLE | SBARS_SIZEGRIP, 0, 0, 0, 0, // координаты, ширина, высота hWnd, // идентификатор родительского окна (HMENU)IDS_STATUSBAR, // идентификатор Statusbar hInst, // идентификатор приложения NULL ); // доп. данные для окна
Обратите внимание на стиль SBARS_SIZEGRIP . Если Statusbar имеет этот стиль, его правый нижний угол принимает вид, показанный на рис. 2.2.
Рис. 2.2. Орган управления Toolbar , имеющий стиль SBARS_SIZEGRIP
Во втором случае вы должны вызвать функцию CreateStatusWindow , прототип которой приведен ниже:
HWND CreateStatusWindow( LONG style, // стиль окна LPCTSTR lpszText, // текст для первой области Statusbar HWND hwndParent, // идентификатор родительского окна UINT wID // идентификатор Statusbar );
Обычно создание органов управления Statusbar и Toolbar выполняется приложением при обработке функцией главного окна сообщения WM_CREATE.
При создании Statusbar вы можете также использовать стиль CCS_TOP . При этом окно Statusbar будет расположено в верхней части родительского окна. Очевидно, этот стиль нельзя комбинировать со стилем SBARS_SIZEGRIP , так как во-первых, такой орган управления будет выглядеть очень странно, а во-вторых, он не будет работать так, как этого ожидает пользователь.
Орган управления Statusbar может работать в двух режимах - стандартном и упрощенном.
В первом режиме приложение может разделить окно Statusbar на несколько областей для раздельного вывода в эти области текста или графической информации.
В упрощенном режиме приложение может выводить в окно Statusbar только текстовую информацию, причем разделение этого окна на несколько областей невозможно.
Орган управления Statusbar переводится из стандартного режима в упрощенный и обратно при помощи сообщения SB_SIMPLE , которое мы рассмотрим немного позже. В процессе переключения содержимое области стандартного режима не исчезает, так как соответствующие данных хранятся в разных местах для разных режимов.
Для того чтобы разделить Statusbar на несколько областей достаточно сразу после создания послать ему сообщение SB_SETPARTS :
SendMessage(hwndSb, SB_SETPARTS, 3, (LPARAM)ptWidth);
Через параметр wParam передается количество областей (в нашем примере создается три области).
Параметр lParam должен содержать адрес массива ширин областей Toolbar :
int ptWidth[3]; // таблица ширин для Statusbar
Количество элементов в массиве должно быть равно количеству областей, на которые делится Statusbar.
Однако это еще не все.
При обработке сообщения WM_SIZE вы должны выполнить повторное определение областей:
void WndProc_OnSize(HWND hwnd, UINT state, int cx, int cy) { SendMessage(hwndTb, WM_SIZE, cx, cy); SendMessage(hwndSb, WM_SIZE, cx, cy); ptWidth[0] = cx/2; ptWidth[1] = cx/2 + cx/4; ptWidth[2] = -1; SendMessage(hwndSb, SB_SETPARTS, 3, (LPARAM)ptWidth); return FORWARD_WM_SIZE(hwnd, state, cx, cy, DefWindowProc); }
Когда обработчик сообщения WM_SIZE передает сообщение WM_SIZE органам управления Toolbar и Statusbar, они устанавливают размеры своих окон.
Затем необходимо подготовить массив ширин областей Statusbar.
В элемент массива вы можете записать либо позицию правой границы области от левого края окна Statusbar, либо значение -1. В последнем случае правая граница области простирается до правого края окна Statusbar.
Для записи текста в область Statusbar необходимо использовать сообщение SB_SETTEXT :
SendMessage(hwndSb,SB_SETTEXT,0, (LPARAM)"Statusbar, Part 1"); SendMessage(hwndSb, SB_SETTEXT,1 | SBT_NOBORDERS, (LPARAM)"");
Через параметр wParam передается номер области,
который может быть скомбинирован при помощи
логической операции ИЛИ с одной из констант,
определяющих внешний вид области:
Константа |
Описание |
SBT_NOBORDERS |
Текст рисуется без рамки |
SBT_POPOUT |
Текст рисуется с рамкой, которая
выглядит приподнятой над поверхностью окна Statusbar
|
SBT_RTLREADING |
Текст отображается справа налево (такой
способ отображения используется, например, в
арабских странах) |
SBT_OWNERDRAW |
Текст (или графическое изображение)
рисуется родительским окном во время обработки
сообщения WM_DRAWITEM |
Через параметр lParam передается указатель на строку текста, которая должна быть записана в область.
Текстовая строка может быть пустой. Если необходимо выполнить выравнивание текста по центру или по правому краю, вы можете включить в текстовую строку символы табуляции \t. Текст, расположенный после первого символа табуляции, выравнивается по центру, после второго - по правому краю области.
Если в сообщении SB_SETTEXT указать константу SBT_OWNERDRAW , родительское окно, создавшее Statusbar, может нарисовать в соответствующей области любое изображение или написать текст любым шрифтом, используя всю мощь графического интерфейса GDI.
Ниже мы привели фрагмент кода, который загружает из ресурсов приложения битовое изображение и передает его идентификатор через параметр lParam сообщения SB_SETTEXT . При этом номер области, в которой будет нарисовано это изображения, комбинируется с константой SBT_OWNERDRAW :
hSbLogoBmp = LoadBitmap(hInst, MAKEINTRESOURCE(IDB_SBLOGO)); SendMessage(hwndSb, SB_SETTEXT, 2 | SBT_OWNERDRAW, (LPARAM)hSbLogoBmp);
Когда область Statusbar должны быть перерисована, родительское окно получает сообщение WM_DRAWITEM . Параметр wParam этого сообщения содержит идентификатор органа управления, которое послало сообщение, или ноль, если сообщение пришло от меню. В нашем случае через wParam будет передаваться идентификатор органа управления Statusbar.
Параметр lParam содержит указатель на структуру типа DRAWITEMSTRUCT (описание полей приведено для Statusbar):
typedef struct tagDRAWITEMSTRUCT { UINT CtlType; // не используется UINT CtlID; // идентификатор окна Statusbar UINT itemID; // номер области, которая будет перерисована UINT itemAction; // не используется UINT itemState; // не используется HWND hwndItem; // идентификатор окна Statusbar HDC hDC; // идентификатор контекста отобаржения RECT rcItem; // координаты области DWORD itemData; // значение lParam сообщения SB_SETTEXT } DRAWITEMSTRUCT;
Обработчик сообщения WM_DRAWITEM должен проверить содержимое поля CtlID - в этом поле должен находится идентификатор Statusbar. Далее обработчик может выполнить рисование, пользуясь контекстом отображения hDC в области, координаты которой передаются через поле rcItem.
При этом обработчик сообщения WM_DRAWITEM может воспользоваться содержимым поля itemData, которое равно значению параметра lParam сообщения SB_SETTEXT. Через этот параметр можно передать, например, идентификатор изображения, которое должно быть нарисовано в области Statusbar, идентификатор шрифта, которым должен быть написан текст в этой области или любая другая информация.
Ниже мы привели фрагмент исходного текста приложения Smart Application, который рисует в самой правой области Statusbar небольшое графическое изображение:
void WndProc_OnDrawItem(HWND hwnd, const DRAWITEMSTRUCT * lpDrawItem) { if(lpDrawItem->CtlID == IDS_STATUSBAR) { LPDRAWITEMSTRUCT lpDis; HDC hdcMem; HBITMAP hbmOld; BITMAP bm; lpDis = (LPDRAWITEMSTRUCT)lpDrawItem; hdcMem = CreateCompatibleDC(lpDis->hDC); hbmOld = SelectObject(hdcMem, hSbLogoBmp); GetObject(hSbLogoBmp, sizeof(bm), &bm); BitBlt(lpDis->hDC, lpDis->rcItem.left, lpDis->rcItem.top, bm.bmWidth, bm.bmHeight, hdcMem, 0, 0, SRCCOPY); SelectObject(hdcMem, hbmOld); DeleteDC(hdcMem); } return FORWARD_WM_DRAWITEM(hwnd, lpDrawItem, DefWindowProc); }
Если вы незнакомы с понятиями контекста отображения, не умеете работать с битовыми графическими изображениями или никогда не пользовались функцией BitBlt, мы рекомендуем вам обратиться к 14 тому "Библиотеки системного программиста", который называется "Графический интерфейс GDI в Microsoft Windows".
Если вам нужно нарисовать непрямоугольное или частично прозрачное изображение, недостаточно выбрать в качестве цвета фона цвет окна Statusbar, так как пользователь может легко изменить цветовую гамму оболочки Microsoft Windows 95. В этом случае мы рекомендуем вам использовать более сложные процедуры рисования с использованием растровых операций ROP . Всю необходимую для этого информацию, а также пример приложения (которое называется BMPLOGO) вы найдете в 14 томе "Библиотеки системного программиста".
В этом разделе мы перечислим и кратко опишем сообщения, специально предназначенные для работы с органом управления Statusbar. Полную информацию вы сможете найти в справочной системе SDK.
Установка минимальной высоты области окна Statusbar, которая используется для отображения текста или рисования изображений.
wParam = (WPARAM) minHeight; // минимальная высота в пикселах
Значение параметра lParam должно быть равно нулю.
Запись текста в заданную область окна Statusbar.
wParam = (WPARAM) iPart | uType; // область и тип отображения lParam = (LPARAM) (LPSTR) szText; // адрес строки
Через параметр iPart передается номер области. Этот номер может комбинироваться при помощи логической операции ИЛИ с одной из нескольких констант, описанной нами ранее в разделе " Запись текста в область Statusbar".
Через параметр szText передается адрес строки текста. Если используется константа SBT_OWNERDRAW, параметр сообщения lParam служит для передачи произвольного 32-разрядного значения, например, идентификатора битового изображения.
Установка количества областей и определение правой границы каждой области.
wParam = (WPARAM) nParts; // количество областей lParam = (LPARAM) (LPINT) aWidths; // границы
Это сообщение мы подробно описали в разделе " Разделение Statusbar на области".
Переключение органа Statusbar из стандартного режима в упрощенный и обратно.
wParam = (WPARAM) (BOOL); // флаг режима
Параметр lParam должен быть равен нулю.
Если флаг fSimple принимает значение TRUE, Statusbar переключается в упрощенный режим. если FALSE - возвращается в стандартный режим.
Отметим, что в упрощенном режиме родительское окно не может рисовать в единственной области окна Statusbar. Поэтому упрощенный режим пригоден только для отображения текстовых сообщений.
Определение ширины горизонтальной и вертикальной рамки окна Statusbar.
lParam = (LPARAM) (LPINT) aBorders; // адрес массива
Параметр wParam должен быть равен нулю.
Перед тем как послать окну Statusbar сообщение SB_GETBORDERS, вы должны подготовить массив из трех переменных типа int. В первый элемент массива будет записана ширина горизонтальной рамки, во второй - ширина вертикальной рамки, и в третий - ширина рамки между прямоугольниками.
Определение количества областей в окне состояния и координат правых границ этих областей.
wParam = (WPARAM) nParts; // количество областей lParam = (LPARAM) (LPINT) aRightCoord; // массив координат
Через параметр nParts передается количество областей, информация о координатах правых границ которых записывается в массив aRightCoord.
Функция SendMessage, пославшая сообщение SB_GETPARTS, возвращает количество существующих в окне Statusbar областей или нуль при ошибке.
Определение границ заданной области в окне Statusbar.
wParam = (WPARAM) iPart; // номер области lParam = (LPARAM) (LPRECT) lprc; // границы области
Границы области, номер которой задан через параметр iPart, передается приложению через структуру типа RECT. Адрес этой структуры должен быть указан в параметре lprc перед посылкой сообщения.
Извлечение текста из заданной области Statusbar.
wParam = (WPARAM) iPart; // номер области lParam = (LPARAM) (LPSTR) szText; // адрес буфера для текста
Функция SendMesage возвращает в младшем 16-разрядном слове длину текста, в старшем, константу 0, SBT_NOBORDERS, SBT_POPOUT или SBT_RTLREADING. Если родительское окно само рисует внутри области при обработке сообщения WM_DRAWITEM, возвращается 32-разрядное значение, которое было передано при посылке сообщения SB_SETTEXT.
Перед тем как извлекать текст из области Statusbar, необходимо определить размер буфера для записи соответствующей текстовой строки. Это можно сделать при помощи сообщения SB_GETTEXTLENGTH, описанного ниже.
Определение длины строки для заданной области окна Statusbar.
wParam = (WPARAM) iPart; // номер области
Значение параметра lParam должно быть равно нулю.
Функция SendMesage возвращает точно такое же значение, как и для сообщения SB_GETTEXT.
Теперь настало время проверить работу органов управления Toolbar и Statusbar на практике. Для этого мы предлагаем вам ознакомится с исходными текстами приложения Smart Application, внешний вид главного окна которого показан на рис. 2.3.
Рис. 2.3. Главное окно приложения Smart Application
В этом приложении есть меню, орган управления Toolbar с функциями Tool Tip, кнопки которого дублируют некоторые строки меню, а также окно Statusbar, разделенное на три области. Первая область используется для отображения текстового описания строк меню, вторая не используется, а в третьей мы нарисовали маленькие часы. В качестве упражнения вы можете попытаться их запустить.
Если сделать двойной щелчок левой клавишей мыши по окну Toolbar , на экране появится диалоговая панель настройки Customize Toolbar, показанная на рис. 2.4.
Рис. 2.4. Диалоговая панель настройки Customize Toolbar
С помощью этой диалоговой панели вы можете перемещать кнопки из списка Toolbar buttons в список Available buttons и обратно. Первый список содержит все кнопки, отображаемые в настоящий момент в окне Toolbar, второй при первом отображении диалоговой панели содержит только разделитель Separator.
Выделяя кнопки из списка Toolbar buttons и нажимая кнопку Add, вы можете их скрыть. Скрытые кнопки появятся в списке Available buttons. Впоследствии вы сможете их вставить на прежнее или другое место окна Toolbar.
Кнопки Move Up и Move Down предназначены для перемещения кнопок по окну Toolbar . Впрочем, такое перемещение можно выполнить левой клавишей мыши, нажав клавишу <Shift> и установив курсор мыши на нужную кнопку.
Кнопки Help и Reset никак не задействованы в нашем приложении, однако вы и сами легко сможете найти для них применение. Достаточно предусмотреть обработку извещений TBN_CUSTHELP и TBN_RESET , описанных нами ранее.
Заметим, что диалоговая панель Customize Toolbar не определена в ресурсах приложения Smart Application, а встроена в орган управления Toolbar. Так что мы пользуемся готовым средством настройки Toolbar, а не создаем свое.
Основной файл исходных текстов приложения Smart Application представлен в листинге 2.1.
Листинг 2.1. Файл smart\smart.c
#define STRICT #include <windows.h> #include <windowsx.h> #include <commctrl.h> #include "resource.h" #include "afxres.h" #include "smart.h" // ----------------------------------------------------- // Глобальные переменные // ----------------------------------------------------- HINSTANCE hInst; char szAppName[] = "Smart"; char szAppTitle[] = "Smart Application"; HWND hwndTb; // идентификатор Toolbar HWND hwndSb; // идентификатор Statusbar int ptWidth[3]; // таблица ширин для Statusbar HBITMAP hSbLogoBmp; // изображение для Statusbar // Идентификаторы строк описания временных меню UINT nIdPopup[] = { IDS_FILEMENU, IDS_EDITMENU, IDS_HELPMENU }; // Описание кнопок Toolbar TBBUTTON tbButtons[] = { { 0, ID_FILE_NEW, TBSTATE_ENABLED, TBSTYLE_BUTTON, 0L, 0}, { 1, ID_FILE_OPEN, TBSTATE_ENABLED, TBSTYLE_BUTTON, 0L, 0}, { 2, ID_FILE_SAVE, TBSTATE_ENABLED, TBSTYLE_BUTTON, 0L, 0}, { 0, 0, TBSTATE_ENABLED, TBSTYLE_SEP, 0L, 0}, { 3, ID_EDIT_CUT, TBSTATE_ENABLED, TBSTYLE_BUTTON, 0L, 0}, { 4, ID_EDIT_COPY, TBSTATE_ENABLED, TBSTYLE_BUTTON, 0L, 0}, { 5, ID_EDIT_PASTE, TBSTATE_ENABLED, TBSTYLE_BUTTON, 0L, 0}, { 0, 0, TBSTATE_ENABLED, TBSTYLE_SEP, 0L, 0}, { 6, ID_FILE_PRINT, TBSTATE_ENABLED, TBSTYLE_BUTTON, 0L, 0}, { 0, 0, TBSTATE_ENABLED, TBSTYLE_SEP, 0L, 0}, { 7, ID_HELP_ABOUT, TBSTATE_ENABLED, TBSTYLE_BUTTON, 0L, 0} }; // ----------------------------------------------------- // Функция 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 = CS_HREDRAW | CS_VREDRAW; 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; // Инициализируем библиотеку стандартных органов управления InitCommonControls(); // Создаем главное окно приложения 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_NOTIFY, WndProc_OnNotify); HANDLE_MSG(hWnd, WM_SIZE, WndProc_OnSize); HANDLE_MSG(hWnd, WM_DRAWITEM, WndProc_OnDrawItem); HANDLE_MSG(hWnd, WM_MENUSELECT, WndProc_OnMenuSelect); default: return(DefWindowProc(hWnd, msg, wParam, lParam)); } } // ----------------------------------------------------- // Функция WndProc_OnCreate // ----------------------------------------------------- BOOL WndProc_OnCreate(HWND hWnd, LPCREATESTRUCT lpCreateStruct) { // Создаем Toolbar hwndTb = CreateToolbarEx(hWnd, WS_CHILD | WS_BORDER | WS_VISIBLE | TBSTYLE_TOOLTIPS | CCS_ADJUSTABLE, IDT_TOOLBAR, // идентификатор органа Toolbar 8, // количество пиктограмм hInst, // идентификатор приложения IDB_TBBITMAP,// идентификатор битового изображения // кнопок (LPCTBBUTTON)&tbButtons,// адрес описания кнопок 11, // количество кнопок 16,16, // ширина и высота кнопок 16,16, // ширина и высота пиктограмм sizeof(TBBUTTON)); // размер структуры в байтах if(hwndTb == NULL) return FALSE; // Создаем Statusbar hwndSb = CreateWindowEx( 0L, // расширенный стиль окна STATUSCLASSNAME, // класс окна для Statusbar "", // заголовок окна отсутствует WS_CHILD | WS_BORDER | // стиль окна WS_VISIBLE | SBARS_SIZEGRIP, 0, 0, 0, 0, // координаты, ширина, высота hWnd, // идентификатор родительского окна (HMENU)IDS_STATUSBAR, // идентификатор Statusbar hInst, // идентификатор приложения NULL ); // доп. данные для окна if(hwndSb == NULL) return FALSE; // Делим Statusbar на 3 области SendMessage(hwndSb, SB_SETPARTS, 3, (LPARAM)ptWidth); // Инициализируем области Statusbar SendMessage(hwndSb, SB_SETTEXT, 0, (LPARAM)""); SendMessage(hwndSb, SB_SETTEXT, 1 | SBT_NOBORDERS, (LPARAM)""); // Загружаем изображение, которое будет нарисовано // в третьей части Statusbar hSbLogoBmp = LoadBitmap(hInst, MAKEINTRESOURCE(IDB_SBLOGO)); if(hSbLogoBmp== NULL) return FALSE; // Рисуем это изображение SendMessage(hwndSb, SB_SETTEXT, 2 | SBT_OWNERDRAW, (LPARAM)hSbLogoBmp); return TRUE; } // ----------------------------------------------------- // Функция WndProc_OnDestroy // ----------------------------------------------------- #pragma warning(disable: 4098) void WndProc_OnDestroy(HWND hWnd) { PostQuitMessage(0); return FORWARD_WM_DESTROY(hWnd, DefWindowProc); } // ----------------------------------------------------- // Функция WndProc_OnCommand // ----------------------------------------------------- #pragma warning(disable: 4098) void WndProc_OnCommand(HWND hWnd, int id, HWND hwndCtl, UINT codeNotify) { switch (id) { case ID_FILE_EXIT: PostQuitMessage(0); break; case ID_FILE_NEW: case ID_FILE_OPEN: case ID_FILE_SAVE: case ID_FILE_SAVEAS: case ID_FILE_PRINT: case ID_EDIT_CUT: case ID_EDIT_COPY: case ID_EDIT_PASTE: case ID_HELP_ABOUT: default: MessageBox(NULL,"Command stub","Smart Application",MB_OK); } return FORWARD_WM_COMMAND(hWnd, id, hwndCtl, codeNotify, DefWindowProc); } // ----------------------------------------------------- // Функция WndProc_OnNotify // ----------------------------------------------------- LRESULT WndProc_OnNotify(HWND hWnd, int idFrom, NMHDR* pnmhdr) { LPTOOLTIPTEXT lpToolTipText; LPTBNOTIFY lptbn; int nItem; static CHAR szBuf[128]; switch(pnmhdr->code) { // Если получили сообщение от ToolTips, загружаем из // ресурсов соответствующую текстовую строку case TTN_NEEDTEXT: lpToolTipText = (LPTOOLTIPTEXT)pnmhdr; LoadString(hInst, lpToolTipText->hdr.idFrom, szBuf, sizeof(szBuf)); lpToolTipText->lpszText = szBuf; break; // Возвращаем окну Toolbar характеристики кнопки, // с номером, заданным в lptbn->iItem case TBN_GETBUTTONINFO: lptbn = (LPTBNOTIFY)pnmhdr; nItem = lptbn->iItem; lptbn->tbButton.iBitmap = tbButtons[nItem].iBitmap; lptbn->tbButton.idCommand = tbButtons[nItem].idCommand; lptbn->tbButton.fsState = tbButtons[nItem].fsState; lptbn->tbButton.fsStyle = tbButtons[nItem].fsStyle; lptbn->tbButton.dwData = tbButtons[nItem].dwData; lptbn->tbButton.iString = tbButtons[nItem].iString; // Если запрашиваются характеристики несуществующей // кнопки, возвращаем FALSE return((nItem < sizeof(tbButtons)/sizeof(tbButtons[0]))? TRUE : FALSE); break; // Разрешаем удаление любой кнопки, кроме самой первой case TBN_QUERYDELETE: lptbn = (LPTBNOTIFY)pnmhdr; nItem = lptbn->iItem; return (nItem == 0)? FALSE : TRUE; break; // Разрешаем вставку любой кнопки, кроме самой первой case TBN_QUERYINSERT: lptbn = (LPTBNOTIFY)pnmhdr; nItem = lptbn->iItem; return (nItem == 0)? FALSE : TRUE; break; // В ответ на завершение операции перемещения // перерисовываем Toolbar case TBN_TOOLBARCHANGE: SendMessage(hwndTb, TB_AUTOSIZE, 0L, 0L); return TRUE; break; default: break; } return FALSE; } // ----------------------------------------------------- // Функция WndProc_OnSize // ----------------------------------------------------- #pragma warning(disable: 4098) void WndProc_OnSize(HWND hwnd, UINT state, int cx, int cy) { // Изменяем размеры Toolbar и Statusbar в соответствии с // новыми размерами окна SendMessage(hwndTb, WM_SIZE, cx, cy); SendMessage(hwndSb, WM_SIZE, cx, cy); // Рассчитываем размеры областей Statusbar ptWidth[0] = cx/2; ptWidth[1] = cx/2 + cx/4; ptWidth[2] = -1; // Устанавливаем новые размеры Statusbar SendMessage(hwndSb, SB_SETPARTS, 3, (LPARAM)ptWidth); return FORWARD_WM_SIZE(hwnd, state, cx, cy, DefWindowProc); } // ----------------------------------------------------- // Функция WndProc_OnDrawItem // ----------------------------------------------------- #pragma warning(disable: 4098) void WndProc_OnDrawItem(HWND hwnd, const DRAWITEMSTRUCT * lpDrawItem) { // Необходимо выполнить перерисовку области Statusbar, // которая была определена как SBT_OWNERDRAW if(lpDrawItem->CtlID == IDS_STATUSBAR) { LPDRAWITEMSTRUCT lpDis; HDC hdcMem; HBITMAP hbmOld; BITMAP bm; // Рисуем битовое изображение внутри области lpDis = (LPDRAWITEMSTRUCT)lpDrawItem; hdcMem = CreateCompatibleDC(lpDis->hDC); hbmOld = SelectObject(hdcMem, hSbLogoBmp); GetObject(hSbLogoBmp, sizeof(bm), &bm); BitBlt(lpDis->hDC, lpDis->rcItem.left, lpDis->rcItem.top, bm.bmWidth, bm.bmHeight, hdcMem, 0, 0, SRCCOPY); SelectObject(hdcMem, hbmOld); DeleteDC(hdcMem); } return FORWARD_WM_DRAWITEM(hwnd, lpDrawItem, DefWindowProc); } // ----------------------------------------------------- // Функция WndProc_OnMenuSelect // ----------------------------------------------------- #pragma warning(disable: 4098) void WndProc_OnMenuSelect(HWND hwnd, HMENU hmenu, int item, HMENU hmenuPopup, UINT flags) { static char szBuf[128]; UINT nStrID = 0; szBuf[0] = 0; // Пользователь закрыл меню if(flags == 0xffffffff && hmenu == NULL) nStrID = IDS_DESCRIPTION; // Пользователь выделил в меню строку разделителя else if(flags & MFT_SEPARATOR) nStrID = 0; // Выбрано временное меню else if(flags & MF_POPUP) { // Выбрано системное меню if(flags & MF_SYSMENU) nStrID = IDS_SYSMENU; // Вычисляем идентификатор строки, описывающей // временное меню else nStrID = ((item < sizeof(nIdPopup)/sizeof(nIdPopup[0]))? nIdPopup[item] : 0); } // Используем идентификатор строки, соответствующий // выделенной строке меню else nStrID = item; // Загружаем строку из ресурсов приложения if(nStrID != 0) LoadString(hInst, nStrID, szBuf, sizeof(szBuf)); // Отображаем строку в первой области Toolbar SendMessage(hwndSb, SB_SETTEXT, 0, (LPARAM)szBuf); return FORWARD_WM_MENUSELECT(hwnd, hmenu, item, hmenuPopup, flags, DefWindowProc); }
В листинге 2.2 мы привели файл smart.h, который содержит прототипы функций и определения констант.
Листинг 2.2. Файл smart\smart.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); void WndProc_OnMenuSelect(HWND hwnd, HMENU hmenu, int item, HMENU hmenuPopup, UINT flags); #define IDT_TOOLBAR 801 #define IDS_STATUSBAR 802 #define IDS_SCSIZE SC_SIZE #define IDS_SCMOVE SC_MOVE #define IDS_SCMINIMIZE SC_MINIMIZE #define IDS_SCCLOSE SC_CLOSE #define IDS_SCRESTORE SC_RESTORE #define IDS_SCTASKLIST SC_TASKLIST #define IDS_SCMAXIMIZE SC_MAXIMIZE
Файл resource.h (листинг 2.3) был создан автоматически системой разработки приложений Microsoft Visual C++. Он содержит определения констант, которые используются для доступа к ресурсам приложения Smart Application.
Листинг 2.3. Файл smart\resource.h
//{{NO_DEPENDENCIES}} // Microsoft Visual C++ generated include file. // Used by SMART.RC // #define IDI_APPICON 102 #define IDI_APPICONSM 103 #define IDR_APPMENU 104 #define IDB_TBBITMAP 105 #define IDB_SBLOGO 107 #define ID_FILE_EXIT 40001 #define ID_FILE_SAVEAS 40002 #define ID_HELP_ABOUT 40010 #define IDS_DESCRIPTION 57638 #define IDS_SYSMENU 57639 #define IDS_FILEMENU 57640 #define IDS_EDITMENU 57641 #define IDS_HELPMENU 57642 #define IDS_TEST 61745 // Next default values for new objects // #ifdef APSTUDIO_INVOKED #ifndef APSTUDIO_READONLY_SYMBOLS #define _APS_NO_MFC 1 #define _APS_3D_CONTROLS 1 #define _APS_NEXT_RESOURCE_VALUE 108 #define _APS_NEXT_COMMAND_VALUE 40013 #define _APS_NEXT_CONTROL_VALUE 1000 #define _APS_NEXT_SYMED_VALUE 101 #endif #endif
И, наконец, файл smart.rc (листинг 2.4) содержит описание ресурсов приложения Smart Application. Как и предыдущий файл, он был создан автоматически системой разработки приложений Microsoft Visual C++.
Листинг 2.4. Файл smart\smart.rc
//Microsoft Visual C++ generated resource script. #include "resource.h" #define APSTUDIO_READONLY_SYMBOLS ////////////////////////////////////////////////////////////// // Generated from the TEXTINCLUDE 2 resource. #include "afxres.h" #include "smart.h" ////////////////////////////////////////////////////////////// #undef APSTUDIO_READONLY_SYMBOLS ////////////////////////////////////////////////////////////// // Icon IDI_APPICON ICON DISCARDABLE "smart.ico" IDI_APPICONSM ICON DISCARDABLE "smartsm.ico" #ifdef APSTUDIO_INVOKED ////////////////////////////////////////////////////////////// // TEXTINCLUDE 1 TEXTINCLUDE DISCARDABLE BEGIN "resource.h\0" END 2 TEXTINCLUDE DISCARDABLE BEGIN "#include ""afxres.h""\r\n" "#include ""smart.h""\0" END 3 TEXTINCLUDE DISCARDABLE BEGIN "\r\n" "\0" END ////////////////////////////////////////////////////////////// #endif // APSTUDIO_INVOKED ////////////////////////////////////////////////////////////// // Menu // IDR_APPMENU MENU DISCARDABLE BEGIN POPUP "&File" BEGIN MENUITEM "&New...", ID_FILE_NEW MENUITEM "&Open...", ID_FILE_OPEN MENUITEM "&Close", ID_FILE_CLOSE MENUITEM SEPARATOR MENUITEM "&Save", ID_FILE_SAVE MENUITEM "Save &As...", ID_FILE_SAVEAS MENUITEM SEPARATOR MENUITEM "&Print...", ID_FILE_PRINT MENUITEM SEPARATOR MENUITEM "E&xit", ID_FILE_EXIT END POPUP "&Edit" BEGIN MENUITEM "Cu&t", ID_EDIT_CUT MENUITEM "&Copy", ID_EDIT_COPY MENUITEM "&Paste", ID_EDIT_PASTE END POPUP "&Help" BEGIN MENUITEM "&About...", ID_HELP_ABOUT END END ////////////////////////////////////////////////////////////// // Bitmap // IDB_TBBITMAP BITMAP DISCARDABLE "toolbar.bmp" IDB_SBLOGO BITMAP DISCARDABLE "sblogo.bmp" ////////////////////////////////////////////////////////////// // String Table // STRINGTABLE DISCARDABLE BEGIN ID_FILE_NEW "Creates a new document" ID_FILE_OPEN "Open an existing document" ID_FILE_CLOSE "Closes the active document" ID_FILE_SAVE "Save the active document" ID_FILE_PRINT "Prints the active document" END STRINGTABLE DISCARDABLE BEGIN ID_FILE_EXIT "Exit application" ID_FILE_SAVEAS "Saves the active document under a different name" ID_HELP_ABOUT "Displays information about application" END STRINGTABLE DISCARDABLE BEGIN ID_EDIT_COPY "Copies the selection and puts it on the clipboard" ID_EDIT_CUT "Cuts the selection and puts it on the clipboard" ID_EDIT_PASTE "Inserts the clipboards contents at the insertion point" IDS_DESCRIPTION "Smart Application" IDS_SYSMENU "Move, size or close application window" IDS_FILEMENU "Create, open, save or print documents" IDS_EDITMENU "Delete, copy and insert" IDS_HELPMENU "Get help" END STRINGTABLE DISCARDABLE BEGIN IDS_SCSIZE "Changes the size of the window" END STRINGTABLE DISCARDABLE BEGIN IDS_SCMOVE "Changes the position of the window" END STRINGTABLE DISCARDABLE BEGIN IDS_SCMINIMIZE "Minimizes the window to an icon" END STRINGTABLE DISCARDABLE BEGIN IDS_SCCLOSE "Closes the window" END STRINGTABLE DISCARDABLE BEGIN IDS_SCRESTORE "Restores the window to normal size" END STRINGTABLE DISCARDABLE BEGIN IDS_SCTASKLIST "Switches to another application through the Windows Task Manager" END STRINGTABLE DISCARDABLE BEGIN IDS_SCMAXIMIZE "Enlarges the window to full size" END #ifndef APSTUDIO_INVOKED ////////////////////////////////////////////////////////////// // Generated from the TEXTINCLUDE 3 resource. ////////////////////////////////////////////////////////////// #endif // not APSTUDIO_INVOKED
Приведем краткое описание функций, определенных в файле smart.c приложения Smart Application.
Функция WinMain сохраняет идентификатор приложения в переменной hInst и проверяет, было ли приложение Smart Application запущено ранее. Для этого используется функция FindWindow, с которой вы знакомы по предыдущему приложению Window Application.
После регистрации класса главного окна приложения функцией RegisterClassEx (или функцией RegisterClass, если приложение работает в среде Microsoft Windows NT версии 3.5).
Затем для инициализации библиотеки стандартных органов управления COMCTL32.DLL вызывается функция InitCommonControls .
Далее функция WinMain создает главное окно приложения и запускает цикл обработки сообщений обычным образом.
Функция главного окна приложения Smart Application обрабатывает сообщения WM_CREATE, WM_DESTROY, WM_COMMAND, WM_NOTIFY, WM_SIZE, WM_DRAWITEM и WM_MENUSELECT. Для этого она использует макрокоманду HANDLE_MSG, передавая ей в качестве второго параметра адрес функции обработки сообщения.
Эта функция обрабатывает сообщение WM_CREATE и, следовательно, вызывается при создании главного окна приложения. В задачу обработчика сообщения входит создание и инициализация органов управления Toolbar и Statusbar.
Орган управления Toolbar создается специально предназначенной для этого функцией CreateToolbarEx.
Для того чтобы пользователь смог получать краткую подсказку о назначении кнопок Toolbar , мы указали стиль TBSTYLE_TOOLTIPS . Кроме этого, был задан стиль CCS_ADJUSTABLE, позволяющий пользователю выполнять настройку Toolbar.
Так как между кнопками имеются промежутки, количество пиктограмм (восемь) больше, чем количество кнопок (одиннадцать). В массиве структур tbButtons находятся описания кнопок. Формат этих структур вы уже знаете.
Что же касается Statusbar, то этот орган управления в нашем примере создается при помощи функции CreateWindowEx с использованием стиля окна STATUSCLASSNAME . Для того чтобы правый нижний угол Statusbar можно было использовать для изменения размеров главного окна приложения, мы включили стиль SBARS_SIZEGRIP .
После создания Statusbar его окно разделяется на три области. Для этого окну Statusbar посылается сообщение SB_SETPARTS . Первые две области инициализируются пустыми строками при помощи сообщения SB_SETTEXT . Область с номером 1 отображается без рамки, так как этот номер скомбинирован при помощи логической операции ИЛИ с константой SBT_NOBORDERS .
Область с номером 2 используется для отображения небольшой картинки, загруженной из ресурса приложения с идентификатором IDB_SBLOGO. Поэтому номер области комбинируется с константой SBT_OWNERDRAW .
Эта функция обрабатывает сообщение WM_DESTROY, вызывая функцию PostQuitMessage. В результате приложение завершает свою работу.
Задача функции WndProc_OnCommand - обработка сообщения WM_COMMAND, поступающего от меню приложения, а также от органа управления Toolbar .
Для всех идентификаторов, кроме ID_FILE_EXIT, в этой функции стоит "заглушка", которая выводит сообщение при помощи функции MessageBox. Если же пользователь выбирает строку Exit из меню File, функция обработки сообщения WM_COMMAND вызывает функцию PostQuitMessage для завершения работы приложения.
Функция WndProc_OnNotify обрабатывает извещения, поступающие от органа управления Toolbar в виде сообщения WM_NOTIFY. Мы уже рассказывали вам о том, как нужно обрабатывать эти извещения.
Для примера наш обработчик извещений запрещает пользователю выполнять удаление и вставку самой первой кнопки в органе управления Toolbar .
Функция WndProc_OnSize обрабатывает сообщение WM_SIZE . Напомним, что это сообщение приходит при создании окна, а также при изменении его размеров.
Наш обработчик посылает сообщение WM_SIZE органам управления Toolbar и Statusbar. В ответ на это они изменяют свои размеры таким образом, чтобы расположиться, соответственно, в верхней и нижней части главного окна приложения.
Кроме этого, обработчик сообщения WM_SIZE рассчитывает размеры областей органа управления Statusbar, заполняя массив ptWidth.
Правая граница первой области располагается в середине окна Statusbar, а правая граница второй области - на расстоянии 3/4 длины окна Statusbar от его левой границы. Правая граница третьей области будет простираться до правой границы окна Statusbar.
Размеры устанавливаются при помощи сообщения SB_SETPARTS .
Функция WndProc_OnDrawItem обрабатывает сообщение WM_DRAWITEM , которое посылается органом управления Statusbar при необходимости перерисовки третьей области его окна.
Соответствующая процедура рисует небольшое битовое изображение при помощи функции BitBlt, которая была нами подробно описана в 14 томе "Библиотеки системного программиста".
Идентификатор битового изображения передается через структуру типа DRAWITEMSTRUCT. Адрес структуры, в свою очередь, передается через параметр lParam сообщения WM_DRAWITEM.
Первая область окна Statusbar используется для отображения краткой подсказки о назначении строк системного меню, временных меню приложений и строк временных меню приложений. Для того чтобы проследить выделение строк перечисленных меню пользователем, в нашем приложении предусмотрен обработчик сообщения WM_MENUSELECT . Его роль выполняет функция WndProc_OnMenuSelect.
Обработчик сообщения WM_MENUSELECT анализирует переменные item, flags и hmenu, которые образуются из параметров сообщения следующим образом:
item = (UINT)LOWORD(wParam); flags = (UINT)HIWORD(wParam); hmenu = (HMENU)lParam;
Через 16-разрядный параметр item передается номер временного меню или идентификатор строки меню.
Параметр flags также 16-разрядный. Через него передается тип меню или тип строки. Например, константа MF_POPUP соответствует временному меню, константа MF_SYSMENU - системному меню, а константа MFT_SEPARATOR - разделительной строке во временном меню.
В результате анализа параметров обработчик сообщения записывает в переменную nStrID идентификатор текстовой строки из ресурсов приложения, содержащей нужную подсказку. Если такой строки нет, в переменную nStrID будет записано нулевое значение.
Когда пользователь закрывает меню, например, при помощи клавиши <Esc>, параметр flags содержит значение 0xffff. При этом в параметр hmenu (идентификатор меню) записывается значение NULL.
В параметрах функции WndProc_OnMenuSelect параметр item описан как имеющий тип int, поэтому его значение расширяется до 32-разрядного. Именно поэтому для того чтобы определить момент, когда пользователь закрыл меню, мы сравниваем его значение с константой 0xffffffff:
if(flags == 0xffffffff && hmenu == NULL) nStrID = IDS_DESCRIPTION;
Если пользователь открыл системное или временное меню, в переменную nStrID записывается, соответственно, идентификатор строки IDS_SYSMENU или идентификатор, взятый из массива nIdPopup. Массив nIdPopup содержит таблицу идентификаторов текстовых строк, предназначенных для краткого описания временных меню.
В том случае, когда пользователь открыл временное меню и перемещается по его строкам, в переменную nStrID записывается идентификатор соответствующей строки меню. В ресурсах приложения определены строки с идентификаторами, равными идентификаторам строк меню.
В том случае, когда нужная строка определена, она загружается из ресурсов приложения функцией LoadString и отображается в первой области окна Satusbar при помощи сообщения SB_SETTEXT .