КАК вызывать и обрабатывать диалоговые окна?

До сих пор мы пользовались только готовыми диалоговыми окнами: сообщениями об ошибке, информационными сообщениями, предупреждениями и др. Их вызывала стандартная функция MessageDlg(), которую к этому моменту мы успели уже вызубрить, как "Отче наш". Всё остальное - графический вывод, информацию пользователя мы выводили в главное окно приложения. Это не всегда удобно, т.к. в крупных программах появляется постоянная необходимость в перерисовке главного окна. Кроме того, получать данные через главное окно и выводить результат туда же не всегда удаётся. Налицо дефицит свободного места.

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

Создадим собственное диалоговое окно, в которое пользователю будет вежливо предложено ввести данные. Пользователь вводит числа в поля ввода, нажимает кнопку, а программа сама подставит эти числа в теорему Пифгора и выдаст ответ на экран.

Хотите попробовать? Создайте пустой проект Win32 API. Добавьте в него исходник-шаблон первой программы.

Новые диалоговые окна, с которым мы будем работать, можно добавлять как новый ресурс приложения, через insert->Resource, а можно набирать их текст вручную. В файле RC для диалоговых окон припасено много тёплых слов.
Чем лучше набирать вручную? Вы знакомитесь с языком ресурсов и полностью контроллируете процесс. Имейте в виду, что редактор ресурсов предназначен в первую очередь, всё-таки для MFC приложений, поэтому в файл ресурсов будет автоматически включено помимо нужной информации, масса констант и директив препроцессора. Из-за этого он может некорректно работать с приложением Win32 API, вплоть до того, что вообще не вызываться. Эта тема слабых мест Microsoft довольно болезнена и редко обсуждается. Тем не менее, в любой книге по Win 32 API авторы приводят тексты RC файлов непохожие на те, что генерирует редактор ресурсов. Мы рассмотрим оба способа.

Выберите следующие пункты меню: Insert->Resource->Dialog->New для 6-й верссии. В 7-й версии это будет: Project->Add Resource->Dialog->New.

На экране появится пустая форма, в которую вы можете перетаскивать готовые элементы управления. Все они представлены в маленьком перемещаемом окошечке, напоминающем палитру художника. Подобно художнику, выбирающему из палитры нужные краски, вы сможете перетаскивать в окно необходимые элементы управления: кнопки, флажки, слайдеры, ползунки, поля ввода, текстовые метки и др.

Окно свойств диалоговых окон и элементов управления в 6-й версии Свойства диалогов и элементов управления в 7-й версии Потренеруйтесь в перетаскивании элементов управления, и вы узнаете среди них много "старых знакомых". Если же вы щёлкните правой кнопкой мыши по форме и в выберите пункт меню Properties, то увидите окно Dialog Properties (в 7-й версии, оно в правом нижнем углу экрна, но содержит все те же строчки).
В нём можно задать заголовок окна (Caption), идентификатор (IDD_DIALOG1 - по умолчанию), шрифт, меню, стиль и много чего ещё.

Вот что должно получиться:

Так будет выглядеть наше окно для ввода данных В этом окне мы видим две текстовые метки (static text), которые содержат надписи над полями ввода. Три кнопки - Считать, Очистить и Выход, а такде два поля ввода (Edit Box), в которые будут вводиться данные. Перетащите все эти элементы в форму и разместите так, как показано на рисунке. Знать, как они работают от вас пока не требуется. Наша задача - красиво всё расположить. Замёмся элементами static text. Поменяйте их содержимое с помощью окна Properties. Отредактируйте оба Edit Box. В окне Properties измените их идентификаторы на IDC_VALUE1 и IDC_VALUE2 соответственно. У каждого элемента, окно Properties своё, с разным набором параметров. Перетащите три кнопки и измените поля (ID) на: IDC_COUNT, IDC_CLEAR, IDC_EXIT.
Теперь вызовите Properties для самого окошка. Для этого надо снять выделение со всех элементов в окне. Поменяйте через Properties шрифт на "System", высотой 10. Также поменяйте заголовок (Caption) на Pifagor's Theoreme. Само окно будет называться IDD_PIFAGORUS. Всё! Окно готово.

Итак, файл ресурсов описывает и диалоговые окна. Все компоненты окна - кнопки, элементы управления и даже статический текст, имеют свои идентификаторы, координаты в окне и номера, как имеют их и элементы меню. Точно так же, как мы это делали для меню, мы должны присвоить каждому элементу число, включая и само диалоговое окно IDD_PIFAGORUS.

#define IDD_PIFAGORUS 101
#define IDC_VALUE1 1000
#define IDC_VALUE2 1001
#define ID_EXIT 1002
#define ID_COUNT 1003
#define ID_CLEAR 1004
#define IDC_STATIC -1

