лекции / Shchupak_Yu._Win32_API_Razrabotka_prilozheniy_dlya_Windows
.pdfИспользование других элементов управления |
361 |
|
|
|
|
POINT p0;
GetWindowRect(hCtrl, &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);
ShowWindow(hCtrl, SW_HIDE);
MoveWindow(hCtrl, p0.x, p0.y, width, height, TRUE); ShowWindow(hCtrl, SW_SHOW);
}
//////////////////////////////////////////////////////////////////////
// MyNotebook2.cpp #include <windows.h> #include <fstream> using namespace std;
#include "KWndPlut.h" #include "resource.h"
#define N1 21 #define N2 81
#define TOTAL_SIZE (3*N1 + N2) fstream fdat;
struct SubStr {
/* Текст из листинга 7.4 */
};
union Data {
/* Текст из листинга 7.4 */
};
class ListItem {
/* Текст из листинга 7.4 */
};
ListItem item;
int fontHeight[] = { 16, 20, 24, 28, 30 };
int iFont = 0; // текущий шрифт для окна List box
BOOL CALLBACK ViewDlgProc(HWND, UINT, WPARAM, LPARAM); BOOL CALLBACK AddRecDiaProc(HWND, UINT, WPARAM, LPARAM); void AdjustDialogSize(HWND hDlg);
LRESULT CALLBACK WndProc(HWND, UINT, WPARAM, LPARAM); //==================================================================== int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance,
LPSTR lpCmdLine, int nCmdShow)
{
MSG msg;
KWnd mainWnd("MyNotebook2", hInstance, nCmdShow, WndProc, MAKEINTRESOURCE(IDR_MENU1), 100, 100, 400, 300);
while (GetMessage(&msg, NULL, 0, 0)) {
TranslateMessage(&msg);
продолжение
362 |
Глава 7. Диалоговые окна |
|
|
Листинг 7.5 (продолжение)
DispatchMessage(&msg);
}
return msg.wParam;
}
//==================================================================== LRESULT CALLBACK WndProc(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
{
/* Текст из листинга 7.4 */
}
//====================================================================
//Настройка размеров диалога и размещение элементов управления
//по метрикам шрифта, установленного в окне списка
void AdjustDialogSize(HWND hDlg) {
int nCtrlId[] = { IDC_GROUP_1, IDC_STATIC_ADDR, IDC_STATIC_BDAY, IDC_INFO_ADDR, IDC_INFO_BDAY, IDOK, IDCANCEL, IDC_REC_ADD, IDC_REC_DELETE };
HWND hListBox = GetDlgItem(hDlg, IDC_LIST1); static LOGFONT lf;
static HFONT hFont1; HDC hDC;
TEXTMETRIC tm;
int showTextWidth; // длина текста (в пикселах), показываемого
|
// в окне списка |
RECT rcLB; |
// первоначальное размещение окна списка |
int shift; |
// сдвиг правой границы окна списка для |
//отображения текста длиной showTextWidth
//Модификация шрифта для элемента hListBox
if (hFont1) DeleteObject(hFont1); lf.lfHeight = fontHeight[iFont];
lstrcpy( (LPSTR)&lf.lfFaceName, "Courier" ); hFont1 = CreateFontIndirect(&lf);
SendMessage(hListBox, WM_SETFONT, (WPARAM)hFont1, TRUE );
//Модификация формы диалога с учетом размера шрифта,
//используемого в окне списка
hDC = GetDC(hListBox); SelectObject(hDC, hFont1); GetTextMetrics(hDC, &tm); ReleaseDC(hListBox, hDC);
GetWindowRect(hListBox, &rcLB);
showTextWidth = tm.tmAveCharWidth * 2 * N1 + 10; shift = showTextWidth - (rcLB.right - rcLB.left);
ShiftWindow(hDlg, 0, 0, shift);
ShiftWindow(IDC_LIST1, hDlg, 0, 0, shift);
ShiftWindow(IDC_GROUP_0, hDlg, 0, 0, shift);
for (int i = 0; i < sizeof(nCtrlId) / sizeof(int); ++i) ShiftWindow(nCtrlId[i], hDlg, shift);
ShiftWindow(IDC_STATIC_PHONE, hDlg, shift / 2);
}
Использование других элементов управления |
363 |
|
|
|
|
//==================================================================== BOOL CALLBACK ViewDlgProc(HWND hDlg, UINT uMsg, WPARAM wParam,
LPARAM lParam) {
static HWND hListBox, hComboFont;
static HWND hEditInfoAddr, hEditInfoBday; HWND hCtrl;
COLORREF editInfoColor; static HBRUSH hBrush;
int nItems; // количество элементов в списке int iCurItem; // текущий выбранный элемент списка BOOL dlgYes;
int i;
char buf[10];
switch (uMsg) { case WM_INITDIALOG:
hListBox = GetDlgItem(hDlg, IDC_LIST1); hComboFont = GetDlgItem(hDlg, IDC_COMBO_FONT); hEditInfoAddr = GetDlgItem(hDlg, IDC_INFO_ADDR); hEditInfoBday = GetDlgItem(hDlg, IDC_INFO_BDAY);
//Создание кисти для фона элементов управления editInfoColor = RGB(190, 255, 255);
hBrush = CreateSolidBrush(editInfoColor);
//Настройка формы диалога AdjustDialogSize(hDlg);
//Инициализация комбинированного списка
for (i = 0; i < sizeof(fontHeight) / sizeof(int); ++i) { sprintf(buf, "%d", fontHeight[i]); SendMessage(hComboFont, CB_ADDSTRING, 0, (LPARAM)buf);
}
SendDlgItemMessage(hDlg, IDC_COMBO_FONT, CB_SETCURSEL, iFont, 0);
// Чтение "базы данных" из файла и инициализация списка 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:
/* Текст из листинга 7.4 */ break;
case WM_COMMAND: |
|
switch (LOWORD(wParam)) |
|
{ |
|
case IDC_REC_ADD: |
|
/* Текст из листинга 7.4 */ |
продолжение |
|
364 Глава 7. Диалоговые окна
Листинг 7.5 (продолжение) break;
case IDC_REC_DELETE:
/* Текст из листинга 7.4 */ break;
case IDC_LIST1:
/* Текст из листинга 7.4 */ break;
case IDOK:
/* Текст из листинга 7.4 */ return TRUE;
case IDCANCEL: EndDialog(hDlg, FALSE); return TRUE;
case IDC_COMBO_FONT:
switch (HIWORD(wParam)) { case CBN_SELCHANGE:
// Выбираем используемый шрифт
iFont = SendMessage(hComboFont, CB_GETCURSEL, 0, 0); // Настройка формы диалога
AdjustDialogSize(hDlg); return TRUE;
}
break;
}
break;
}
return FALSE;
}
//==================================================================== BOOL CALLBACK AddRecDiaProc(HWND hDlg, UINT uMsg,
WPARAM wParam, LPARAM lParam)
{
/* Текст из листинга 7.4 */
}
//////////////////////////////////////////////////////////////////////
Обрабатывая уведомительное сообщение CBN_SELCHANGE, мы посылаем комби нированному списку сообщение CB_GETCURSEL, чтобы узнать, какой элемент спис ка был выбран пользователем. Индекс выбранного шрифта сохраняется в глобаль ной переменной iFont. После этого вызывается новая функция AdjustDialogSize, которая не использовалась в предыдущем приложении. Она выполняет всю ру тинную работу по настройке формы диалога. Во первых, функция устанавливает выбранный шрифт в окне списка hListBox. Во вторых, она изменяет размеры диа логового окна и окна списка hListBox с учетом метрик выбранного шрифта так, чтобы в окне списка отображались только первые два поля каждой записи. В тре тьих, она изменяет размещение по горизонтали некоторых элементов управле ния, чтобы их позиция согласовывалась с шириной окна списка.
Для выполнения описанной работы функция AdjustDialogSize пользуется услу гами новой перегруженной функции ShiftWindow. Прототипы функции ShiftWindow
Использование других элементов управления |
365 |
|
|
|
|
объявлены в файле KWndPlut.h, а реализация перегруженных версий функции оп ределена в файле KWndPlut.cpp.
Первая версия функции ShiftWindow с пятью параметрами предназначена для перемещения и изменения размеров окна верхнего уровня (top level window). Деск риптор окна hwnd передается в качестве первого параметра. В нашей программе эта версия функции вызывается для изменения размеров диалогового окна hDlg.
Вторая версия функции ShiftWindow с шестью параметрами предназначена для перемещения и изменения размеров дочернего окна. Первый параметр этой функ ции имеет тип HWND. Он принимает дескриптор дочернего окна. Второй параметр позволяет задавать дескриптор родительского окна. В реализации функции учи тывается, что функция GetWindowRect определяет позицию окна, выраженную в эк ранных координатах. В то же время функция MoveWindow работает с экранными координатами, если применяется к окну верхнего уровня, и с клиентскими ко ординатами, если применяется к дочернему окну. Необходимое преобразование экранных координат к клиентским координатам достигается вызовом функции ScreenToClient. Данная версия функции в этом приложении не используется, но может быть применена, например, для немодальных диалоговых окон.
Третья версия функции ShiftWindow предназначена для перемещения и изме нения размеров дочерних окон элементов управления. Она тоже принимает шесть параметров, но первый параметр имеет тип int. Первый параметр этой функции позволяет указывать идентификатор элемента управления, а второй параметр — дескриптор родительского окна.
На рис. 7.32 показан внешний вид работающей программы MyNotebook2, когда в окне Combo box выбран шрифт размером 24 логические единицы.
Рис. 7.32. Просмотр списка в приложении MyNotebook2. Установлен размер шрифта 24
Обратите внимание на то, что файлы KWndPlut.h, KWndPlut.cpp вы можете вклю чать и в другие проекты. В этом случае помимо класса KWnd в вашем распоряже нии оказывается также перегруженная функция ShiftWindow.
До сих пор мы изучали применение элементов управления, размещая их в мо дальном диалоговом окне. В принципе, подобным же образом они могут работать и в составе немодального диалогового окна. Однако создание немодального диа логового окна и работа с ним несколько отличаются от создания и обработки мо дального диалога.
366 |
Глава 7. Диалоговые окна |
|
|
Немодальный диалог
Немодальные диалоговые окна позволяют пользователю переключаться между диалоговым окном и окном, в котором оно было создано. Окна этого типа больше напоминают обычные всплывающие окна, которые могут создаваться программой. Немодальные диалоговые окна лучше использовать, когда пользователь работает с ними и с основным окном одновременно. Пожалуй, самые распространенные не модальные окна — это окна инструментов поиска Find и Replace, отображаемые про граммами обработки текстов. Пользователю может потребоваться оставить такой диалог в активном состоянии, если нужно выполнить несколько операций поиска
изамены. При отображении этого окна можно также редактировать документ, в котором выполняется поиск или замена.
Иногда немодальное диалоговое окно создается в момент старта приложения
иможет оставаться на экране до окончания работы приложения.
Различия между модальными и немодальными окнами диалога
Мы уже знаем, что модальные диалоговые окна создаются при помощи функции DialogBox. Эта функция возвращает управление только после закрытия диалогово го окна. Немодальные диалоговые окна создаются с помощью функции CreateDialog. Она принимает такие же параметры, что и функция DialogBox:
hDlgModeless = CreateDialog(hInst, MAKEINTRESOURCE(IDD_DLG), hWnd, DlgProc);
то есть функции передаются дескриптор экземпляра приложения, идентифика тор шаблона диалога, дескриптор родительского окна и адрес диалоговой проце дуры.
Различие состоит в том, что функция CreateDialog сразу возвращает дескриптор диалогового окна. Как правило, этот дескриптор хранится в глобальной переменной.
Определяя свойства шаблона диалогового окна, обязательно установите фла жок Visible на вкладке More Styles окна Dialog Properties. Если этот флажок сброшен, то для появления окна на экране потребуется после вызова функции CreateDialog дополнительно вызвать функцию ShowWindow:
ShowWindow(hDlgModeless, SW_SHOW);
При сброшенном флажке Visible и отсутствии указанного вызова функции ShowWindow немодальный диалог вообще не появится на экране. Модальное диа логовое окно менее притязательно: оно появляется на экране и при сброшенном флажке Visible.
В отличие от сообщений для модальных диалоговых окон, направляемых сис темой непосредственно менеджеру диалогового окна, сообщения для немодаль ного диалогового окна проходят через очередь сообщений программы. Поэтому цикл обработки сообщений в теле функции WinMain должен быть приведен к сле дующему виду:
while (GetMessage(&msg, NULL, 0, 0)) {
if (!IsDialogMessage(hModelessDlg, &msg)) { TranslateMessage(&msg); DispatchMessage(&msg);
}
}
Немодальный диалог |
367 |
|
|
|
|
Функция IsDialogMessage определяет, относится ли сообщение, сохраненное в переменной msg, к диалоговому окну hModelessDlg. Если да, то функция обраба тывает это сообщение, отправляя его диалоговой процедуре и возвращая значе ние TRUE. В ином случае функция ничего не делает с сообщением и возвращает значение FALSE.
Сообщения, переданные на обработку диалоговой процедуре, не должны об рабатываться функциями TranslateMessage и DispatchMessage. Это обеспечивает ус ловный оператор if, анализирующий код возврата функции IsDialogMessage. Заме тим, что если немодальное диалоговое окно еще не создано, то дескриптор hModelessDlg должен быть равен нулю и в этом случае функция IsDialogMessage тоже возвращает значение FALSE.
Для закрытия немодального диалогового окна вместо функции EndDialog вы зывается функция DestroyWindow. Если диалог закрывается, а приложение продол жает работать, то рекомендуется дескриптор hModelessDlg установить в нулевое значение.
Если немодальное диалоговое окно имеет заголовок, в котором размещается кнопка закрытия окна, то пользователь по привычке может попытаться закрыть диалог с помощью данной кнопки. Менеджер немодального диалогового окна не обрабатывает сообщение WM_CLOSE. Поэтому реакция приложения будет зависеть от решения программиста. Если вы считаете, что окно можно закрыть, то в диало говую процедуру нужно добавить следующий код:
case WM_CLOSE: DestroyWindow(hDlg); hModelessDlg = 0; break;
Это же сообщение будет обрабатываться в случае выбора команды Close (Alt+F4) в системном меню диалогового окна.
Пример использования немодального окна диалога
В приложении ModelessDlg, которое мы сейчас разработаем, используется немо дальное диалоговое окно, содержащее три полосы прокрутки. При помощи этих полос пользователь может изменять цвет фона в клиентской области основного окна.
Создайте новый проект типа Win32 Application с именем ModelessDlg. Скопируй те в папку проекта файлы KWndPlut.h, KWndPlut.cpp из папки проекта MyNotebook2, после чего добавьте их в состав нового проекта.
Включите в приложение ресурс диалогового окна c идентификатором IDD_
MODELESS и заголовком Öâåò ôîíà. На вкладке Styles окна Dialog Properties нужно выбрать стиль окна Child, а на вкладке More Styles установить флажок Visible. Ос тальные свойства окна оставьте со значениями по умолчанию.
Удалите с заготовки диалогового окна кнопки OK и Cancel.
Поместите на форму диалога надписи Red, Green и Blue, а под ними — элементы управления типа Vertical Scroll Bar (рис. 7.33).
Для полос прокрутки задайте, соответственно, идентификаторы IDC_RED, IDC_GREEN, IDC_BLUE. В окне свойств каждой полосы прокрутки должны быть ус тановлены флажки Visible и Tab stop.
368 |
Глава 7. Диалоговые окна |
|
|
Рис. 7.33. Размещение элементов управления на форме диалога IDD_MODELESS
Добавьте к проекту файл ModelessDlg.cpp, текст которого приведен в лис тинге 7.6.
Листинг 7.6. Проект ModelessDlg
//////////////////////////////////////////////////////////////////////
// ModelessDlg.cpp #include <windows.h>
#include "KWndPlut.h" #include "resource.h"
enum UserMsg { UM_CHANGE = WM_USER+1 };
HWND hModelessDlg;
RECT rcWork; // прямоугольник рабочей области
int rgb[3]; // интенсивность для R-, G-, B-компонентов цвета
BOOL CALLBACK ModelessDlgProc(HWND, UINT, WPARAM, LPARAM); void AdjustDlgPlace(HWND, HWND);
LRESULT CALLBACK WndProc(HWND, UINT, WPARAM, LPARAM); //==================================================================== int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance,
LPSTR lpCmdLine, int nCmdShow)
{
MSG msg;
KWnd mainWnd("ModelessDlg", hInstance, nCmdShow, WndProc,
NULL, 100, 100, 400, 300);
while (GetMessage(&msg, NULL, 0, 0)) {
if (!IsDialogMessage(hModelessDlg, &msg)) { TranslateMessage(&msg); DispatchMessage(&msg);
}
}
return msg.wParam;
}
//==================================================================== LRESULT CALLBACK WndProc(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
Немодальный диалог |
369 |
|
|
|
|
{
switch (uMsg) {
case WM_CREATE:
hModelessDlg = CreateDialog(GetModuleHandle(NULL), MAKEINTRESOURCE(IDD_MODELESS), hWnd, ModelessDlgProc);
AdjustDlgPlace(hWnd, hModelessDlg); break;
case WM_SIZE:
AdjustDlgPlace(hWnd, hModelessDlg); break;
case UM_CHANGE:
// Изменение цвета фона основного окна
SetClassLong(hWnd, GCL_HBRBACKGROUND, (LONG)CreateSolidBrush( RGB(rgb[0], rgb[1], rgb[2])));
InvalidateRect(hWnd, &rcWork, TRUE); break;
case WM_DESTROY: DestroyWindow(hModelessDlg); PostQuitMessage(0);
break;
default:
return DefWindowProc(hWnd, uMsg, wParam, lParam);
}
return 0;
}
//==================================================================== BOOL CALLBACK ModelessDlgProc(HWND hDlg, UINT uMsg, WPARAM wParam,
LPARAM lParam) {
HWND hCtrl;
int iCtrlID, iIndex;
HWND hRvalue = GetDlgItem(hDlg, IDC_RED);
HWND hGvalue = GetDlgItem(hDlg, IDC_GREEN);
HWND hBvalue = GetDlgItem(hDlg, IDC_BLUE);
switch (uMsg) { case WM_INITDIALOG:
SetScrollRange(hRvalue, SB_CTL, 0, 255, FALSE); SetScrollPos(hRvalue, SB_CTL, 0, FALSE);
SetScrollRange(hGvalue, SB_CTL, 0, 255, FALSE);
SetScrollPos(hGvalue, SB_CTL, 0, FALSE);
SetScrollRange(hBvalue, SB_CTL, 0, 255, FALSE); SetScrollPos(hBvalue, SB_CTL, 0, FALSE);
return TRUE;
case WM_VSCROLL:
hCtrl = (HWND)lParam;
iCtrlID = GetDlgCtrlID(hCtrl);
продолжение
370 |
Глава 7. Диалоговые окна |
|
|
Листинг 7.6 (продолжение) |
|
iIndex = iCtrlID - IDC_RED; |
|
switch(LOWORD(wParam)) { |
|
case SB_LINEUP: |
|
rgb[iIndex] = max(0, rgb[iIndex] - 1); |
|
break; |
|
case SB_LINEDOWN: |
|
rgb[iIndex] = min(255, rgb[iIndex] + 1); |
|
break; |
|
case SB_PAGEUP: |
|
rgb[iIndex] -= 15; |
|
break; |
|
case SB_PAGEDOWN: |
|
rgb[iIndex] += 15; |
|
break; |
|
case SB_THUMBTRACK: |
|
rgb[iIndex] = HIWORD(wParam); |
|
break; |
|
} |
|
SetScrollPos(hCtrl, SB_CTL, rgb[iIndex], TRUE); |
|
// Сообщение родительскому окну об изменении цвета фона SendMessage(GetParent(hDlg), UM_CHANGE, 0, 0);
break;
}
return FALSE;
}
//==================================================================== void AdjustDlgPlace(HWND hParent, HWND hDlg) {
RECT rcParent, rcDlg;
//Установка размеров окна hDlg GetClientRect(hParent, &rcParent); GetWindowRect(hDlg, &rcDlg);
int width = rcDlg.right - rcDlg.left; int height = rcDlg.bottom - rcDlg.top; int dH = rcParent.bottom - height; ShiftWindow(hDlg, hParent, 0, 0, 0, dH);
//Установка размеров рабочей области для окна hParent rcWork = rcParent;
rcWork.left += width;
}
//////////////////////////////////////////////////////////////////////
Немодальное диалоговое окно hModelessDlg с шаблоном IDD_MODELESS создает ся в оконной процедуре WndProc при помощи вызова функции CreateDialog, кото рый размещается в блоке обработки сообщения WM_CREATE.
После этого вызывается функция AdjustDlgPlace, которая регулирует высоту диалогового окна таким образом, чтобы она совпадала с высотой клиентской области главного окна. Для решения этой подзадачи вызывается вторая версия перегруженной функции ShiftWindow. Кроме того, функция AdjustDlgPlace вычис ляет прямоугольник рабочей области главного окна, к которой относится вся клиентская область, за исключением участка, закрытого диалоговым окном. Этот
