лекции / Shchupak_Yu._Win32_API_Razrabotka_prilozheniy_dlya_Windows
.pdfИспользование других элементов управления |
351 |
|
|
|
|
buf[N1-1] = 0; return buf;
}
const char* GetAddress() { memcpy(buf, dat.ss.address, N2); buf[N2-1] = 0;
return buf;
}
Data dat;
};
ListItem item;
BOOL CALLBACK ViewDlgProc(HWND, UINT, WPARAM, LPARAM); BOOL CALLBACK AddRecDiaProc(HWND, UINT, WPARAM, LPARAM); LRESULT CALLBACK WndProc(HWND, UINT, WPARAM, LPARAM);
//==================================================================== int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance,
LPSTR lpCmdLine, int nCmdShow)
{
MSG msg;
KWnd mainWnd("MyNotebook1", hInstance, nCmdShow, WndProc, MAKEINTRESOURCE(IDR_MENU1), 100, 100, 400, 300);
while (GetMessage(&msg, NULL, 0, 0)) { TranslateMessage(&msg); DispatchMessage(&msg);
}
return msg.wParam;
}
//==================================================================== LRESULT CALLBACK WndProc(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
{
HINSTANCE hInst;
switch (uMsg) {
case WM_COMMAND:
switch (LOWORD(wParam)) { case IDM_VIEW:
hInst = GetModuleHandle(NULL);
DialogBox(hInst, MAKEINTRESOURCE(IDD_VIEW), hWnd, ViewDlgProc); break;
}
break;
case WM_DESTROY: PostQuitMessage(0); break;
default:
return DefWindowProc(hWnd, uMsg, wParam, lParam);
}
return 0;
}
//====================================================================
продолжение
352 |
Глава 7. Диалоговые окна |
|
|
Листинг 7.4 (продолжение)
BOOL CALLBACK ViewDlgProc(HWND hDlg, UINT uMsg, WPARAM wParam,
LPARAM lParam) {
static HWND hListBox;
static HWND hEditInfoAddr, hEditInfoBday; HWND hCtrl;
static LOGFONT lf; HFONT hFont1;
COLORREF editInfoColor; static HBRUSH hBrush;
int nItems; // количество элементов в списке int iCurItem; // текущий выбранный элемент списка BOOL dlgYes;
int i;
switch (uMsg) { case WM_INITDIALOG:
hListBox = GetDlgItem(hDlg, IDC_LIST1); hEditInfoAddr = GetDlgItem(hDlg, IDC_INFO_ADDR); hEditInfoBday = GetDlgItem(hDlg, IDC_INFO_BDAY);
//Модификация шрифта для элемента hListBox lf.lfHeight = 16;
lstrcpy( (LPSTR)&lf.lfFaceName, "Courier" ); hFont1 = CreateFontIndirect(&lf);
SendMessage(hListBox, WM_SETFONT, (WPARAM)hFont1, TRUE );
//Создание кисти для фона элементов управления editInfoColor = RGB(190, 255, 255);
hBrush = CreateSolidBrush(editInfoColor);
//Чтение "базы данных" из файла и инициализация списка fdat.open("notebook.dat", ios::in);
if (!fdat) { fdat.clear(); return TRUE; } else while (1) {
fdat.getline(item.dat.line, sizeof(Data));
if (fdat.eof()) { fdat.clear(); break; } SendMessage(hListBox, LB_ADDSTRING, 0,
(LPARAM)item.dat.line);
}
fdat.close(); return TRUE;
case WM_CTLCOLORSTATIC:
// Модификация цветовых атрибутов окон редактирования hCtrl = (HWND)lParam;
if ((hCtrl == hEditInfoAddr) || (hCtrl == hEditInfoBday)) { SetBkColor((HDC)wParam, editInfoColor); SetBkMode((HDC)wParam, TRANSPARENT);
return (DWORD)hBrush;
}
break;
case WM_COMMAND:
switch (LOWORD(wParam))
{
Использование других элементов управления |
353 |
|
|
|
|
case IDC_REC_ADD:
dlgYes = DialogBox((HINSTANCE)GetModuleHandle(NULL), MAKEINTRESOURCE(IDD_ADD_REC), hDlg, AddRecDiaProc);
if (dlgYes)
SendMessage(hListBox, LB_ADDSTRING, 0, (LPARAM)item.dat.line);
break;
case IDC_REC_DELETE:
iCurItem = SendMessage(hListBox, LB_GETCURSEL, 0, 0); if (iCurItem != LB_ERR) {
SendMessage(hListBox, LB_DELETESTRING, iCurItem, 0); SetDlgItemText(hDlg, IDC_INFO_ADDR, ""); SetDlgItemText(hDlg, IDC_INFO_BDAY, "");
}
else {
MessageBox(hDlg, "Сначала надо выделить элемент списка", "Ошибка", MB_OK);
break;
}
break;
case IDC_LIST1:
switch (HIWORD(wParam)) { case LBN_SELCHANGE:
// Показываем дополнительную информацию
iCurItem = SendMessage(hListBox, LB_GETCURSEL, 0, 0); SendMessage(hListBox, LB_GETTEXT, iCurItem,
(LPARAM)item.dat.line);
SetDlgItemText(hDlg, IDC_INFO_ADDR, item.GetAddress()); SetDlgItemText(hDlg, IDC_INFO_BDAY, item.GetBirthday());
return TRUE;
}
break;
case IDOK:
fdat.open("notebook.dat", ios::out | ios::trunc); if (!fdat) {
MessageBox(hDlg,
"Не могу открыть файл 'notebook.dat' для записи!", "Ошибка", MB_OK);
return FALSE;
}
nItems = SendMessage(hListBox, LB_GETCOUNT, 0, 0); for (i = 0; i < nItems; ++i) {
SendMessage(hListBox, LB_GETTEXT, i, (LPARAM)item.dat.line); fdat << item.dat.line << endl;
}
fdat.close();
EndDialog(hDlg, TRUE); return TRUE;
case IDCANCEL: EndDialog(hDlg, FALSE);
продолжение
Использование других элементов управления |
355 |
|
|
|
|
Вклассе также определен метод DoOneString, склеивающий четыре подстроки
водну строку. Эта подзадача решается путем сканирования массива dat.line от его конца к началу, с заменой всех нулевых байтов, кроме ближайшего к концу масси ва, символом пробела. В классе также определены метод GetBirthday, выполняю щий извлечение в виде C строки значения поля dat.ss.birthday, и метод GetAddress, делающий то же самое для поля dat.ss.address.
Переменная item, которая является объектом класса ListItem, объявлена в гло бальной области видимости программы. Эта переменная используется и для внут ренних нужд диалоговой процедуры ViewDlgProc, и для обмена данными между диалоговыми процедурами AddRecDiaProc и ViewDlgProc.
Диалоговая процедура ViewDlgProc, обслуживающая диалог IDD_VIEW, выпол няет самую большую часть работы данного приложения.
Блок обработки сообщения WM_INITDIALOG содержит следующую функцио нальность:
Создается специальный шрифт с дескриптором hFont1 для использования в окне списка. В программе используется моноширинный шрифт Courier, что бы обеспечить правильное расположение границы второго столбца, в котором выводится информация о телефоне. Модификация шрифта в окне hListBox ре ализуется отправкой сообщения WM_SETFONT.
Создается кисть hBrush для изменения фона элементов управления hEditInfoAddr и hEditInfoBday. Эта кисть применяется в блоке обработки сообщения
WM_CTLCOLORSTATIC.
Инициализируется список hListBox. Для этого из файла notebook.dat считыва ется информация, и каждая прочитанная строка добавляется в список с помо щью посылки сообщения LB_ADDSTRING.
Вблоке обработки сообщения WM_COMMAND решаются следующие подзадачи:
Если сообщение поступило от кнопки IDC_REC_ADD, то при помощи функции DialogBox вызывается модальный диалог IDD_ADD_REC. Работая с этим диало гом, пользователь вводит информацию для очередной записи. Если ввод за вершен нажатием кнопки OK, то функция DialogBox вернет значение TRUE и будет вызвана функция SendMessage для отправки сообщения LB_ADDSTRING.
Если сообщение поступило от кнопки IDC_REC_DELETE, мы определяем, какой пункт списка выделен пользователем. Если такой пункт есть, то отправляем окну списка сообщение LB_DELETESTRING, а также очищаем окна редактирова ния IDC_INFO_ADDR и IDC_INFO_BDAY при помощи функции SetDlgItemText (в них может отображаться информация для выбранного пункта списка).
Если сообщение поступило от окна списка IDC_LIST1 и оно содержит код уве домления LBN_SELCHANGE, то надо получить данные для выделенного пункта iCurItem. Для этого отправляется сообщение LB_GETTEXT. Затем поля записи, со храненной в объекте item, с информацией об адресе и дне рождения выводятся с помощью функции SetDlgItemText в окна редактирования IDC_INFO_ADDR и
IDC_INFO_BDAY.
Если сообщение поступило от кнопки IDOK, то приложение записывает содер жимое списка в файл notebook.dat.
356 |
Глава 7. Диалоговые окна |
|
|
Диалоговая процедура AddRecDiaProc очень проста и не требует дополнительных пояснений. Но все же стоит обратить внимание на вызов метода item.DoOneString, выполняющего объединение четырех подстрок в одну C строку.
На рис. 7.27 показан внешний вид работающей программы MyNotebook1.
Рис. 7.27. Просмотр содержимого списка в приложении MyNotebook1
Комбинированный список
Комбинированный список (Combo box), называемый также комбинированным ок ном, состоит из окна редактирования и окна списка. В классическом варианте ком бинированный список выглядит как окно редактирования со стрелкой справа. Если на ней щелкнуть мышью, то появится выпадающий список.
Комбинированный список помещается на форму диалога при помощи мыши с предварительным выделением элемента Combo box на панели инструментов Controls. Сразу после щелчка на форме диалога элемент Combo box выглядит при мерно так, как показано на рис. 7.28.
Рис. 7.28. Размещение комбинированного списка на форме диалога
Обратите внимание на специальную «редакторскую» рамку вокруг окна эле мента Combo box. Она говорит о том, что элемент управления сейчас находится в выделенном состоянии. Мы уже имели дело с выделением элементов управле ния, занимаясь выравниванием их размещения на форме диалога с помощью под меню Layout. Но сейчас речь пойдет совсем о другом.
Щелкните на стрелке справа, чтобы открыть окно выпадающего списка. Ре дактор диалога покажет размеры этого окна, растянув прямоугольник рамки вниз. А теперь — внимание! Если размеры окна списка окажутся слишком маленькими и их не будет хватать для отображения хотя бы одного двух элементов списка, то элемент управления Combo box работать не будет! Поэтому растяните мышью ог
Использование других элементов управления |
357 |
|
|
|
|
раничивающую рамку окна списка до того размера, который вы хотите увидеть в работающем приложении (рис. 7.29).
Рис. 7.29. Установка размеров для выпадающего списка
К сожалению, ни в справочной помощи MSDN, ни в других источниках ин формации об этой технологической «мелочи» ничего не говорится. Я знаю про граммистов, которые потеряли много часов, выясняя, почему у них не работает Combo box, пока они не находили ответ случайно в процессе экспериментов либо благодаря подсказке более опытных коллег.
После размещения элемента управления Combo box на форме диалога вы опре деляете его атрибуты, вызывая окно свойств Combo box Properties.
На вкладке General в поле ID введите необходимый идентификатор элемента управления. На вкладке Styles (рис. 7.30) установите дополнительные свойства комбинированного списка.
Рис. 7.30. Вкладка Styles в окне Combo Box Properties
Многие свойства Combo box аналогичны свойствам элемента управления List box. Но есть и некоторые различия. Стиль списка выбирается в окне Type из сле дующих значений:
Стиль |
Описание |
|
|
Simple |
Комбинированный список отображается окном редактирования и раскрытым окном |
|
списка. Содержание окна редактирования можно изменять |
Dropdown |
Комбинированный список отображается в свернутом состоянии со стрелкой справа. |
|
Содержание окна редактирования можно изменять |
Drop List |
Комбинированный список отображается в свернутом состоянии со стрелкой справа. |
|
Содержание окна редактирования изменять нельзя |
|
|
В окне свойств появилась новая вкладка Data. Разработчики, создающие при ложения на базе библиотеки классов MFC, пользуются вкладкой Data для ввода строк, которыми инициализируется Combo box. Редактор диалоговых окон сохра няет введенные строки в файле описания ресурсов в виде определения ресурса DLGINIT. К сожалению, работая с Win32 API, вы не можете использовать подоб ный способ инициализации списка, так как в этом случае отсутствует поддержка
358 |
Глава 7. Диалоговые окна |
|
|
интерпретации ресурса типа DLGINIT. Для инициализации комбинированного спис ка применяется посылка сообщений CB_ADDSTRING.
Обмен сообщениями с комбинированным списком
Обмен сообщениями с комбинированным списком во многом похож на обмен со общениями с элементами Edit box и List box. При этом идентификаторы сообщений, отправляемых элементу Combo box, начинаются с префикса CB_, а идентификаторы уведомительных сообщений, посылаемых от Combo box своему родительскому окну, имеют префикс CBN_.
В табл. 7.11 приведено сопоставление некоторых кодов сообщений, посылае мых элементу управления Combo box, с аналогичными сообщениями, посылаемы ми элементам управления Edit box и List box.
Таблица 7.11. Однотипные сообщения для элементов управления Edit box, List box и Combo box
Код сообщения для Edit box или List box |
Код сообщения для Combo box |
|
|
EM_GETSEL |
CB_GETEDITSEL |
EM_SETSEL |
CB_SETEDITSEL |
LB_ADDSTRING |
CB_ADDSTRING |
LB_DELETESTRING |
CB_DELETESTRING |
LB_GETCOUNT |
CB_GETCOUNT |
LB_GETCURSEL |
CB_GETCURSEL |
LB_GETTEXT |
CB_GETLBTEXT |
LB_SETCURSEL |
CB_SETCURSEL |
|
|
Полный список сообщений, посылаемых элементу Combo box, можно найти в справочных материалах MSDN. Всего существует более шестидесяти подобных сообщений.
В табл. 7.12 приведено аналогичное сопоставление некоторых кодов уведоми тельных сообщений.
Таблица 7.12. Однотипные уведомительные сообщения от Edit box, List box и Combo box
Код сообщения от Edit box или List box |
Код сообщения от Combo box |
|
|
EN_SETFOCUS, LBN_SETFOCUS |
CBN_SETFOCUS |
EN_KILLFOCUS, LBN_KILLFOCUS |
CBN_KILLFOCUS |
EN_UPDATE |
CBN_EDITUPDATE |
EN_CHANGE |
CBN_EDITCHANGE |
EN_ERRSPACE, LBN_ERRSPACE |
CBN_ERRSPACE |
LBN_SELCHANGE |
CBN_SELCHANGE |
|
|
Пример использования элемента управления Combo box
Сделаем небольшую доработку предыдущего приложения. Добавим возможность для пользователя выбирать размер шрифта в окне списка нашей электронной за писной книжки. Нам будут очень признательны те пользователи, у которых есть проблемы со зрением и которым трудно читать мелкий шрифт.
Использование других элементов управления |
359 |
|
|
|
|
Для выбора размера шрифта используем элемент управления Combo box. В но вой программе кроме изменения шрифта, используемого в окне списка IDC_LIST1, необходимо изменять ширину этого окна так, чтобы в нем помещались первые два поля каждой записи. Кроме того, должны регулироваться размеры диалого вого окна и позиции других элементов управления в зависимости от используе мого шрифта.
Проблему изменения размеров и позиции окна приходится решать в самых разных приложениях. Поэтому мы разработаем функцию ShiftWindow общего при менения, код которой будет размещен в одном файле с реализацией класса KWnd.
Прежде всего создайте новый проект с именем MyNotebook2. Скопируйте из папки проекта MyNotebook1 (см. листинг 7.4) в папку проекта MyNotebook2 файлы с расширениями .cpp, .h и .rc, скорректировав их имена заменой подстроки
MyNotebook1 на MyNotebook2. Измените имена файлов KWnd.h, KWnd.cpp, содержа щих интерфейс и реализацию класса KWnd, на имена KWndPlut.h, KWndPlut.cpp1. Добавьте все перечисленные файлы в состав проекта.
Откройте шаблон диалога IDD_VIEW двойным щелчком мыши на соответствую щем значке на вкладке Resource View в окне Workspace. Добавьте на форму диалога (рис. 7.31) элементы управления Static Text (с текстом Шрифт:) и Combo box.
Рис. 7.31. Размещение на форме диалога новых элементов управления
В окне свойств элемента Combo box укажите идентификатор IDC_FONT. На вклад ке Styles выберите стиль списка Drop List и сбросьте флажок Sort.
Измените текст в файлах KWndPlut.h, KWndPlut.cpp и MyNotebook2.cpp так, чтобы он соответствовал листингу 7.5.
Листинг 7.5. Проект MyNotebook2
//////////////////////////////////////////////////////////////////////
// KWndPlut.h #include <windows.h>
class KWnd {
/* Текст из листинга 1.2 */ };
//====================================================================
продолжение
1 Plut — plus utilities.
360 |
Глава 7. Диалоговые окна |
|
|
Листинг 7.5 (продолжение)
//Утилиты общего применения //====================================================================
//Сдвиг окна верхнего уровня с модификацией размеров
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);
//////////////////////////////////////////////////////////////////////
// |
KWndPlut.cpp |
#include "KWndPlut.h" |
|
KWnd::KWnd(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) |
{ |
|
/* |
Текст из листинга 1.2 */ |
} |
|
//====================================================================
//Утилиты общего применения //====================================================================
//Сдвиг окна верхнего уровня с модификацией размеров
void ShiftWindow(HWND hwnd, int dX, int dY, int dW, int dH) { RECT rect;
GetWindowRect(hwnd, &rect); int x0 = rect.left + dX; int y0 = rect.top + dY;
int width = rect.right - rect.left + dW; int height = rect.bottom - rect.top + dH;
MoveWindow(hwnd, x0, y0, width, height, TRUE);
}
// Сдвиг дочернего окна с модификацией размеров
void ShiftWindow(HWND hChild, HWND hParent, int dX, int dY, int dW, int dH) { RECT rect;
POINT p0;
GetWindowRect(hChild, &rect);
int width = rect.right - rect.left + dW; int height = rect.bottom - rect.top + dH;
p0.x = rect.left + dX; p0.y = rect.top + dY;
ScreenToClient(hParent, &p0);
MoveWindow(hChild, p0.x, p0.y, width, height, TRUE);
}
// Сдвиг окна элемента управления с модификацией размеров
void ShiftWindow(int ctrlID, HWND hParent, int dX, int dY, int dW, int dH) { RECT rect;
HWND hCtrl = GetDlgItem(hParent, ctrlID);