#define IDM_ENTER 1005
#define IDM_EXIT 1006

//Pifagorus.rc

IDD_PIFAGORUS DIALOG DISCARDABLE 0, 0, 250, 134 //Параметры диалогового окна
STYLE DS_MODALFRAME | WS_POPUP | WS_CAPTION | WS_SYSMENU //стили окна
CAPTION "Pifagor's Theoreme" //заголовок окна
FONT 10, "System" //шрифт
{
DEFPUSHBUTTON "Считать",ID_COUNT,40,100,50,14 //Тип кнопки, надпись на ней, идентификатор, её координаты
PUSHBUTTON "Очистить",ID_CLEAR,104,100,50,14
LTEXT "Введите первое число": ",IDC_STATIC,13,15,80,8 //Статический текст
EDITTEXT IDC_VALUE1,13,26,217,14,ES_AUTOHSCROLL //поле ввода
LTEXT "Введите второе число:",IDC_STATIC,15,47,78,8
EDITTEXT IDC_VALUE2,14,58,217,14,ES_AUTOHSCROLL
PUSHBUTTON "Выход",ID_EXIT,163,100,50,14
}

//И небольшое такое меню

PifagrorusMenu MENU
{
POPUP "&Файл"
{
MENUITEM "&Ввод данных...", IDM_ENTER
MENUITEM "Вы&ход", IDM_EXIT
}
}


Директивы #define можно включать как в файл RC, так и в исходный текст программы для того, чтобы не создавать третий файл - заголовочный, который обычно называют resource.h. Вы можете в нём указать все ваши #define и включить на него ссылку как в файле RC, так и в файле CPP:

#include "resource.h"

Итак, меню создано. Создано и диалоговое окно. Они появились во вкладке ресурсов:


Во вкладке "Ресурсы" мы имеем меню и новое окно


Теперь поговорим об особенностях работы с диалоговыми окнами. Хотя у нас есть меню и есть уже готовое окно, вызвать это окно из программы мы пока не умеем.


КАК вызвать с помощью строчки меню диалоговое окно?

Опишем поэтапно, после чего я приведу уже весь текст программы.

1. Для каждого окна нужная своя функция окна, поэтому в программу помимо WndProc - функции главного окна, придётся включить PifProc() - функцию диалогового окна. Аргументы у неё будут такие же, как и у WndProc:

BOOL CALLBACK PifProc(HWND, UINT, WPARAM, LPARAM);

2. В функции окна будет обработка по меньшей мере двух сообщеий: WM_INITDIALOG - для главного окна мы не обрабатывали это сообщение, и сообщение WM_COMMAND, аналогичное такому же сообщению главного окна. Оно будет обрабатывать сообщения элементов управления диалога. Вот как это будет выглядеть:

//Функция диалогового окна
BOOL CALLBACK PifProc(HWND hdlg, UINT messg, WPARAM wParam, LPARAM lParam)
{
//Цикл обработки сообщений
switch (messg)
{
case WM_INITDIALOG: //инициализация диалога
return TRUE;

case WM_COMMAND:
switch(LOWORD(wParam)) {

case ID_COUNT: //Нажатие кнопки "Считать"

break;

case ID_CLEAR: //Нажатие кнопки "Очистить"

break;

case ID_EXIT: //Нажатие кнопки "Выход"
EndDialog(hdlg, LOWORD(wParam));
return TRUE;
break;

}
break;

}

}


3. В сообщении WM_COMMAND главного окна надо прописать выхов диалога из меню.

