лекции / Shchupak_Yu._Win32_API_Razrabotka_prilozheniy_dlya_Windows
.pdf
Элементы управления главного окна |
401 |
|
|
|
|
Теперь панель инструментов увеличила размеры кнопок с учетом размещения самого длинного текста. По умолчанию текстовые метки выводятся ниже рисун ков. Но можно расположить их справа, если при вызове функции CreateToolbarEx указать дополнительный стиль TBSTYLE_LIST. Вид панели инструментов для этого стиля показан на рис. 8.7.
Кнопки вытянулись в ширину, поэтому при заданных размерах окна пользова тель видит только первые шесть кнопок. Чтобы увидеть панель инструментов полностью, он вынужден раздвигать окно приложения с помощью мыши.
Но если при вызове функции CreateToolbarExуказать не только стиль TBSTYLE_LIST, но еще и стиль TBSTYLE_WRAPABLE, то панель инструментов будет автоматически пе реходить на новую строку, когда ширина окна оказывается недостаточной для нор мального отображения панели. Этот вариант реализации панели инструментов по казан на рис. 8.8.
Рис. 8.8. Панель инструментов со стилями TBSTYLE_LIST | TBSTYLE_WRAPABLE
Размещение на панели инструментов других элементов управления
Панель инструментов поддерживает только кнопки, поэтому для расположения на ее поверхности какого либо другого элемента управления следует создать до чернее окно. Наиболее часто на панель инструментов добавляются комбиниро ванные списки (combo box).
Добавление дочернего окна элемента управления на панель инструментов связано с решением трех проблем: а) резервирование места для дочернего окна; б) обработка сообщений от дочернего окна; в) поддержка подсказки для дочер него окна.
Резервирование места под встроенный элемент управления несложно обеспе чить, поместив кнопку разделитель требуемой ширины. Напомним, что факти ческая ширина кнопки разделителя задается присваиванием нужного значения полю tbb[i].iBitmap. Обычно это значение подбирается экспериментально, исходя из желательной ширины встраиваемого элемента управления.
Уведомительные сообщения от встроенного элемента управления поступают в виде сообщений WM_COMMAND в родительское окно панели инструментов. Так как оконная процедура панели инструментов (спрятанная в недрах Windows) эти
402 |
Глава 8. Элементы управления общего пользования |
|
|
сообщения не обрабатывает, она передает их родительскому окну панели инстру ментов, то есть главному окну приложения. Поэтому в блоке обработки сообще ния WM_COMMAND оконной процедуры WndProc необходимо предусмотреть обра ботку этих уведомительных сообщений.
Мы уже знаем, что панель инструментов поддерживает окна подсказок для всех своих кнопок. Но на встроенные элементы управления эта поддержка не распро страняется. В следующем разделе будет показано, как решить эту проблему с по мощью автономных элементов управления Tooltip.
Рассмотрим технику добавления комбинированного списка на панель инст рументов на примере разработки приложения ComboInToolbar, которое является модификацией приложения ToolBar. Цель модификации — добавить возможность выбора толщины пера, которым обводится контур рисуемой фигуры (до сих пор использовалось перо по умолчанию толщиной в 1 пиксел).
Создайте новый проект с именем ComboInToolbar. Затем скопируйте из папки про екта ToolBar(см. листинг 8.1) в папку проекта ComboInToolbarфайлы с расширениями
.cpp, .h и .rc, скорректировав их имена заменой подстроки ToolBar на ComboInToolbar. Скопируйте также файл toolbar1.bmp. Добавьте эти файлы в состав проекта. Добавьте к настройкам проекта на вкладке Link библиотеку comctl32.lib.
Откройте вкладку ResourceView в окне Workspace и вызовите редактор панелей инструментов двойным щелчком мыши на элементе IDR_TOOLBAR1. Добавьте в на чале панели инструментов две кнопки без изображений с идентификаторами
ID_SEP.
Откройте текст файла ComboInToolbar.cpp и отредактируйте его так, чтобы он соответствовал листингу 8.3.
Листинг 8.3. Проект ComboInToolbar
//////////////////////////////////////////////////////////////////////
// ComboInToolBar.cpp
/* Директивы #include и #define, объявление типов ShapeSize и ShapeData
– из листинга 8.1 */
#define ID_TOOLBAR |
201 |
|
#define IDC_TB_COMBOBOX |
202 |
|
#define NUM_BUTTONS |
13 |
|
#define SEPARATOR_WIDTH |
10 |
|
#define COMBO_SPACE_WIDTH |
50 |
|
#define COMBO_SPACE_HEIGHT 100 |
||
HWND |
hwndToolBar; |
|
HWND |
hwndCombo; |
|
HWND |
InitToolBar(HWND hWnd); |
|
void |
UpdateToolBar(ShapeData& sd); |
|
LRESULT CALLBACK WndProc(HWND, UINT, WPARAM, LPARAM); //==================================================================== int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance,
LPSTR lpCmdLine, int nCmdShow)
{
MSG msg;
Элементы управления главного окна |
403 |
|
|
|
|
KWnd mainWnd("ComboInToolBar", hInstance, nCmdShow, WndProc,
MAKEINTRESOURCE(IDR_MENU1), 100, 100, 400, 300);
/* Здесь такой же текст, как и в листинге 8.1 */
return msg.wParam;
}
//==================================================================== HWND InitToolBar(HWND hWnd) {
HWND hToolBar;
int btnID[NUM_BUTTONS] = { ID_SEP, ID_SEP, ID_RECTANGLE, ID_RHOMB, ID_ELLIPSE, ID_SEP, ID_RED, ID_GREEN, ID_BLUE, ID_SEP,
ID_DARK, ID_MEDIUM, ID_LIGHT };
int btnStyle[NUM_BUTTONS] = { TBSTYLE_SEP, TBSTYLE_SEP, TBSTYLE_BUTTON, TBSTYLE_BUTTON, TBSTYLE_BUTTON, TBSTYLE_SEP, TBSTYLE_CHECK, TBSTYLE_CHECK, TBSTYLE_CHECK, TBSTYLE_SEP, TBSTYLE_CHECKGROUP, TBSTYLE_CHECKGROUP, TBSTYLE_CHECKGROUP };
TBBUTTON tbb[NUM_BUTTONS]; memset(tbb, 0, sizeof(tbb));
for (int i = 0; i < NUM_BUTTONS; ++i) {
if (!i) tbb[i].iBitmap = COMBO_SPACE_WIDTH; else if (btnID[i] == ID_SEP)
tbb[i].iBitmap = SEPARATOR_WIDTH; else tbb[i].iBitmap = i;
tbb[i].idCommand = btnID[i]; tbb[i].fsState = TBSTATE_ENABLED; tbb[i].fsStyle = btnStyle[i];
}
hToolBar = CreateToolbarEx(hWnd,
WS_CHILD | WS_VISIBLE | WS_BORDER | TBSTYLE_TOOLTIPS, ID_TOOLBAR, NUM_BUTTONS, GetModuleHandle(NULL), IDR_TOOLBAR1, tbb, NUM_BUTTONS, 0, 0, 0, 0, sizeof(TBBUTTON));
// Определение позиции и размеров для элемента управления Combo box int x, y, cx, cy;
RECT rcItem;
SendMessage(hToolBar, TB_GETITEMRECT, 0, (LPARAM)&rcItem);
x = rcItem.left + 2; |
y = rcItem.top; |
cx = COMBO_SPACE_WIDTH; |
cy = COMBO_SPACE_HEIGHT; |
// Создание элемента управления Combo box
hwndCombo = CreateWindow("combobox", NULL, WS_CHILD | WS_VISIBLE | CBS_DROPDOWN, x, y, cx, cy, hToolBar, (HMENU)IDC_TB_COMBOBOX, GetModuleHandle(NULL), 0);
// Инициализация списка для hwndCombo SendMessage(hwndCombo, CB_ADDSTRING, 0, (LPARAM)" 1 "); SendMessage(hwndCombo, CB_ADDSTRING, 0, (LPARAM)" 2 ");
продолжение
404 |
Глава 8. Элементы управления общего пользования |
|
|
Листинг 8.3 (продолжение)
SendMessage(hwndCombo, CB_ADDSTRING, 0, (LPARAM)" 5 ");
SendMessage(hwndCombo, CB_ADDSTRING, 0, (LPARAM)" 10 ");
SendMessage(hwndCombo, CB_SETCURSEL, 0, 0);
return hToolBar;
}
//==================================================================== void UpdateToolBar(ShapeData& sd) {
/* Здесь такой же текст, как и в листинге 8.1 */
}
//==================================================================== LRESULT CALLBACK WndProc(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
{
/* Объявления локальных переменных из листинга 8.1 */
static HPEN hPen, hOldPen; static int indPen;
int penWidth[] = { 1, 2, 5, 10 };
switch (uMsg)
{
case WM_CREATE:
/* Здесь такой же текст, как и в листинге 8.1 */
break;
case WM_SIZE:
SendMessage(hwndToolBar, TB_AUTOSIZE, 0, 0); break;
case WM_NOTIFY:
/* Здесь такой же текст, как и в листинге 8.1 */ break;
case WM_COMMAND:
switch (LOWORD(wParam))
{
/* Обработка для кодов сообщений IDM_OPEN, IDM_CLOSE, … , IDM_RESIZE – как в листинге 8.1 */
case IDM_ABOUT: MessageBox(hWnd,
"ComboInToolBar\nVersion 1.0\nCopyright: " "Finesoft Corporation, 2005.",
"About ComboInToolBar", MB_OK); break;
// Обработка уведомительного сообщения от комбинированного списка case IDC_TB_COMBOBOX:
switch (HIWORD(wParam)) { case CBN_SELCHANGE:
// Выбираем текущий индекс пера
indPen = SendMessage(hwndCombo, CB_GETCURSEL, 0, 0);
}
Элементы управления главного окна |
405 |
|
|
|
|
break;
default:
break;
}
UpdateToolBar(shapeData); InvalidateRect(hWnd, NULL, TRUE); break;
case WM_PAINT:
hDC = BeginPaint(hWnd, &ps);
brightness = intensity[shapeData.id_bright - ID_DARK];
if (bShow) {
hPen = CreatePen(PS_SOLID, penWidth[indPen], RGB(0,0,0)); hOldPen = (HPEN)SelectObject(hDC, hPen);
/* Здесь такой же текст, как и в листинге 8.1 */
DeleteObject(SelectObject(hDC, hOldBrush)); DeleteObject(SelectObject(hDC, hOldPen));
}
EndPaint(hWnd, &ps); break;
case WM_DESTROY: PostQuitMessage(0); break;
default:
return DefWindowProc(hWnd, uMsg, wParam, lParam);
}
return 0;
}
//////////////////////////////////////////////////////////////////////
В этой программе количество кнопок на панели инструментов увеличено до тринадцати, так как добавлены две кнопки разделителя в начале панели. Первая из этих кнопок разделителей, имеющая ширину COMBO_SPACE_WIDTH, использует ся для резервирования места под комбинированный список. Вторая кнопка раз делитель создает промежуток между комбинированным списком и первой стан дартной кнопкой панели инструментов.
Обратите внимание на то, что в массивах btnID и btnStyle учтено появление новых кнопок. Первые два элемента в btnID инициализированы значением ID_SEP, а первые два элемента в btnStyle инициализированы значением TBSTYLE_SEP.
Панель инструментов создается, как и раньше, вызовом функции CreateToolbarEx. Затем определяются позиция и размеры для встраиваемого комбинированного списка. Поскольку он размещается на месте первой кнопки разделителя, мы уз наем координаты изображения этой кнопки, имеющей нулевой индекс, отправив сообщение TB_GETITEMRECT. В результате структура rcItem получает искомые ко ординаты кнопки разделителя. Эти координаты используются затем для вычис ления позиции (x, y) встраиваемого элемента управления.
406 |
Глава 8. Элементы управления общего пользования |
|
|
Дочернее окно комбинированного списка hwndCombo создается вызовом функ ции CreateWindow. Инициализация списка осуществляется отправкой серии сооб щений CB_ADDSTRING.
В тексте оконной процедуры WndProc произошли определенные изменения. Прежде всего были добавлены определения переменных:
static HPEN hPen, hOldPen; static int indPen;
int penWidth[] = { 1, 2, 5, 10 };
Также добавлена обработка уведомительного сообщения CBN_SELCHANGE от ком бинированного списка:
case IDC_TB_COMBOBOX:
switch (HIWORD(wParam)) { case CBN_SELCHANGE:
// Выбираем текущий индекс пера
indPen = SendMessage(hwndCombo, CB_GETCURSEL, 0, 0);
}
break;
В блоке обработки сообщения WM_PAINT выбранный индекс пера используется при создании пера hPen:
hPen = CreatePen(PS_SOLID, penWidth[indPen], RGB(0,0,0)); hOldPen = (HPEN)SelectObject(hDC, hPen);
На рис. 8.9 показано окно работающего приложения ComboInToolbar после вы бора в окне комбинированного списка значения 10.
Рис. 8.9. Приложение ComboInToolbar. Выбрана толщина пера 10 логических единиц
К сожалению, в нашей новой программе комбинированный список, встроен ный на панель инструментов, отличается от кнопок панели тем, что не имеет окна подсказки. В следующем разделе мы сможем устранить этот недостаток, приме нив автономный элемент управления Tooltip.
Окно подсказки
Вотличие от встроенного на панель инструментов элемента управления Tooltip, автономный элемент управления Tooltip создается в явном виде как окно предоп ределенного оконного класса TOOLTIPS_CLASS при помощи функции CreateWindowEx.
Вдальнейшем изложении, употребляя термин «окно подсказки», мы будем гово рить именно об автономном элементе управления Tooltip.
408 |
Глава 8. Элементы управления общего пользования |
|
|
Комментарий к полю lpszText показывает, что текст подсказки может хранить ся либо в виде С строки, либо в виде строкового ресурса в таблице строк прило жения.
Таким образом, создав элемент управления Tooltip и зарегистрировав в нем «го рячую» область какого либо окна, можно обеспечить вывод требуемой подсказки. Перечисленные действия имеет смысл инкапсулировать в теле какой нибудь функ ции. Такой подход будет продемонстрирован в рассмотренном ниже примере.
Замена класса KWnd на класс KWndEx
Вернемся к нашей последней программе, в которой комбинированный список, встроенный в панель инструментов, не имел всплывающей подсказки. Попробу ем устранить этот недостаток.
Одновременно заменим хорошо послуживший нам класс KWnd на его модифи кацию — класс KWndEx. Цель замены — обеспечить регистрацию оконных классов для элементов управления общего пользования в конструкторе класса, чтобы осво бодить программиста от этой рутинной операции. Кроме того, в файлах KWndEx.h, KWndEx.cpp будут размещены также интерфейс и реализация функций общего при менения.
В состав функций общего применения войдут следующие функции:
перегруженная функция ShiftWindow, предназначенная для сдвига и модифи кации размеров окон (эта функция использовалась ранее в листинге 7.5);
функция AddTooltip, добавляющая окно подсказки к указанному окну;
функция TRACE, которую можно использовать для отладочного вывода в окно Output (аналогично макросу TRACE в библиотеке MFC).
Все во имя программиста, все для блага программиста!
Создайте новый проект с именем ToolTip. Скопируйте из папки проекта
ComboInToolbar (см. листинг 8.3) в папку проекта ToolTip файлы с расширениями
.cpp, .h и .rc, скорректировав их имена заменой подстроки ComboInToolbar на ToolTip. Скопируйте также файл toolbar1.bmp. Измените имена файлов Kwnd.h, Kwnd.cpp на KwndEx.h и KwndEx.cpp соответственно. Добавьте все перечисленные файлы в со став проекта. Также к настройкам проекта на вкладке Link надо добавить библио теку comctl32.lib.
Откройте вкладку ResourceView в окне Workspace. В списке ресурсов откройте папку String table и вызовите редактор таблицы строк двойным щелчком мыши на элементе String table. Добавьте в таблицу строк следующие две строки:
Идентификатор |
Строка |
|
|
IDS_TB_COMBOBOX |
Доступ к списку для выбора толщины пера |
IDS_EDIT_IN_COMBO |
Толщина пера для контура фигуры |
|
|
Обратите внимание на то, что потребовалось указать две строки, чтобы эле мент управления Combo box получил все необходимые подсказки. Первый текст подсказки будет отображаться при наведении курсора мыши на стрелку, исполь зуемую для открывания списка, второй текст подсказки — при наведении курсо ра мыши на окно редактирования.
Отредактируйте тексты файлов KwndEx.h, KwndEx.cpp, ToolTip.cpp так, чтобы они соответствовали листингу 8.4.
Элементы управления главного окна |
409 |
|
|
|
|
Листинг 8.4. Проект ToolTip
//////////////////////////////////////////////////////////////////////
// KWndEx.h
#include <windows.h>
class KWndEx { public:
KWndEx(LPCTSTR windowName, HINSTANCE hInst, int cmdShow, LRESULT (WINAPI *pWndProc)(HWND,UINT,WPARAM,LPARAM), LPCTSTR menuName = NULL,
int x = CW_USEDEFAULT, int y = 0,
int width = CW_USEDEFAULT, int height = 0, UINT classStyle = CS_HREDRAW | CS_VREDRAW, DWORD windowStyle = WS_OVERLAPPEDWINDOW, HWND hParent = NULL);
HWND GetHWnd() { return hWnd; }
protected: HWND hWnd;
WNDCLASSEX wc;
};
//==================================================================== // Ô ó í ê ö è è î á ù å ã î ï ð è ì å í å í è ÿ //--------------------------------------------------------------------
// Ñ ä â è ã è î ê î í
//Сдвиг окна верхнего уровня с модификацией размеров void ShiftWindow(HWND hwnd, int dX = 0, int dY = 0,
int dW = 0, int dH = 0);
//Сдвиг дочернего окна с модификацией размеров
void ShiftWindow(HWND hChild, HWND hParent, int dX = 0, int dY = 0, int dW = 0, int dH = 0);
//Сдвиг окна элемента управления с модификацией размеров void ShiftWindow(int ctrlID, HWND hParent, int dX = 0,
int dY = 0, int dW = 0, int dH = 0); //--------------------------------------------------------------------
//Добавление окна подсказки
void AddTooltip (HWND hwndOwner, LPTSTR lpMsg);
//--------------------------------------------------------------------
//Функция отладочной печати (вывод в окно "Output")
//- работает аналогично макросу TRACE в библиотеке MFC void TRACE(LPCTSTR lpszFormat, ...);
//////////////////////////////////////////////////////////////////////
// KWndEx.cpp #include "KWndEx.h" #include <commctrl.h>
KWndEx::KWndEx(LPCTSTR windowName, HINSTANCE hInst, int cmdShow, LRESULT (WINAPI *pWndProc)(HWND,UINT,WPARAM,LPARAM), LPCTSTR menuName, int x, int y, int width, int height, UINT classStyle, DWORD windowStyle, HWND hParent)
{
/* Такой же текст, как и в конструкторе KWnd (листинг 1.2) |
*/ |
// Инициализация библиотеки "Common Control Library"
продолжение
410 Глава 8. Элементы управления общего пользования
Листинг 8.4 (продолжение)
INITCOMMONCONTROLSEX icc;
icc.dwSize = sizeof(INITCOMMONCONTROLSEX); icc.dwICC = ICC_WIN95_CLASSES; InitCommonControlsEx(&icc);
}
//====================================================================
// Ô ó í ê ö è è |
î á ù å ã î |
ï ð è ì å í å í è ÿ |
|
// |
-------------------------------------------------------------------- |
|
|
// |
Сдвиг окна верхнего уровня с |
модификацией размеров |
|
void ShiftWindow(HWND hwnd, int dX, int dY, int dW, int dH) { /* Текст функции из листинга 7.5 (файл KWndPlut.cpp) */
}
// Сдвиг дочернего окна с модификацией размеров
void ShiftWindow(HWND hChild, HWND hParent, int dX, int dY, int dW, int dH) {
/* Текст функции из листинга 7.5 |
*/ |
} |
|
// Сдвиг окна элемента управления с модификацией размеров
void ShiftWindow(int ctrlID, HWND hParent, int dX, int dY, int dW, int dH) { /* Текст функции из листинга 7.5 */
}
//--------------------------------------------------------------------
// Добавление окна подсказки
void AddTooltip (HWND hwndOwner, LPTSTR lpMsg) {
HWND hwndTip; |
// дескриптор элемента управления Tooltip |
|
static TOOLINFO ti; |
// информация об |
инструменте (о «горячей» области), |
|
// обслуживаемом |
элементом hwndTip |
HINSTANCE hInst = (HINSTANCE)GetWindowLong(hwndOwner, GWL_HINSTANCE);
hwndTip = CreateWindowEx(WS_EX_TOPMOST, TOOLTIPS_CLASS, NULL, WS_POPUP | TTS_NOPREFIX | TTS_ALWAYSTIP,
CW_USEDEFAULT, CW_USEDEFAULT, CW_USEDEFAULT, CW_USEDEFAULT, hwndOwner, NULL, hInst, NULL);
//Инициализация структуры ti ti.cbSize = sizeof(TOOLINFO); ti.uFlags = TTF_SUBCLASS; ti.hwnd = hwndOwner;
ti.hinst = hInst; ti.uId = 0; ti.lpszText = lpMsg;
//Местоположение инструмента в окне hwndOwner
//(инструмент покрывает всю клиентскую область) GetClientRect (hwndOwner, &ti.rect);
//Регистрация инструмента с информацией ti SendMessage(hwndTip, TTM_ADDTOOL, 0, (LPARAM)(LPTOOLINFO) &ti);
}
//--------------------------------------------------------------------