case WM_COMMAND:
switch(LOWORD(wParam)) {

case IDM_ENTER:
DialogBox(hInst, (LPCTSTR)IDD_PIFAGORUS, hWnd, (DLGPROC)PifProc);
break;


Функция DialogBox() выводит окно на экран. В этой функции мы указываем идентификатор приложения hInst, идентификатор диалогового окна IDD_PIFAGORUS (в большой программе может быть десяток диалогов, и чтобы вызвать нужный, надо знать его идентификатор). Далее мы указываем идентфикатор окна-родителя hWnd и функцию, которая будет обрабатывать все сообщения этого окна PifProc().

Окно не исчезнет до тех пор, пока не будет закрыто пользователем и не получит сообщение WM_QUIT. Сообщение выхода генерируется функцией EndDialog(), которая закрывает диалоговое окно.

Теперь, когда понятен механизм, рассмотрим всю программу целиком. Ничего нового в ней не будет.

//resource.h
#define IDD_PIFAGORUS 101
#define IDC_VALUE1 1000
#define IDC_VALUE2 1001
#define ID_EXIT 1002
#define ID_COUNT 1003
#define ID_CLEAR 1004
#define IDC_STATIC -1

#define IDM_ENTER 1005
#define IDM_EXIT 1006

#include <windows.h>
#include <stdio.h>
#include <math.h>

#include "resource.h"

//Прототип функции главного окна
LRESULT CALLBACK WndProc(HWND, UINT, WPARAM, LPARAM);
//Прототип функции диалогового окна
BOOL CALLBACK PifProc(HWND, UINT, WPARAM, LPARAM);

char szProgName[]="Имя программы";
char szMenu[]="PifMenu";
char szMessage[]="Выберите нужный пункт меню";

double a,b,c; //Переменные для расчётов
char str[80]="Строка текста";

int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance,
LPSTR lpszCmdLine, int nCmdShow)
{
HWND hWnd;
MSG lpMsg;


WNDCLASS w; //создаём экземпляр структуры WNDCLASS
//И начинаем её заполнять
w.lpszClassName=szProgName;
w.hInstance=hInstance;
w.lpfnWndProc=WndProc;
w.hCursor=LoadCursor(NULL, IDC_ARROW);
w.hIcon=LoadIcon(NULL, IDI_APPLICATION);
w.lpszMenuName=szMenu;
w.hbrBackground=(HBRUSH)CreateSolidBrush(RGB(128,0,128)); //цвет фона окна
w.style=CS_HREDRAW|CS_VREDRAW; //стиль - перерисовываемое по х и по у
w.cbClsExtra=0;
w.cbWndExtra=0;

//Если не удалось зарегистрировать класс окна - выходим
if(!RegisterClass(&w))
return 0;

//Создадим окно в памяти, заполнив аргументы CreateWindow
hWnd=CreateWindow(szProgName,
"Теорема Пифагора",
WS_OVERLAPPEDWINDOW,
100,
100,
500,
400,
(HWND)NULL,
(HMENU)NULL,
hInstance,
(LPSTR)NULL);

//Выводим окно из памяти на экран
ShowWindow(hWnd, nCmdShow);
//Обновим содержимое окна
UpdateWindow(hWnd);

while(GetMessage(&lpMsg, NULL, 0, 0)) {
TranslateMessage(&lpMsg);
DispatchMessage(&lpMsg);
}
return(lpMsg.wParam);
}

//Функция диалогового окна - новинка!
BOOL CALLBACK PifProc(HWND hdlg, UINT messg, WPARAM wParam, LPARAM lParam)
{
switch (messg)
{
case WM_INITDIALOG:
return TRUE;

case WM_COMMAND:
switch(LOWORD(wParam)) {

case ID_COUNT: //Кнопка "Cчитать"
GetDlgItemText(hdlg, IDC_VALUE1, str, 40);
a=atof(str);
GetDlgItemText(hdlg, IDC_VALUE2, str, 40);
b=atof(str);
c=sqrt(a*a+b*b);
gcvt(c, 10, str);
MessageBox(hdlg, str, "Ответ: ", 0);
break;

//Очистка полей ввода
case ID_CLEAR:
SetDlgItemText(hdlg, IDC_VALUE1, "");
SetDlgItemText(hdlg, IDC_VALUE2, "");
break;

//Закрытие окна
case ID_EXIT:
EndDialog(hdlg, LOWORD(wParam));
return TRUE;
break;

}


break;

case WM_QUIT:
DestroyWindow(hdlg);
break;


default: return false;
}

}

//Функция главного окна
LRESULT CALLBACK WndProc(HWND hWnd, UINT messg,
WPARAM wParam, LPARAM lParam)
{
HDC hdc; //создаём контекст устройства
static HINSTANCE hInst;

PAINTSTRUCT ps; //создаём экземпляр структуры графического вывода

//Цикл обработки сообщений
switch(messg)
{
case WM_COMMAND:
switch(LOWORD(wParam)){

case IDM_ENTER:
DialogBox(hInst, (LPCTSTR)IDD_PIFAGORUS, hWnd, PifProc);
break;

//Выход из программы
case IDM_EXIT:
DestroyWindow(hWnd);
break;

}
break;

//Вывод строки на экран
case WM_PAINT:
hdc=BeginPaint(hWnd, &ps);
SetTextColor(hdc, RGB(255,255,0));
SetBkMode(hdc, TRANSPARENT);
TextOut(hdc, 10,10, szMessage, strlen(szMessage));
ValidateRect(hWnd, NULL);
EndPaint(hWnd, &ps);
break;

//Завершение работы окна
case WM_DESTROY:
PostQuitMessage(0);
break;

default:
return(DefWindowProc(hWnd, messg, wParam, lParam));
}
return 0;
}

Как видите, даже небольшая программа на API приводит к разрастанию исходного текста. За это многие не любят API и предпочитают библиотеки классов MFC и VCL. До классов мы ещё доберёмся, а пока поразмышляем, как работает программа.

Как создавать дилаоги мы уже разобрались. Как вызывать их из меню, вроде бы тоже. Мы написали для диалогового окна свою функцию обработки сообщений. Теперь самое главное понять, как программа узнала что мы ввели в поля ввода.

Поэтому для нас представляют интерес функции GetDlgItemText() и SetDlgItemText().

Функция GetDlgItemText получает текст из любого элемента управления. Причём ей совсем неважно кнопка это, поле ввода или статическая метка. Главное - указать идентификатор. Синтаксис у неё такой:

UINT GetDlgItemText(HWND hdlg, int IDC_VALUE, LPSTR str, int maxcol);

hdlg - идентификатор окна диалога
IDC_VALUE - идентификатор поля ввода, из которого получаем данные
str - строка, в которую помещаем текст
maxcol - длина строки (массива символов)

Функция хороша тем, что позволяет считывать любые данные из поля ввода - строки, целые и дробные числа. Изначально данные будут представлены, как строки, но используя функции конвертирования всегда можно преобразовать их в целые и дробные числа. Для ввода целых чисел правда есть ещё функция GetDlgItemInt(), но в математических программах она используется редко.

Функции конвертирования известны: itoa, atoi, atof, gcvt... В Windows появляются дополнительные функции, которые мы рассмотрим в разделе "Строки".

Обратная по значению функция SetDlgItemText(), которая может записать в поле ввода любую строку. Это может быть ответ решения, значение по умолчанию, фраза-приглашение и др. Синтаксис у неё аналогичен.

SetDlgItemText(HWND hdlg, int IDC_EDIT, LPSTR str);

Кстати говоря, с помощью SetDlgItemText вы сможете менять текст и на кнопках и в текстовых метках. Главное - указать правильно идентификатор.

Вы видите, какая громоздкая получается программа, когда в ней есть диалоговые окна. И функцию свою этому окну надо создавать и свой цикл обработки сообщений. Это очень неудобно, а самое главное (действительно, кого интересуют неудобства какого-то программиста) это замедляет скорость создания программ. В условиях современного рынка программы нужно создавать быстро. Слова быстро и API-программа почти несовместимы.

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

Задание 1:
Добавьте к этой программе окно "О программе", в котором будет информация о вас, как о создателе. Пусть окно это вызывается из пункта меню "Помощь"-"О программе". В окне можно разместить свою фотографию, переместив на него компонент Picture из редактора ресурсов. Разобраться с этим компонентом я предлагаю вам самим.

Задание 2: Добавьте в проект ещё одно диалоговое окно IDD_USERNAME, содержащее поле ввода IDC_NAME, над ним статический текст: "Введите своё имя:" (IDC_NAME) и две кнопки: ID_OK, ID_EXIT. По нажатию Ок, функция получает имя пользователя из поля ввода и выводит сообщение (функция MessageBox с полем MB_ICONINFORMATION), в котором пишет: "Здравствуй"+Имя. Если имя не введено, выводится сообщение с полем MB_ICONSTOP, в котором говорится, что вы не ввели своё имя. По нажатию кнопки Еxit, окно закрывается.

Набор стандартных элементов управления Задание 3: Ещё немного математики. Вычисляем формулу квадратного уравнения: a*x*x+b*x+c=0. Вводим через поля ввода a,b,c. Программа считает x1, x2, ответ выводится на экран, причём надо предусмотреть случаи, когда дискриминант < 0 и = 0. Вот математика этой задачи:

a*x*x+b*x+c=0

d=b*b-4*a*c. Если d=0, то х будет всего один, если d<0, то решений нет.

x1=-b+sqrt(b*b-4*a*c)/2*a
x2=-b-sqrt(b*b-4*a*c)/2*a

Часто техническому человеку приходится заниматься кое-какими вычислениями в этой жизни. Как правило, многие формулы приходится использовать снова и снова - например расчёт делителей напряжения, равнодействующей сил или закон Ома. Чтобы облегчить себе жизнь, вы можете написать программу-уникум, в которой есть диалоговые окна для всех вычислений, встречающихся в вашей жизни. Это будет хорошей практикой!

И ещё. В дальнейшем, когда мы будем проходить элементы управления пользовательского интерфейса, я иногда буду давать вам не исходники целиком, а только функции окон. Вам нужно будет самим прописывать в пункты меню вызов диалогового окна и вставлять функцию этого окна. Так вы быстрее запомните, как добавлять в проект новые окна. Это будет хорошим подспорьем к дальнейшей вашей деятельности. Когда вам нужно будет вставить в проект новое окно, это будет вызывать у вас не зубную боль, а радость и ликование ещё раз доказать себе свой профессионализм. Поэтому ещё раз настоятельно рекомендую вам выполнять все домашние задания.

Назад Главная Вперёд