On-Line Библиотека www.XServer.ru - учебники, книги, статьи, документация, нормативная литература.
       Главная         В избранное         Контакты        Карта сайта   
    Навигация XServer.ru








 

3. Диалоговые панели

3.1. Создание диалоговой панели

3.2. Сообщения для органов управления

3.3. Немодальные диалоговые панели

3.4. Функция MessageBox

В предыдущей главе мы научили вас создавать в главном окне приложения различные органы управления, такие как кнопки, списки, полосы просмотра и т. п. Однако обычно для объединения органов управления используются временные (pop-up) окна, созданные на базе предопределенного внутри Windows класса окон - класса диалоговых панелей (dialоg window class). На поверхности такого окна располагаются дочерние окна - органы управления. Функция окна для класса диалоговых панелей, определенная в Windows, выполняет практически всю работу, необходимую для организации взаимодействия органов управления с приложением.

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

Диалоговые панели значительно упрощают использование органов управления, так как функция окна, соответствующая классу диалоговых панелей и расположенная внутри Windows, обеспечивает как взаимодействие органов управления между собой, так и их взаимодействие с приложением. В частности, эта функция обеспечивает передачу фокуса ввода от одного органа управления к другому при помощи клавиши <Tab> и клавиш перемещения курсора <Up> и <Down>, выполняет обработку сообщений от клавиш <Enter> и <Esc>.

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

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

DIALOG_BOX DIALOG 25, 34, 152, 67
STYLE DS_MODALFRAME | WS_POPUP | WS_CAPTION | WS_SYSMENU
CAPTION "Приложение DIALOG"
BEGIN
  CTEXT "Microsoft Windows Application\n"
    "Приложение DIALOG\n (C) Frolov A.V., 1994",
    -1, 28, 9, 117, 26, WS_CHILD | WS_VISIBLE | WS_GROUP
  ICON "APPICON", -1, 6, 14, 16, 16, WS_CHILD | WS_VISIBLE
  DEFPUSHBUTTON "OK", IDOK, 56, 43, 36, 14,
    WS_CHILD | WS_VISIBLE | WS_TABSTOP
END

Не останавливаясь пока на деталях, отметим, что в приведенном выше описании определен шаблон диалоговой панели с идентификатором DIALOG_BOX (см. первую строку). Расположение и размеры диалоговой панели определяются цифрами, стоящими после оператора DIALOG.

Вторая строка описания (оператор STYLE) предназначена для определения стиля временного окна, на поверхности которого будут расположены органы управления.

Далее следует описание заголовка диалоговой панели (оператор CAPTION).

Между строками BEGIN и END находится описание органов управления и пиктограмм. В нашем случае это статический орган управления (оператор CTEXT), пиктограмма (ICON) и кнопка (DEFPUSHBUTTON). Для каждого органа управления приводится вся необходимая информация - заголовок, стиль, расположение и размеры.

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

Второй способ размещения органов управления на поверхности диалоговой панели предполагает использование специального редактора диалогов, такого как Borland Resource Workshop или Microsoft Dialog Editor. Эти редакторы позволяют нарисовать диалоговую панель и сохранить ее текстовое описание в файле ресурсов приложения.

На рис. 3.1 показан процесс создания диалоговой панели, текстовое описание шаблона которой мы только что привели. С помощью мыши вы можете изменять размер и расположение диалоговой панели, размещать на ее поверхности органы управления, выбирая их из меню или из окна "Tools". Размеры органов управления можно изменять мышью, выделяя органы управления и перемещая их границы. При использовании специальных редакторов диалога у вас нет необходимости задавать для расположения и размеров органов управления численные значения.

Рис. 3.1. Создание диалоговой панели при помощи Borland Resource Workshop

Такой подход в создании приложений носит зачатки визуального программирования, когда внешний вид и поведение приложения определяется с помощью специальных графических средств проектирования без традиционного программирования на каком-либо алгоритмическом языке. Визуальное программирование реализовано полностью в таких программных продуктах, как Borland Object Vision или Microsoft FoxPro for Windows 2.5. Специальные средства проектирования приложений Windows позволяют вам не только нарисовать диалоговые панели, но и "привязать", например, поля, предназначенные для ввода и отображения текста, к полям файлов баз данных. Вы можете создавать полноценные приложения Windows, которые работают с базами данных, не написав ни строчки исходного текста. Однако в виде "компенсации" за значительное упрощение процесса разработки вы можете получить снижение скорости работы приложения. Это связано с тем что универсальные средства работают, как правило, медленнее специализированных. Однако вернемся к диалоговым панелям, так как визуальное программирование заслуживает отдельного рассмотрения.

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

В операционной системе Windows версии 3.1 появились встроенные диалоговые панели, реализованные в виде библиотеки динамической загрузки commdlg.dll. Приложения могут вызывать стандартные диалоговые панели для работы с файлами (мы это делали в наших приложениях, см. например приложение OEM2ANSI), для выбора цветов и шрифтов, для работы с принтерами и текстовыми строками (поиск и замена строк в текстовых редакторах и текстовых процессорах).

Хорошо знакомая вам функция MessageBox использует встроенные диалоговые панели, в которых могут находиться пиктограмма, статический орган управления (для отображения текста), а также одна или несколько кнопок.

3.1. Создание диалоговой панели

Диалоговая панель обычно представляет собой временное (pop-up) окно, хотя допустимо использовать и перекрывающиеся (overlapped) окна. Для создания диалоговой панели вам не требуется вызывать функцию CreateWindow, так как в программном интерфейсе Windows определены функции, специально предназначенные для создания диалоговых панелей.

Разумеется, перед вызовом этих функций необходимо создать шаблон диалоговой панели. Это можно сделать, использовав один из описанных выше способов.

Помимо шаблона, перед созданием диалоговой панели вам следует определить специальную функцию диалога, в которую будут поступать сообщения от функции окна диалоговой панели (которые, в свою очередь, поступают туда от органов управления). Функция диалога похожа на функцию таймера (получающую сообщения от таймера). Так же, как и функция таймера (а также функция окна), функция диалога является функцией обратного вызова и должна быть описана с использованием ключевого слова _export. Вместо этого имя функции обратного вызова можно занести в раздел EXPORTS файла определения модуля.

Итак, для создания диалоговой панели вы должны предпринять следующие действия:

создать шаблон диалога;

определить функцию диалога;

вызвать одну из функций создания диалога.

Создание шаблона диалога

Для создания шаблона диалога лучше всего воспользоваться редактором диалога Borland Resource Workshop или Microsoft Dialog Editor. Опишем процесс создания диалога с помощью редактора диалога, входящего в состав приложения Resource Workshop.

Использование Resource Workshop

Запустите Resource Workshop и из меню "File" выберите строку "New project...". В появившейся диалоговой панели установите переключатель ".RC" и нажмите кнопку "OK". На экране появится окно, озаглавленное "untitled.rc". Это окно не содержит ни одной строки, так как мы создали новый файл ресурсов (вы можете создать шаблон диалоговой панели в уже существующем файле ресурсов, если из меню "File" выберите строку "Open project...").

Затем из меню "Resource" выберите строку "New...". На экране появится диалоговая панель "New Resource". В списке "Resource Type" выберите строку "DIALOG" и нажмите кнопку "OK". После этого в главном окне приложения появятся окна, озаглавленные как "DIALOG_1", "Caption", "Alignment", "Tools" (рис. 3.1).

Окно "DIALOG_1" - это создаваемая вами диалоговая панель. Заголовок окна можно изменить. Для этого сначала сделайте щелчок мышью по заголовку окна, а затем в окне "Caption" введите новое имя для заголовка и нажмите клавишу <Enter>.

Установите нужный вам размер диалоговой панели, изменив размер окна "DIALOG_1". Это можно сделать, перемещая границы или углы окна мышью.

Далее следует создать и расположить на поверхности диалоговой панели все необходимые органы управления. Для создания органа управления (а также изменения стиля диалоговой панели) можно воспользоваться меню "Control".

Выберите из этого меню строку "Style...". На экране появится диалоговая панель "Window style" (рис. 3.2).

Рис. 3.2. Диалоговая панель "Window style"

В этой диалоговой панели вы можете изменить заголовок создаваемой диалоговой панели (поле "Caption"), класс окна ("Class"), который будет использоваться для диалоговой панели и даже подключить к диалоговой панели меню (поле "Menu"). Группа переключателей "Dialog style" позволяет изменить стиль диалоговой панели, добавив к ней системное меню ("System menu"), толстую рамку ("Thick frame"), вертикальную полосу просмотра ("Vertical scroll"), горизонтальную полосу просмотра ("Horizontal scroll"), кнопки минимизации ("Minimize Box") и максимизации ("Maximize box"), а также определить другие стили. Вы можете выбрать тип окна ("Window type"), стиль рамки ("Frame style") и шрифт ("Font..."). Однако пока мы рекомендуем вам использовать значения, установленные по умолчанию и показанные на рис. 3.2, изменив только заголовок диалоговой панели.

Остальные строки меню "Control" предназначены для создания органов управления.

Выберите из этого меню строку "Push button". Форма курсора при этом изменится. Курсор примет вид маленького крестика с нарисованной около него кнопкой "OK". Поместите курсор в то место диалоговой панели, в котором вам нужно создать кнопку и сделайте щелчок левой клавишей мыши. На поверхности диалоговой панели появится кнопка с надписью "Text". Размеры и расположение только что созданной кнопки можно изменять при помощи мыши.

Если сделать по кнопке двойной щелчок левой клавишей мыши, на экране появится диалоговая панель "Button styles", с помощью которой можно изменить атрибуты кнопки (рис. 3.3).

Рис. 3.3. Диалоговая панель "Button styles"

При помощи этой панели вы можете изменить текст, написанный на кнопке (поле "Caption"), выбрать стиль кнопки (группа переключателей "Button type"), а также определить другие атрибуты. Каждый орган управления, расположенный в диалоговой панели, должен иметь свой идентификатор. Этот идентификатор можно определить в поле "Control ID" в цифровом или символическом виде (в последнем случае файл описания ресурсов должен включать в себя файл с описанием символических имен идентификаторов органов управления).

Создав все органы управления и определив их атрибуты, сохраните шаблон диалоговой панели в файле описания ресурсов, для чего из меню "File" выберите строку "Save project" или "Save file as...".

Далее вы можете вносить изменения в шаблон при помощи любого текстового редактора. Можно также отредактировать готовый шаблон, запустив приложение Resource Workshop и загрузив шаблон, выбрав из меню "File" строку "Open project...".

Использование текстового редактора

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

Описание шаблона

Описание шаблона имеет следующий вид:

nameID DIALOG [load] [mem] x, y, width, height
CAPTION "Заголовок диалоговой панели"
STYLE   Стиль 
BEGIN      . . .
  . . .
  . . .
END

В этом описании nameID используется для идентификации шаблона диалоговой панели и может указываться либо в виде текстовой строки, либо в виде числа от 1 до 65535.

Параметр load - необязательный. Он используется для определения момента загрузки диалоговой панели в память. Если этот параметр указан как PRELOAD, диалоговая панель загружается в память сразу после запуска приложения. По умолчанию используется значение LOADONCALL, при использовании которого загрузка шаблона в память происходит только при отображении диалоговой панели.

Параметр mem также необязательный. Он влияет на тип выделяемой для хранения шаблона памяти и может указываться как FIXED (ресурс всегда остается в фиксированной области памяти), MOVEABLE (при необходимости ресурс может перемещаться в памяти, это значение используется по умолчанию) или DISCARDABLE (если ресурс больше не нужен, занимаемая им память может быть использована для других задач). Значение DISCARDABLE может использоваться вместе со значением MOVEABLE.

Параметры x и y определяют, соответственно, x-координату левой границы диалоговой панели и y-координату верхней стороны диалоговой панели. Координаты могут принимать значения от 0 до 65535.

Параметры width и height определяют, соответственно, ширину и высоту диалоговой панели. Эти параметры могут находиться в диапазоне от 1 до 65535.

Сделаем замечание относительно единиц измерения, принятых при описании размеров диалоговых панелей и органов управления, расположенных на поверхности диалоговых панелей.

Для описания шаблонов диалоговых панелей используется специальная координатная система, в которой размер единицы длины в пикселах зависит от размера системного шрифта. Такая координатная система позволяет создавать диалоговые панели, размер которых не зависит от режима работы видеоадаптера. Это возможно благодаря тому, что размер системного шрифта в пикселах зависит от разрешения - в режиме 800х600 точек размеры системного шрифта больше, чем, например, в режиме 640х480 точек.

Одна единица длины в координатной системе, используемой при описании ширины элементов шаблонов диалоговых панелей, составляет четверть средней ширины символов системного шрифта, а при описании высоты (или вертикальных размеров) - восьмую часть высоты символов системного шрифта. Так как высота символов системного шрифта примерно в два раза больше средней ширины этих символов, единица длины в этой системе координат имеет одинаковый размер по вертикали и горизонтали. Эта единица называется диалоговая единица (dialog unit).

Размер единицы измерения dialog unit можно получить при помощи функции GetDialogBaseUnits:

DWORD WINAPI GetDialogBaseUnits(void);

Младшее слово возвращаемого значения представляет собой ширину в пикселях диалоговой единицы длины, старшее - высоту.

Оператор CAPTION предназначен для определения заголовка диалоговой панели.

Оператор STYLE используется для назначения стиля окну диалоговой панели. В качестве стиля вы можете использовать комбинацию символических имен, определенных в файле windows.h и имеющих префикс WS_. Специально для диалоговых панелей в этом файле определены несколько констант с префиксом DS_.

Имя константыОписание
DS_LOCALEDITПри использовании этого стиля редакторы текста, созданные в диалоговой панели, будут использовать память в сегменте данных приложения. В этом случае можно использовать сообщения EM_GETHANDLE и EM_SETHANDLE
DS_MODALFRAMEСоздается модальная диалоговая панель (см. ниже)
DS_NOIDLEMSGЕсли этот стиль не указан, когда диалоговая панель переходит в видимое состояние (отображается), Windows посылает родительскому окну (создавшему диалоговую панель), сообщение WM_ENTERIDLE
DS_SYSMODALСоздается системная модальная диалоговая панель

Для создания стандартной диалоговой панели используются стили WS_POPUP, WS_BORDER, WS_SYSMENU, WS_CAPTION, DS_MODALFRAME. Если нужно создать диалоговую панель с рамкой, но без заголовка, используется стиль WS_DLGFRAME.

Отметим, что диалоговые панели бывают трех типов: модальные, системные модальные, и немодальные.

При выводе на экран модальной диалоговой панели работа приложения приостанавливается. Функции главного окна приложения и всех дочерних окон перестают получать сообщения от мыши и клавиатуры. Все эти сообщения попадают в временное (pop-up) окно диалоговой панели. Когда работа пользователя с диалоговой панелью будет завершена, главное окно приложения и его дочерние окна будут разблокированы. Заметьте, что диалоговая панель не должна создаваться как дочернее окно - в этом случае при активизации диалоговой панели она будет заблокирована наряду с остальными дочерними окнами и приложение "зависнет".

Модальная диалоговая панель, тем не менее, позволяет пользователю переключиться на работу с другими приложениями. Если вам требуется запретить такое переключение, используйте системные модальные диалоговые панели. Типичным примером такой панели является панель "Exit Windows", появляющаяся на экране перед завершением работы Windows. Пока эта панель находится на экране, вы не можете переключиться на работу с другими приложениями.

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

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

Помимо операторов STYLE и CAPTION, описание шаблона может содержать операторы CLASS и FONT.

Оператор CLASS используется в тех случаях, когда диалоговая панель использует свой собственный класс, а не тот, который определен для диалоговых панелей операционной системой Windows:

CLASS "PrivateDlgClass"

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

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

Оператор FONT позволяет задать шрифт, с использованием которого Windows будет писать текст в диалоговой панели:

FONT 10, "MS Serif"

Первый параметр оператора FONT указывает размер шрифта в пунктах, второй - название шрифта, определенного в файле win.ini. Отметим, что единственный шрифт, присутствие которого гарантируется - это системный шрифт. Все остальные шрифты можно отключить при помощи приложения Control Panel. Указывая шрифт, отличный от системного, вы не можете быть уверены, что этот шрифт будет установлен у пользователя. В этом случае перед выводом диалоговой панели имеет смысл убедиться в том, что в системе зарегистрирован требуемый шрифт (о том, как это сделать, вы узнаете позже). Если нужный шрифт не установлен, можно выдать предупреждающее сообщение.

Описание всех органов управления, расположенных на поверхности диалоговой панели, должно находиться между строками BEGIN и END.

Для описания органов управления используются три формата строк.

Первый формат можно использовать для всех органов управления, кроме списков, редакторов текста и полосы просмотра:

CtlType "Текст", ID, x, y, width, height [,style]

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

Параметр "Текст" определяет текст, который будет написан на органе управления.

Параметр ID - идентификатор органа управления. Этот идентификатор передается вместе с сообщением WM_CONTROL.

Параметры x и y определяют координаты органа управления относительно левого верхнего угла диалоговой панели. Используется единица длины dialog unit.

Параметры width и height определяют, соответственно, ширину и высоту органа управления в единицах длины dialog unit.

Параметр style определяет стиль органа управления (необязательный параметр). Это тот самый стиль, который указывается при вызове функции CreateWindow.

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

Обозначение органа управленияКласс окна Описание и стиль, используемый по умолчанию
CHECHBOXbuttonПереключатель в виде прямоугольника BS_CHECKBOX, WS_TABSTOP
CTEXTstaticСтрока текста, выровненная по центру SS_CENTER, WS_GROUP
DEFPUSHBUTTONbuttonКнопка, выбираемая в диалоговой панели по умолчанию BS_DEFPUSHBUTTON, WS_TABSTOP
GROUPBOXbuttonПрямоугольник, объединяющий группу органов управления BS_GROUPBOX
ICONstaticПиктограммаSS_ICON Параметры width, height и style можно не указывать
LTEXTstaticСтрока текста, выровненная по левой границе органа управления SS_LEFT, WS_GROUP
PUSHBUTTONbuttonКнопкаBS_PUSHBUTTON, WS_TABSTOP
RADIOBUTTONbuttonПереключатель в виде кружка (радиопереключатель) BS_RADIOBUTTON, WS_TABSTOP
RTEXTstaticСтрока текста, выровненная по правой границе органа управления SS_RIGHT, WS_GROUP

Стили WS_TABSTOP и WS_GROUP будут описаны позже.

Второй формат используется для описания списков, редакторов текста и полос просмотра:

CtlType ID, x, y, width, height [,style]

В этом формате нет параметра "Текст", остальные параметры используются так же, как и в первом формате.

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

Обозначение органа управленияКласс окна Описание и стиль, используемый по умолчанию
COMBOBOXcomboboxСписок с окном редактирования CBS_SIMPLE, WS_TABSTOP
LISTBOXlistboxСписок LBS_NOTIFY, WS_BORDER
EDITTEXeditРедактор текста ES_LEFT, WS_BORDER, WS_TABSTOP
SCROLLBARSscrollbarПолоса просмотраSBS_HORZ

Третий формат описания органов управления наиболее универсальный:

CONTROL "Текст", ID, class, style, x, y, width, height

Этот формат позволяет описать орган управления, принадлежащий классу class, который указывается в виде строки символов. Вы можете использовать третий формат для описания предопределенных классов органов управления, таких как "button", "combobox", "edit", "listbox", "scrollbar", "static". Данный формат описания можно использовать для любых органов управления.

Функция диалога

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

BOOL CALLBACK _export
DlgProc (HWND hdlg, UINT msg, WPARAM wParam, LPARAM lParam);

Вы можете выбрать для функции диалога любое имя.

Параметры функции диалога напоминают параметры функции окна. Все они, за исключением первого, имеют аналогичное назначение. Через первый параметр функции диалога передается идентификатор диалога hdlg, а не идентификатор окна hwnd.

В отличие от функции окна, функция диалога не должна вызывать функцию DefWindowProc для тех сообщений, которые она не желает обрабатывать. Если функция диалога обрабатывает сообщение, она должна вернуть значение TRUE, а если нет - FALSE.

Функция диалога не обрабатывает сообщения WM_CREATE, WM_PAINT, WM_DESTROY. При инициализации диалога в функцию диалога вместо сообщения WM_CREATE передается сообщение WM_INITDIALOG. Как правило, функция диалога всегда обрабатывает сообщения WM_INITDIALOG и WM_COMMAND.

Сообщение WM_INITDIALOG использует параметры wParam и lParam.

Параметр wParam содержит идентификатор органа управления, который первым получит фокус ввода после отображения диалоговой панели. Это первый орган управления, описанный в шаблоне диалога со стилем WM_TABSTOP. Параметр lParam содержит значение, передаваемое приложением при создании диалоговой панели.

Если в ответ на сообщение WM_INITDIALOG функция диалога возвращает значение TRUE, после создания диалоговой панели фокус ввода передается органу управления, идентификатор которого был записан в параметре wParam.

Если при инициализации диалоговой панели обработчик сообщения WM_INITDIALOG устанавливает фокус ввода на другой орган управления (вызывая функцию SetFocus), функция диалога должна вернуть значение FALSE.

Сообщение WM_COMMAND, поступающее в функцию диалога, передает сообщения или извещения от органов управления, расположенных в диалоговой панели. Крое этого, функция диалога может получить это сообщение с параметром wParam, равным константам IDOK и IDCANCEL, описанным в файле windows.h.

Сообщение с параметром IDOK поступает в функцию диалога в том случае, если пользователь нажал клавишу <Enter> в момент, когда ни одна из кнопок, расположенных в диалоговой панели, не имеет фокус ввода, и ни одна из кнопок не имеет стиль WS_DEFPUSHBUTTON. Если в диалоговой панели есть кнопка со стилем WS_DEFPUSHBUTTON, в описанной ситуации в функцию диалога поступает сообщение WM_COMMAND с параметром wParam, равным идентификатору этой кнопки.

Сообщение с параметром IDCANCEL появится тогда, когда пользовательзакроет диалоговую панель с помощью системного меню или клавиши <Esc>.

Обычно в диалоговой панели всегда создается одна клавиша, имеющая стиль WS_DEFPUSHBUTTON. Как правило, на этой клавише пишется слово "OK" и она используется для нормального завершения работы диалоговой панели. Для этой клавиши имеет смысл использовать идентификатор IDOK.

Еще одна клавиша, присутствующая практически во всех диалоговых панелях, имеет надпись "Cancel" и используется для отмены диалоговой панели. Если определить идентификатор этой клавиши как IDCANCEL, вы сможете использовать единый обработчик сообщения для отмены диалоговой панели при помощи кнопки и при помощи системного меню или клавиши <Esc>.

Функции для создания диалоговой панели

В программном интерфейсе Windows определены восемь функций, предназначенных для создания модальных и немодальных диалоговых панелей.

Для создания модальной диалоговой панели чаще всего используется функция DialogBox:

int WINAPI DialogBox(
  HINSTANCE hInstance,
  LPCSTR lpszTemplate,
  HWND hwndOwner,
  DLGPROC dlgprc);

Через параметр hInstance необходимо передать идентификатор текущей копии приложения.

Параметр lpszTemplate представляет собой указатель на строку имени шаблона, указанном в операторе DIALOG текстового описания шаблона.

Параметр hwndOwner - идентификатор окна, создавшего диалоговую панель.

Последний параметр, dlgprc, представляет собой адрес функции диалога.

Если при создании диалоговой панели ей необходимо передать параметр, воспользуйтесь функцией DialogBoxParam:

int WINAPI DialogBoxParam(
  HINSTANCE hInstance,
  LPCSTR lpszTemplate,
  HWND hwndOwner,
  DLGPROC dlgprc,
  LPARAM lParamInit);

Эта функция полностью аналогична функции DialogBox, за исключением дополнительного параметра lParamInit. Значение этого параметра передается через параметр lParam сообщения WM_INITDIALOG и может быть проанализировано на этапе создания диалоговой панели.

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

Функция DialogBoxIndirect аналогична функции DialogBox, но в качестве второго параметра в ней используется не указатель на строку имени шаблона, а идентификатор глобального блока памяти, в котором подготовлен шаблон:

int WINAPI DialogBoxIndirect(
  HINSTANCE hInstance,
  HGLOBAL hglbDlgTemplate,
  HWND hwndOwner, DLGPROC dlgprc);

Функция DialogBoxIndirectParam аналогична функции DialogBoxIndirect, но имеет дополнительный параметр lParamInit:

int WINAPI DialogBoxIndirectParam(
  HINSTANCE hInstance, HGLOBAL hglbDlgTemplate,
  HWND hwndOwner, DLGPROC dlgprc,
  LPARAM lParamInit);

Для создания немодальных диалоговых панелей используются функции CreateDialog, CreateDialogParam, CreateDialogIndirect, CreateDialogIndirectParam. Эти функции имеют параметры, аналогичные параметрам функций DialogBox, DialogBoxParam, DialogBoxParamIndirect:

HWND WINAPI CreateDialog(HINSTANCE hInstance,
  LPCSTR lpszTemplate, HWND hwndOwner,
  DLGPROC dlgprc);

HWND WINAPI CreateDialogParam(HINSTANCE hInstance,
  LPCSTR lpszTemplate,  HWND hwndOwner,
  DLGPROC dlgprc, LPARAM lParamInit);

HWND WINAPI CreateDialogIndirect(HINSTANCE hInstance,
  HGLOBAL hglbDlgTemplate,
  HWND hwndOwner, DLGPROC dlgprc);

HWND WINAPI CreateDialogIndirectParam(
  HINSTANCE hInstance, HGLOBAL hglbDlgTemplate,
  HWND hwndOwner, DLGPROC dlgprc,
  LPARAM lParamInit);

Функции DialogBox, DialogBoxParam, DialogBoxIndirect, и DialogBoxIndirectParam возвращают значение, передаваемое при завершении работы диалоговой панели с помощью функции EndDialog.

Функция EndDialog имеет следующий прототип:

void WINAPI EndDialog(HWND hdlg, int nResult);

В качестве первого параметра функции необходимо указать идентификатор завершаемой диалоговой панели, который передается в функцию диалога через параметр hdlg.

Функции CreateDialog, CreateDialogParam, CreateDialogIndirect, и CreateDialogIndirectParam возвращают идентификатор окна для созданной диалоговой панели.

Приложение DIALOG

Прежде чем двинуться дальше, рассмотрим приложение DIALOG, создающее простейшую диалоговую панель, похожую по внешнему виду на диалоговую панель, появляющуюся на экране при вызове функции MessageBox (рис. 3.4).

Рис. 3.4. Главное окно приложения DIALOG

В главном окне приложения имеется кнопка "About...". Если нажать на нее левой клавишей мыши, на экране появится диалоговая панель "Приложение DIALOG". На поверхности этой диалоговой панели мы разместили пиктограмму, статический орган управления и кнопку.

Вы можете завершить работу диалоговой панели, нажав на кнопку "OK" или отменив диалог с помощью системного меню или клавиши <Esc>.

Исходный текст главного файла приложения представлен в листинге 3.1.


Листинг 3.1. Файл dialog\dialog.cpp


// ----------------------------------------
// Простейшая диалоговая панель
// ----------------------------------------

#define STRICT
#include <windows.h>
#include <mem.h>

#define IDB_Button1 1

// Прототипы функций
BOOL InitApp(HINSTANCE);
LRESULT CALLBACK _export WndProc(HWND, UINT, WPARAM, LPARAM);
BOOL CALLBACK    _export DlgProc(HWND, UINT, WPARAM, LPARAM);

// Имя класса окна
char const szClassName[]   = "DialogAppClass";

// Заголовок окна
char const szWindowTitle[] = "Dialog Box Demo";

HINSTANCE hInst;

// =====================================
// Функция WinMain
// =====================================
#pragma argsused

int PASCAL
WinMain(HINSTANCE hInstance,
        HINSTANCE hPrevInstance,
        LPSTR     lpszCmdLine, 
        int       nCmdShow)   
{
  MSG  msg;   // структура для работы с сообщениями
  HWND hwnd;  // идентификатор главного окна приложения

  HWND hButton1;

  // Инициализируем приложение
  if(!InitApp(hInstance))
      return FALSE;

  hInst = hInstance;

  // После успешной инициализации приложения создаем
  // главное окно приложения
  hwnd = CreateWindow(
    szClassName,         // имя класса окна
    szWindowTitle,       // заголовок окна
    WS_OVERLAPPEDWINDOW, // стиль окна
    CW_USEDEFAULT,       // задаем размеры и расположение
    CW_USEDEFAULT,       // окна, принятые по умолчанию 
    CW_USEDEFAULT,
    CW_USEDEFAULT,
    0,                   // идентификатор родительского окна
    0,                   // идентификатор меню
    hInstance,           // идентификатор приложения
    NULL);               // указатель на дополнительные
                         // параметры

  // Если создать окно не удалось, завершаем приложение
  if(!hwnd)
    return FALSE;

  // Рисуем главное окно
  ShowWindow(hwnd, nCmdShow);
  UpdateWindow(hwnd);

  // Создаем кнопку
  hButton1 = CreateWindow("button", "About...",
    WS_CHILD | WS_VISIBLE | BS_PUSHBUTTON,
    20, 20, 90, 30,
    hwnd, (HMENU) IDB_Button1,
    hInstance, NULL);

  // Запускаем цикл обработки сообщений
  while(GetMessage(&msg, 0, 0, 0))
  {
    DispatchMessage(&msg);
  }
  return msg.wParam;
}

// =====================================
// Функция InitApp
// Выполняет регистрацию класса окна
// =====================================

BOOL
InitApp(HINSTANCE hInstance)
{
  ATOM aWndClass; // атом для кода возврата
  WNDCLASS wc;    // структура для регистрации
                  // класса окна

  memset(&wc, 0, sizeof(wc));

  wc.style = 0;
  wc.lpfnWndProc = (WNDPROC) WndProc;
  wc.cbClsExtra = 0;
  wc.cbWndExtra = 0;
  wc.hInstance = hInstance;
  wc.hIcon = LoadIcon(NULL, IDI_APPLICATION);
  wc.hCursor = LoadCursor(NULL, IDC_ARROW);
  wc.hbrBackground = (HBRUSH)(COLOR_WINDOW + 1);
  wc.lpszMenuName = (LPSTR)NULL;
  wc.lpszClassName = (LPSTR)szClassName;

  // Регистрация класса
  aWndClass = RegisterClass(&wc);

  return (aWndClass != 0);
}

// =====================================
// Функция WndProc
// =====================================

LRESULT CALLBACK _export
WndProc(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam)
{
  static DLGPROC lpfnDlgProc;

  switch (msg)
  {
    case WM_COMMAND:
    {
      // Если нажата кнопка, выводим
      // диалоговую панель
      if(wParam == IDB_Button1)
      {
        // Переходник для функции диалоговой панели
        lpfnDlgProc = (DLGPROC) 
          MakeProcInstance((FARPROC)DlgProc, hInst);

        // Создаем модальную диалоговую панель
        DialogBox(hInst, "DIALOG_OK", hwnd, lpfnDlgProc);
      }
      return 0;
    }

    case WM_DESTROY:
    {
      PostQuitMessage(0);
      return 0;
    }
  }
  return DefWindowProc(hwnd, msg, wParam, lParam);
}

// =====================================
// Функция DlgProc
// =====================================
#pragma argsused

BOOL CALLBACK _export
DlgProc(HWND hdlg, UINT msg, WPARAM wParam, LPARAM lParam)
{
  switch(msg)
  {
    // Инициализация диалоговой панели
    case WM_INITDIALOG:
    {
      return TRUE;
    }

    case WM_COMMAND:
    {
      switch(wParam)
      {
        // Сообщение от кнопки "OK"
        case IDOK:

        // Отмена диалоговой панели.
        // Это сообщение приходит, когда пользователь
        // нажимает на клавишу <Esc>
        case IDCANCEL:
        {
          // Устанавливаем флаг завершения диалога
          EndDialog(hdlg, 0);
          return TRUE;
        }
      }
    }
  }
  return FALSE;
}

Функция WinMain создает главное окно приложения и располагает в нем один орган управления - кнопку с надписью "About...". Эта кнопка предназначена для активизации диалоговой панели.

Функция главного окна приложения WndProc обрабатывает сообщение WM_COMMAND, поступающее от кнопки:

case WM_COMMAND:
{
  if(wParam == IDB_Button1)
  {
    lpfnDlgProc = (DLGPROC) 
      MakeProcInstance((FARPROC)DlgProc, hInst);

    DialogBox(hInst, "DIALOG_OK", hwnd, lpfnDlgProc);
  }
  return 0;
}

Прежде всего вызывается функция MakeProcInstance, создающая переходник для функции диалога DlgProc. Если вы пользуетесь современными средствами разработки, такими как Borland C++ for Windows версий 3.1 или 4.0 или Microsoft Visual C++, и если функция диалога описана с ключевым словом _export, переходник можно не создавать.

Далее обработчик сообщения вызывает функцию DialogBox, создающую модальную диалоговую панель. Шаблон этой модели описан в файле ресурсов приложения (листинг 3.2).

Функция диалоговой панели DlgProc обрабатывает сообщения WM_INITDIALOG и WM_COMMAND.

Обработчик сообщения WM_INITDIALOG состоит из одной строки, возвращающей значение TRUE. В этом случае после инициализации диалоговой панели фокус ввода передается первому органу управления, описанному в шаблоне со стилем WS_TABSTOP. В нашем случае это кнопка с надписью "OK":

DEFPUSHBUTTON "OK", IDOK, 56, 43, 36, 14,
    WS_CHILD | WS_VISIBLE | WS_TABSTOP

Обработчик сообщения WM_COMMAND в ответ на любые действия пользователя вызывает функцию EndDialog, предназначенную для завершения работы диалоговой панели:

case WM_COMMAND:
{
  switch(wParam)
  {
    case IDOK:
    case IDCANCEL:
    {
      EndDialog(hdlg, 0);
      return TRUE;
    }
  }
}

Листинг 3.2. Файл dialog\dialog.rc


#include "g:\tcwin\include\windows.h"

APPICON ICON "appicon.ico"

DIALOG_OK DIALOG 25, 34, 152, 67
STYLE DS_MODALFRAME | WS_POPUP | WS_CAPTION | WS_SYSMENU
CAPTION "Приложение DIALOG"
BEGIN
  CTEXT "Microsoft Windows Application\n"
    "Приложение DIALOG\n"
    "(C) Frolov A.V., 1994",
    -1, 28, 9, 117, 26, WS_CHILD | WS_VISIBLE | WS_GROUP
  ICON "APPICON", -1, 6, 14, 16, 16, WS_CHILD | WS_VISIBLE
  DEFPUSHBUTTON "OK", IDOK, 56, 43, 36, 14,
    WS_CHILD | WS_VISIBLE | WS_TABSTOP
END

В файл описания ресурсов приложения мы включили файл windows.h, так как именно там описаны идентификаторы IDOK и IDCANCEL.

Перед шаблоном в файле ресурсов приложения описана пиктограмма с идентификатором APPICON (листинг 3.3), на которую есть ссылка в шаблоне диалоговой панели.


Листинг 3.3. Файл dialog\appicon.ico



Описание шаблона диалога начинается с оператора DIALOG. В этом операторе определено имя шаблона DIALOG_OK, на которое ссылается функция DialogBox.

При помощи оператора STYLE определен стиль диалога. Мы создаем модальный диалог (DS_MODALFRAME), созданный как временное окно (WS_POPUP), с заголовком (WS_CAPTION) и системным меню (WS_SYSMENU).

Заголовок диалоговой панели задан в операторе CAPTION.

Далее в описании шаблона между операторами BEGIN и END находятся строки описания органов управления.

Оператор CTEXT описывает статический орган управления. Обратите внимание на использование символа "\n" для принудительного перехода на новую строку.

Оператор ICON описывает пиктограмму.

Оператор DEFPUSHBUTTON описывает кнопку с надписью "OK". Эта кнопка - единственный орган управления, имеющий стиль WS_TABSTOP, поэтому при инициализации диалоговой панели ей передается фокус ввода.

Файл определения модуля приложения DIALOG приведен в листинге 3.4.


Листинг 3.4. Файл dialog\dialog.def


; =============================
; Файл определения модуля
; =============================
NAME DIALOG
DESCRIPTION 'Приложение DIALOG, (C) 1994, Frolov A.V.'
EXETYPE windows
STUB 'winstub.exe'
STACKSIZE 5120
HEAPSIZE 1024
CODE preload moveable discardable
DATA preload moveable multiple

3.2. Сообщения для органов управления

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

Использование функции SendMessage

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

Программный интерфейс Windows содержит специальную функцию, предназначенную для определения идентификаторов окна органов управления по идентификатору окна диалога и идентификатору самого органа управления. Эта функция называется GetDlgItem:

HWND WINAPI GetDlgItem(HWND hdlg, int idControl);

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

Второй параметр является идентификатором органа управления, указанным в шаблоне диалоговой панели.

Для того чтобы установить переключатель с идентификатором IDC_SWITCH во включенное состояние, вы можете вызывать функцию SendMessage следующим образом:

SendMessage(GetDlgItem(hdlg, IDC_SWITCH),
   BM_SETCHECK, TRUE, 0L);

Зная идентификатор окна органа управления, вы можете получить идентификатор самого органа управления, т. е. решить задачу, обратную выполняемой функцией DetDlgItem. Для этого следует воспользоваться функцией GetWindowWord, передав ей в качестве второго параметра константу GWW_ID:

nIDControl = GetWindowWord(hwndControl, GWW_ID);

Эта функция возвращает значения из области дополнительной памяти, определенной при регистрации класса окна. Напомним, что размер дополнительной области памяти задается значением, записанным в элементе cbWndExtra структуры WNDCLASS.

Использование специальных функций

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

Для посылки сообщения органу управления удобно использовать функцию SendDlgItemMessage:

LRESULT WINAPI SendDlgItemMessage(
  HWND hdlg, int idDlgItem, 
  UINT uMsg, WPARAM wParam, LPARAM lParam);

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

Для выполнения некоторых часто использующихся операций с органами управления в программном интерфейсе Windows определены специальные функции.

В частности, для заполнения списка LISTBOX именами файлов, каталогов и дисковых устройств предназначена функция DlgDirList:

int WINAPI DlgDirList(HWND hdlg,
  LPSTR lpszPath, int idListBox,
  int idStatic, UINT uFileType);

Первый параметр этой функции указывает идентификатор окна диалоговой панели.

Параметр lpszPath - указатель на строку, содержащую шаблон для имен файлов.

Параметр idListBox перед вызовом функции должен содержать идентификатор заполняемого списка.

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

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

Аналогичная функция предусмотрена и для списка COMBOBOX:

int WINAPI DlgDirListComboBox (HWND hdlg,
  LPSTR lpszPath, int idListBox,
  int idStatic, UINT uFileType);

Назначение параметров этой функции полностью аналогично назначению параметров функции DlgDirList.

Функция DlgDirSelect предназначена для получения из списка LISTBOX (подготовленного с помощью функции DlgDirList) строки, выбранной пользователем:

BOOL WINAPI DlgDirSelect(HWND hdlg,
  LPSTR lpszBuffer, int idListBox);

Параметр hdlg определяет диалоговую панель. Нужный список задается параметром idListBox. Выбранная строка будет записана в буфер, адрес которой указан с помощью параметра lpszBuffer. Размер буфера должен быть не меньше 128 байт.

Аналогичная функция предусмотрена для списка COMBOBOX:

BOOL WINAPI DlgDirSelectComboBox (HWND hdlg,
  LPSTR lpszBuffer, int idListBox);

Если ваше приложение будет работать в среде Windows версии 3.1 или более старшей версии, для получения выбранной пользователем строки вы можете использовать функции DlgDirSelectEx и DlgDirSelectComboBoxEx:

BOOL WINAPI DlgDirSelectEx(HWND hdlg,
  LPSTR lpszBuffer, int cbBufSize, int idListBox);
BOOL WINAPI DlgDirSelectComboBoxEx(HWND hdlg,
  LPSTR lpszBuffer, int cbBufSize, int idListBox);

Эти функции позволяют получить в буфер lpszBuffer размером cbBufSize байт строку, выбранную пользователем из списка с идентификатором idListBox, расположенном в диалоговой панели hdlg. Однако для выбранной строки выполняется дополнительная обработка, а именно: если выбрано имя каталога или дискового устройства, функция удаляет из строки квадратные скобки и символы "-".

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

Функция SetDlgItemText позволяет изменить заголовок органа управления или записать текст в текстовый редактор:

void WINAPI SetDlgItemText(HWND hdlg,
  int idControl, LPCSTR lpszText);

Текстовая строка lpszText записывается в орган управления с идентификатором idControl, расположенным в диалоговой панели hdlg.

Функция SetDlgItemInt позволяет записать в заголовок органа управления или текстовый редактор текстовую строку, полученную после преобразования целого числа в формат строки символов:

void WINAPI SetDlgItemInt(HWND hdlg,
  int idControl, UINT uValue, BOOL fSigned);

Для диалоговой панели с идентификатором окна, равным hdlg, эта функция записывает символьное представление параметра uValue в заголовок органа управления или редактор текста с идентификатором idControl. Если параметр fSigned указан как TRUE, значение uValue интерпретируется как знаковое целое, если FALSE - как беззнаковое целое.

Для получения строки, связанной с органом управления, расположенном в диалоговой панели, можно использовать функцию GetDlgItemText:

int WINAPI GetDlgItemText(HWND hdlg,
  int idControl, LPSTR lpszBuffer, int cbBufferSize);

Эта функция записывает текст, связанный с органом управления idControl, в буфер lpszBuffer, имеющий размер cbBufferSize байт.

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

UINT WINAPI GetDlgItemInt (HWND hdlg, 
  int idControl, BOOL FAR* lptTranslated, BOOL fSigned);

Эта функция возвращает целое число, которое образуется после преобразования текста, связанного с органом управления idControl в диалоговой панели hdlg. Если параметр fSigned указан как TRUE, преобразуемая строка интерпретируется как символьное представление знакового целого, если FALSE - как беззнакового целого. В переменную, адрес которой передается через параметр lptTranslated, записывается код ошибки. Если преобразование выполнено без ошибок, в переменную записывается значение TRUE, в противном случае - FALSE.

Есть также функции, предназначенные для работы с переключателями.

Функция CheckDlgButton предназначена для изменения состояния переключателя CHECKBOX (включения или выключения):

void WINAPI CheckDlgButton(HWND hdlg,
  int idButton, UINT uState);

Для переключателя с идентификатором idButton, расположенного в диалоговой панели hdlg, устанавливается новое состояние в соответствии со значением параметра uState. Для выключения переключателя параметр uState должен иметь нулевое значение. Если этот параметр будет равен 1, переключатель будет включен, а если 2 - переведен в неактивное состояние.

Аналогичная функция предусмотрена для переключателей RADIOBUTTON:

void WINAPI CheckRadioButton (HWND hdlg,
  int idFirstButton, int idLastButton, int idCheckButton);

Эта функция может обслуживать сразу группу переключателей, имеющих идентификаторы от idFirstButton до idLastButton. Она включает переключатель с идентификатором idCheckButton, после чего выключает все остальные переключатели группы в указанном параметрами idFirstButton и idLastButton диапазоне идентификаторов.

Для определения текущего состояния переключателя вы можете воспользоваться функцией IsDlgButtonChecked:

UINT WINAPI IsDlgButtonChecked(HWND hdlg, int idButton);

Эта функция возвращает состояние переключателя с идентификатором idButton, расположенного в диалоговой панели hdlg. Если переключатель находится в выключенном состоянии, возвращается нулевое значение. Для включенного переключателя возвращается значение 1. Значение 2 соответствует неактивному переключателю, изображенному серым цветом. В случае ошибки возвращается отрицательное значение -1.

Если ваше приложение использует собственную логику для передачи фокуса ввода между органами управления, расположенными в диалоговой панели, ему может потребоваться информация о последовательности, в которой должен передаваться фокус ввода. С помощью функции GetNextDlgGroupItemприложение может определить идентификатор окна предыдущего или следующего органа управления в группе:

HWND WINAPI GetNextDlgGroupItem(HWND hdlg,
   HWND hwndControl, BOOL fPrevious);

В зависимости от значения флага fPrevious функция возвращает идентификатор предыдущего или следующего органа управления группе относительно органа управления с идентификатором hwndControl. Если значение флага fPrevious равно TRUE, функция возвращает идентификатор окна для предыдущего органа управления в группе, если FALSE - для следующего.

Функция GetNextDlgTabItem позволяет определить идентификатор окна для первого органа управления, который имеет стиль WS_TABSTOP и расположен после органа управления с заданным идентификатором или перед этим органом:

HWND WINAPI GetNextDlgTabItem(HWND hdlg,
  HWND hwndControl, BOOL fPrevious);

Параметр hwndControl определяет орган управления, начиная с которого функция будет выполнять поиск, параметр fPrevious определяет направление поиска. Если значение параметра fPrevious равно TRUE, функция ищет предыдущий орган управления в группе, если FALSE - следующий.

Следует упомянуть еще две функции, имеющие отношение к специфической системе координат, принятой для работы с диалоговыми панелями.

Функция MapDialogRect преобразует координаты из единиц диалоговой панели (dialog units) в пиксели:

void WINAPI MapDialogRect(HWND hdlg, RECT FAR* lprc);

Преобразуемые координаты необходимо записать в структуру типа RECT, адрес которой указывается во втором параметре функции. Результат преобразования будет записан в эту же структуру.

Функция GetDialogBaseUnits возвращает двойное слово, содержащее информацию о диалоговой системе координат:

DWORD WINAPI GetDialogBaseUnits(void);

Младшее слово представляет собой ширину в пикселях диалоговой единицы длины, старшее - высоту.

Приложение DLGCOMBO

Следующее приложение называется DLGCOMBO. Оно создает диалоговую панель, содержащую список "combobox", две кнопки ("OK" и "Cancel") и пиктограмму (рис. 3.5). С его помощью мы продемонстрируем использование функций, предназначенных для работы с органами управления, расположенными в диалоговой панели.

Рис. 3.5. Диалоговая панель, создаваемая приложением DLGCOMBO

Главный файл приложения представлен в листинге 3.5.


Листинг 3.5. Файл dlgcombo\dlgcombo.cpp


// ----------------------------------------
// Диалоговая панель со списком COMBOBOX
// ----------------------------------------

#define STRICT
#include <windows.h>
#include <mem.h>
#include "dlgcombo.hpp"

#define IDB_Button1 1

// Прототипы функций
BOOL InitApp(HINSTANCE);
LRESULT CALLBACK _export WndProc(HWND, UINT, WPARAM, LPARAM);
BOOL    CALLBACK _export DlgProc(HWND, UINT, WPARAM, LPARAM);

// Имя класса окна
char const szClassName[]   = "DialogAppClass";

// Заголовок окна
char const szWindowTitle[] = "Dialog Box Demo";

HINSTANCE hInst;

// =====================================
// Функция WinMain
// =====================================
#pragma argsused

int PASCAL
WinMain(HINSTANCE hInstance,
        HINSTANCE hPrevInstance,
        LPSTR     lpszCmdLine, 
        int       nCmdShow)   
{
  MSG  msg;   // структура для работы с сообщениями
  HWND hwnd;  // идентификатор главного окна приложения

  HWND hButton1;

  // Инициализируем приложение
  if(!InitApp(hInstance))
      return FALSE;

  hInst = hInstance;

  // После успешной инициализации приложения создаем
  // главное окно приложения
  hwnd = CreateWindow(
    szClassName,         // имя класса окна
    szWindowTitle,       // заголовок окна
    WS_OVERLAPPEDWINDOW, // стиль окна
    CW_USEDEFAULT,       // задаем размеры и расположение
    CW_USEDEFAULT,       // окна, принятые по умолчанию 
    CW_USEDEFAULT,
    CW_USEDEFAULT,
    0,                   // идентификатор родительского окна
    0,                   // идентификатор меню
    hInstance,           // идентификатор приложения
    NULL);               // указатель на дополнительные
                         // параметры

  // Если создать окно не удалось, завершаем приложение
  if(!hwnd)
    return FALSE;

  // Рисуем главное окно
  ShowWindow(hwnd, nCmdShow);
  UpdateWindow(hwnd);

  // Создаем кнопку
  hButton1 = CreateWindow("button", "Open...",
    WS_CHILD | WS_VISIBLE | BS_PUSHBUTTON,
    20, 20,
    90, 30,
    hwnd,
    (HMENU) IDB_Button1,
    hInstance, NULL);

  // Запускаем цикл обработки сообщений
  while(GetMessage(&msg, 0, 0, 0))
  {
    DispatchMessage(&msg);
  }
  return msg.wParam;
}

// =====================================
// Функция InitApp
// Выполняет регистрацию класса окна
// =====================================

BOOL
InitApp(HINSTANCE hInstance)
{
  ATOM aWndClass; // атом для кода возврата
  WNDCLASS wc;    // структура для регистрации
                  // класса окна

  memset(&wc, 0, sizeof(wc));

  wc.style = 0;
  wc.lpfnWndProc = (WNDPROC) WndProc;
  wc.cbClsExtra = 0;
  wc.cbWndExtra = 0;
  wc.hInstance = hInstance;
  wc.hIcon = LoadIcon(NULL, IDI_APPLICATION);
  wc.hCursor = LoadCursor(NULL, IDC_ARROW);
  wc.hbrBackground = (HBRUSH)(COLOR_WINDOW + 1);
  wc.lpszMenuName = (LPSTR)NULL;
  wc.lpszClassName = (LPSTR)szClassName;

  // Регистрация класса
  aWndClass = RegisterClass(&wc);

  return (aWndClass != 0);
}

// =====================================
// Функция WndProc
// =====================================

LRESULT CALLBACK _export
WndProc(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam)
{
  switch (msg)
  {
    case WM_COMMAND:
    {
      // Если нажата кнопка, выводим
      // диалоговую панель
      if(wParam == IDB_Button1)
      {
        // Создаем модальную диалоговую панель
        DialogBox(hInst, "SELECT_FILE", hwnd,
           (DLGPROC)DlgProc);
      }
      return 0;
    }

    case WM_DESTROY:
    {
      PostQuitMessage(0);
      return 0;
    }
  }
  return DefWindowProc(hwnd, msg, wParam, lParam);
}

// =====================================
// Функция DlgProc
// =====================================
#pragma argsused

BOOL CALLBACK _export
DlgProc(HWND hdlg, UINT msg, WPARAM wParam, LPARAM lParam)
{
  switch(msg)
  {
    // Инициализация диалоговой панели
    case WM_INITDIALOG:
    {
      // Заполняем список именами файлов, каталогов
      // и дисковых устройств
      DlgDirListComboBox(hdlg,
        "*.*", IDC_COMBO, IDC_STATIC,
        DDL_READWRITE | DDL_READONLY  | DDL_HIDDEN |
        DDL_SYSTEM    | DDL_DIRECTORY | DDL_DRIVES |
        DDL_ARCHIVE);

      return TRUE;
    }

    case WM_COMMAND:
    {
      switch(wParam)
      {
        char Buffer[80];

        // Обрабатываем извещение от списка
        case IDC_COMBO:
        {
          // Двойной щелчок мышью по строке списка
          if(HIWORD(lParam) == LBN_DBLCLK)
          {
            // Получаем выбранную строку
            // и отображаем ее на экране
            GetDlgItemText(hdlg, IDC_COMBO, Buffer, 80);
            MessageBox(hdlg, Buffer, szWindowTitle, MB_OK);
          }
          return TRUE;
        }

        // Сообщение от кнопки "OK"
        case IDOK:
        {
          // Получаем выбранную строку
          // и отображаем ее на экране
          GetDlgItemText(hdlg, IDC_COMBO, Buffer, 80);
          MessageBox(hdlg, Buffer, szWindowTitle, MB_OK);
          return TRUE;
        }

        // Отмена диалоговой панели.
        case IDCANCEL:
        {
          // Устанавливаем флаг завершения диалога
          EndDialog(hdlg, 0);
          return TRUE;
        }
      }
    }
  }
  return FALSE;
}

Функция WinMain аналогична использованной в предыдущем приложении. Она создает главное окно и кнопку с надписью "Open..." для создания диалоговой панели.

В функции главного окна приложения WndProc обрабатывается сообщение WM_COMMAND, поступающее от кнопки. В ответ на это сообщение приложение создает модальную диалоговую панель, вызывая функцию DialogBox:

case WM_COMMAND:
{
  if(wParam == IDB_Button1)
  {
    DialogBox(hInst, "SELECT_FILE", hwnd, (DLGPROC)DlgProc);
  }
  return 0;
}

Обратите внимание, что в качестве последнего параметра функции DialogBox указан непосредственный адрес функции диалога. Мы не стали использовать функцию MakeProcInstance и создавать переходник, так как для трансляции приложения была использована система разработки Borland C++ for Windows версии 3.1, а функция диалога описана с ключевым словом _export.

Обработчик сообщения WN_INITDIALOG, расположенный в функции диалога DlgProc, заполняет список COMBOBOX именами файлов и каталогов, расположенных в текущем каталоге, а также именами дисковых устройств. Для этого он вызывает функцию DlgDirListComboBox:

case WM_INITDIALOG:
{
  DlgDirListComboBox(hdlg,
    "*.*", IDC_COMBO, IDC_STATIC,
    DDL_READWRITE | DDL_READONLY  | DDL_HIDDEN |
    DDL_SYSTEM    | DDL_DIRECTORY | DDL_DRIVES |
    DDL_ARCHIVE);
  return TRUE;
}

В качестве первого параметра этой функции передается идентификатор диалоговой панели.

Второй параметр является указателем на строку шаблона имен файлов. Мы отображаем имена всех файлов, поэтому в качестве шаблона используется строка "*.*".

Третий параметр - идентификатор заполняемого списка COMBOBOX. Этот список получит сообщение CB_DIR, что и приведет к заполнению последнего именами файлов, каталогов и дисковых устройств.

Четвертый параметр - идентификатор статического органа управления, в котором будет отображаться путь к текущему каталогу, включающий имя диска.

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

Функция диалога DlgProc обрабатывает сообщение WM_COMMAND.

Если это сообщение содержит извещение от списка о двойном щелчке по строке (LBN_DBLCLK), обработчик сообщения вызывает функцию GetDlgItemText, переписывающую выбранную строку в буфер, вслед за чем содержимое буфера отображается на экране при помощи функции MessageBox:

GetDlgItemText(hdlg, IDC_COMBO, Buffer, 80);
MessageBox(hdlg, Buffer, szWindowTitle, MB_OK);

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

При отмене диалога с помощью кнопки "Cancel", системного меню или клавиши <Esc> работа диалоговой панели завершается, для чего вызывается функция EndDialog:

case IDCANCEL:
{
  EndDialog(hdlg, 0);
  return TRUE;
}

Идентификаторы списка и статического органа управления определены в файле dlgcombo.hpp (листинг 3.6). Этот файл необходимо включить как в главный файл приложения, так и в файл описания ресурсов.


Листинг 3.6. Файл dlgcombo\dlgcombo.hpp


#define IDC_COMBO  101
#define IDC_STATIC 102

Файл описания ресурсов представлен в листинге 3.7.


Листинг 3.7. Файл dlgcombo\dlgcombo.rc


#include "g:\bc\include\windows.h"
#include "dlgcombo.hpp"

APPICON ICON "appicon.ico"

SELECT_FILE DIALOG 6, 37, 199, 120
STYLE DS_MODALFRAME | WS_POPUP | WS_CAPTION | WS_SYSMENU
CAPTION "Входной файл"
BEGIN
  CONTROL "", IDC_STATIC, "STATIC",
    SS_LEFT | WS_CHILD | WS_VISIBLE, 13, 7, 123, 10
  ICON "APPICON", -1, 149, 58, 16, 16,
    WS_CHILD | WS_VISIBLE
  DEFPUSHBUTTON "OK", IDOK, 149, 19, 36, 14,
    WS_CHILD | WS_VISIBLE | WS_TABSTOP
  CONTROL "", IDC_COMBO, "COMBOBOX",
    CBS_SIMPLE | CBS_SORT | CBS_DISABLENOSCROLL | WS_CHILD |
    WS_VISIBLE | WS_VSCROLL | WS_TABSTOP, 13, 19, 123, 94
  PUSHBUTTON "Cancel", IDCANCEL, 149, 39, 36, 14,
    WS_CHILD | WS_VISIBLE | WS_TABSTOP
END

В этом файле есть ссылка на использованную пиктограмму (листинг 3.8).


Листинг 3.8. Файл dlgcombo\appicon.ico



Обратите внимание на то, что в шаблоне диалоговой панели три органа управления имеют стиль WS_TABSTOP. Это кнопка "OK", список COMBOBOX и кнопка 'Cancel". Если во время работы диалоговой панели вы будете нажимать на клавишу <Tab>, фокус ввода будет переключаться между этими тремя органами управления.

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

Файл определения модуля приложения DLGCOMBO приведен в листинге 3.9.


Листинг 3.9. Файл dlgcombo\dlgcombo.def


; =============================
; Файл определения модуля
; =============================
NAME DLGCOMBO
DESCRIPTION 'Приложение DLGCOMBO, (C) 1994, Frolov A.V.'
EXETYPE windows
STUB 'winstub.exe'
STACKSIZE 5120
HEAPSIZE 1024
CODE preload moveable discardable
DATA preload moveable multiple

Приложение DLGTAB

Приложение DLGTAB демонстрирует использование групп органов управления. Оно создает диалоговую панель "Карточка сотрудника", в которой можно ввести имя, фамилию и отчество сотрудника (в окне редактирования "Ф.И.О."), а также указать его должность и прочие сведения (рис. 3.6).

Рис. 3.6. Диалоговая панель, создаваемая приложением DLGTAB

Группа "Должность" содержит переключатель с зависимой фиксацией на три положения: "Инженер", "Старший инженер" и "Программист". Так как у сотрудника может быть только одна должность, в данной группе мы использовали переключатель со стилем BS_AUTORADIOBUTTON.

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

Подготовив все данные, нажмите кнопку "OK" или клавишу <Enter>. При этом состояние окна редактирования и переключателей будет отображено в диалоговой панели "Вы ввели" (рис. 3.7), созданной функцией MessageBox.

Рис. 3.7. Диалоговая панель "Вы ввели"

Главный файл приложения DLGTAB представлен в листинге 3.10.


Листинг 3.10. Файл dlgtab\dlgtab.cpp


// ----------------------------------------
// Диалоговая панель с редактором текста
// и переключателями
// ----------------------------------------

#define STRICT
#include <windows.h>
#include <mem.h>
#include "dlgtab.hpp"

// Прототипы функций
BOOL InitApp(HINSTANCE);
LRESULT CALLBACK _export WndProc(HWND, UINT, WPARAM, LPARAM);
BOOL    CALLBACK _export DlgProc(HWND, UINT, WPARAM, LPARAM);

// Имя класса окна
char const szClassName[]   = "DlgTabAppClass";

// Заголовок окна
char const szWindowTitle[] = "DlgTab Box Demo";

HINSTANCE hInst;

// =====================================
// Функция WinMain
// =====================================
#pragma argsused

int PASCAL
WinMain(HINSTANCE hInstance,
        HINSTANCE hPrevInstance,
        LPSTR     lpszCmdLine, 
        int       nCmdShow)   
{
  MSG  msg;   // структура для работы с сообщениями
  HWND hwnd;  // идентификатор главного окна приложения

  // Инициализируем приложение
  if(!InitApp(hInstance))
      return FALSE;

  hInst = hInstance;

  // После успешной инициализации приложения создаем
  // главное окно приложения
  hwnd = CreateWindow(
    szClassName,         // имя класса окна
    szWindowTitle,       // заголовок окна
    WS_OVERLAPPEDWINDOW, // стиль окна
    CW_USEDEFAULT,       // задаем размеры и расположение
    CW_USEDEFAULT,       // окна, принятые по умолчанию 
    CW_USEDEFAULT,
    CW_USEDEFAULT,
    0,                   // идентификатор родительского окна
    0,                   // идентификатор меню
    hInstance,           // идентификатор приложения
    NULL);               // указатель на дополнительные
                         // параметры
  // Если создать окно не удалось, завершаем приложение
  if(!hwnd)
    return FALSE;

  // Запускаем цикл обработки сообщений
  while(GetMessage(&msg, 0, 0, 0))
  {
    DispatchMessage(&msg);
  }
  return msg.wParam;
}

// =====================================
// Функция InitApp
// Выполняет регистрацию класса окна
// =====================================

BOOL
InitApp(HINSTANCE hInstance)
{
  ATOM aWndClass; // атом для кода возврата
  WNDCLASS wc;    // структура для регистрации
                  // класса окна

  memset(&wc, 0, sizeof(wc));

  wc.style = 0;
  wc.lpfnWndProc = (WNDPROC) WndProc;
  wc.cbClsExtra = 0;
  wc.cbWndExtra = 0;
  wc.hInstance = hInstance;
  wc.hIcon = LoadIcon(NULL, IDI_APPLICATION);
  wc.hCursor = LoadCursor(NULL, IDC_ARROW);
  wc.hbrBackground = (HBRUSH)(COLOR_WINDOW + 1);
  wc.lpszMenuName = (LPSTR)NULL;
  wc.lpszClassName = (LPSTR)szClassName;

  // Регистрация класса
  aWndClass = RegisterClass(&wc);

  return (aWndClass != 0);
}

// =====================================
// Функция WndProc
// =====================================

LRESULT CALLBACK _export
WndProc(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam)
{
  switch (msg)
  {
    case WM_CREATE:
    {
      // Создаем модальную диалоговую панель
      DialogBox(hInst, "SELECT", hwnd, (DLGPROC)DlgProc);

      // После завершения работы диалоговой панели
      // завершаем работу приложения
      DestroyWindow(hwnd);
      return 0;
    }

    case WM_DESTROY:
    {
      PostQuitMessage(0);
      return 0;
    }
  }
  return DefWindowProc(hwnd, msg, wParam, lParam);
}

// =====================================
// Функция DlgProc
// =====================================
#pragma argsused

BOOL CALLBACK _export
DlgProc(HWND hdlg, UINT msg, WPARAM wParam, LPARAM lParam)
{
  switch(msg)
  {
    // Инициализация диалоговой панели
    case WM_INITDIALOG:
    {
      return TRUE;
    }

    case WM_COMMAND:
    {
      switch(wParam)
      {
        char Buffer[256];

        // Сообщение от кнопки "OK"
        case IDOK:
        {
          // Получаем строку из текстового редактора 
          GetDlgItemText(hdlg, IDC_NAME, Buffer, 80);

          lstrcat(Buffer, "\n\nДолжность:\n");

          // Определяем состояние переключателей
          // типа RadioButton
          if(IsDlgButtonChecked(hdlg, IDC_PROGRAMMER))
          {
            lstrcat(Buffer, "Программист");
          }
          else if(IsDlgButtonChecked(hdlg, IDC_ENGINIER))
          {
            lstrcat(Buffer, "Инженер");
          }
          else if(IsDlgButtonChecked(hdlg, IDC_SENGINIER))
          {
            lstrcat(Buffer, "Старший инженер");
          }

          lstrcat(Buffer, "\n\nЗнает языки:\n");

          // Определяем состояние переключателей
          // типа CheckBox
          if(IsDlgButtonChecked(hdlg, IDC_ENGLISH))
          {
            lstrcat(Buffer, "Английский\n");
          }
          if(IsDlgButtonChecked(hdlg, IDC_C))
          {
            lstrcat(Buffer, "Си\n");
          }
          if(IsDlgButtonChecked(hdlg, IDC_PASCAL))
          {
            lstrcat(Buffer, "Паскаль\n");
          }

          MessageBox(hdlg, Buffer, "Вы ввели", MB_OK);
          return TRUE;
        }

        // Отмена диалоговой панели.
        case IDCANCEL:
        {
          // Устанавливаем флаг завершения диалога
          EndDialog(hdlg, FALSE);
          return TRUE;
        }
      }
    }
  }
  return FALSE;
}

Функция WinMain при внимательном взгляде может вызвать у вас удивление. Эта функция регистрирует класс для главного окна приложения и создает это окно, вслед за чем запускает цикл обработки сообщений. Но позвольте, где же вызов привычных вам функций ShowWindow и UpdateWindow?

Мы намеренно не стали вызывать эти функции, в результате чего главное окно приложения получилось... невидимым!

Несмотря на то, что главное окно приложения не отображается, оно в момент своего создания получает сообщение WM_CREATE. Обработчик этого сообщения создает модальную диалоговую панель, вызывая функцию DialogBox:

case WM_CREATE:
{
  DialogBox(hInst, "SELECT", hwnd, (DLGPROC)DlgProc);
  DestroyWindow(hwnd);
  return 0;
}

У нас не было никакой необходимости создавать невидимое главное окно приложения. Мы могли поступить таким же образом, что и в предыдущем приложении - создать в главном окне приложения кнопку, предназначенную для запуска диалоговой панели. Но иногда требуется создать такое приложение, которое выполняет некоторую работу, не появляясь на экране. Функция главного (и невидимого) окна нашего приложения в момент создания окна создает диалоговую панель. При завершении работы диалоговой панели работа приложения завершается.

Вы также можете в этом приложении вообще не создавать главное окно приложения и цикл обработки сообщений, вызвав функцию DialogBox непосредственно в функции WinMain.

Займемся теперь функцией диалога.

Обработчик сообщения WM_INITDIALOG не имеет никаких особенностей. Он возвращает значение TRUE, вследствие чего после инициализации диалоговой панели фокус ввода передается первому органу управления, описанному в шаблоне диалоговой панели со стилем WS_TABSTOP. В данном случае это поле редактирования, так как при заполнении карточки сотрудника прежде всего следует ввести его фамилию, имя и отчество.

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

Для получения строки из текстового редактора вызывается функция GetDlgItemText:

GetDlgItemText(hdlg, IDC_NAME, Buffer, 80);

Для определения состояния переключателей вызывается функция IsDlgButtonChecked:

if(IsDlgButtonChecked(hdlg, IDC_PROGRAMMER))
{
  lstrcat(Buffer, "Программист");
}

Эта функция возвращает значение TRUE для включенного переключателя и FALSE - для выключенного.

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

При отмене диалоговой панели вызывается функция EndDialog:

case IDCANCEL:
{
  EndDialog(hdlg, FALSE);
  return TRUE;
}

Идентификаторы всех органов управления описаны в файле dlgtab.hpp (листинг 3.11).


Листинг 3.11. Файл dlgtab\dlgtab.hpp


#define IDC_COMBO      101
#define IDC_STATIC     102
#define IDC_FUNCTION   103
#define IDC_PROGRAMMER 104
#define IDC_ENGINIER   105
#define IDC_SENGINIER  106
#define IDC_OTHER      107
#define IDC_ENGLISH    108
#define IDC_C          109
#define IDC_PASCAL     110
#define IDC_NAME       111

Файл описания ресурсов приведен в листинге 3.12.


Листинг 3.12. Файл dlgtab\dlgtab.res


#include "g:\tcwin\include\windows.h"
#include "dlgtab.hpp"

APPICON ICON "dlgtab.ico"

SELECT DIALOG 12, 28, 157, 138
STYLE DS_MODALFRAME | WS_POPUP |
      WS_CAPTION | WS_SYSMENU
CAPTION "Карточка сотрудника"
BEGIN
  CONTROL "", IDC_NAME, "EDIT",
    ES_LEFT | WS_CHILD | WS_VISIBLE | WS_BORDER |
    WS_GROUP | WS_TABSTOP,
    7, 20, 143, 12
  CONTROL "&Должность", IDC_FUNCTION, "BUTTON",
    BS_GROUPBOX | WS_CHILD | WS_VISIBLE,
    7, 35, 79, 48
  CONTROL "Инженер", IDC_ENGINIER, "BUTTON",
    BS_AUTORADIOBUTTON | WS_CHILD | WS_VISIBLE |
    WS_GROUP | WS_TABSTOP,
    11, 44, 72, 12
  CONTROL "Старший инженер", IDC_SENGINIER, "BUTTON",
    BS_AUTORADIOBUTTON | WS_CHILD | WS_VISIBLE,
    11, 55, 73, 12
  CONTROL "Программист", IDC_PROGRAMMER, "BUTTON",
    BS_AUTORADIOBUTTON | WS_CHILD | WS_VISIBLE,
    11, 67, 73, 12
  CONTROL "&Прочее", IDC_OTHER, "BUTTON",
    BS_GROUPBOX | WS_CHILD | WS_VISIBLE | WS_GROUP,
    7, 84, 80, 47
  CONTROL "English", IDC_ENGLISH, "BUTTON",
    BS_AUTOCHECKBOX | WS_CHILD | WS_VISIBLE |
    WS_GROUP | WS_TABSTOP,
    11, 93, 74, 12
  CONTROL "Знает Си", IDC_C, "BUTTON",
    BS_AUTOCHECKBOX | WS_CHILD | WS_VISIBLE,
    11, 105, 74, 12
  CONTROL "Знает Паскаль", IDC_PASCAL, "BUTTON",
    BS_AUTOCHECKBOX | WS_CHILD | WS_VISIBLE,
    11, 116, 73, 12
  CONTROL "OK", IDOK, "BUTTON",
    BS_DEFPUSHBUTTON | WS_CHILD | WS_VISIBLE |
    WS_GROUP | WS_TABSTOP,
    114, 38, 36, 14
  CONTROL "Cancel", IDCANCEL, "BUTTON",
    BS_PUSHBUTTON | WS_CHILD | WS_VISIBLE,
    114, 56, 36, 14
  ICON "APPICON", -1, 134, 4, 16, 16,
    WS_CHILD | WS_VISIBLE
  CONTROL "Ф.И.О.", -1, "STATIC",
    SS_LEFT | WS_CHILD | WS_VISIBLE,
    7, 9, 27, 8
END

В этом файле нас больше всего интересует расстановка стилей WS_GROUP и WS_TABSTOP.

Стиль WS_TABSTOP используется для тех органов управления, к которым нужно обеспечить доступ с помощью клавиши <Tab>. В нашей диалоговой панели этот стиль имеют следующие органы управления: редактор текста IDC_NAME, используемый для ввода фамилии, имени и отчества сотрудника; переключатель "Инженер" (первый переключатель в группе "Должность"); переключатель "English" (первый переключатель в группе "Прочее"); кнопка с надписью 'OK". Запустив приложение, вы можете убедиться, что если нажимать клавишу <Tab>, фокус ввода будет передаваться между перечисленными выше органами управления.

Стиль WS_GROUP используется для отметки первого органа управления в группе. Внутри группы, созданной с помощью этого стиля, вы можете передавать фокус ввода при помощи клавиш перемещения курсора <Up> и <Down>.

В нашем случае группа "Должность" содержит три переключателя. Первый переключатель в группе ("Инженер") имеет стили WS_GROUP и WS_TABSTOP.

Следующий орган управления, имеющий стиль WS_GROUP, должен относиться к следующей группе органов управления. В нашем случае это орган управления BS_GROUPBOX с заголовком "Прочее". Этот орган управления завершает первую группу, но сам не входит в нее.

Первый орган второй группы органов управления также имеет стили WS_GROUP и WS_TABSTOP.

Последняя группа органов управления включает в себя кнопки "OK" и "Cancel". первая из этих кнопок имеет стиль WS_GROUP.

Пиктограмма и статический орган управления, описанные в последних строках шаблона, не могут получать фокус ввода.

Шаблон диалоговой панели приложения DLGTAB имеет еще одну особенность.

Обратите внимание, что перед буквой "Д" в слове "&Должность" стоит символ "&". Этот же символ расположен перед буквой "П" в слове "&Прочее". Это не опечатка. Мы намеренно использовали символ "&" для того чтобы продемонстрировать еще одну возможность диалоговых панелей. Речь идет о клавиатурном интерфейсе, предназначенном для передачи фокуса ввода органам управления диалоговой панели.

Если вы внимательно посмотрите на создаваемую нашим приложением диалоговую панель, то сможете заметить, что буквы, перед которыми стоит знак "&", отображаются подчеркнутыми (рис. 3.6). Клавиши, соответствующие подчеркнутым буквам, можно использовать для непосредственной передаче фокуса ввода. Если нажать комбинацию клавиш <Alt+Д>, фокус ввода перейдет к первому органу управления из группы "Должность", а если <Alt+П> - к первому органу управления из группы "Прочее".

Когда диалоговая панель содержит много органов управления, выбор нужного с помощью клавиши <Tab> может отнять много времени. Использование описанного только что способа непосредственной передачи фокуса ввода может упростить задачу выбора нужного органа управления.

Файл описания ресурсов ссылается на пиктограмму, приведенную в листинге 3.13.


Листинг 3.13. Файл dlgtab\dlgtab.ico



Файл определения модуля приложения DLGTAB представлен в листинге 3.14.


Листинг 3.14. Файл dlgtab\dlgtab.def


; =============================
; Файл определения модуля
; =============================
NAME DLGTAB
DESCRIPTION 'Приложение DLGTAB, (C) 1994, Frolov A.V.'
EXETYPE windows
STUB 'winstub.exe'
STACKSIZE 5120
HEAPSIZE 1024
CODE preload moveable discardable
DATA preload moveable multiple

3.3. Немодальные диалоговые панели

Другой вид диалоговых панелей, который мы рассмотрим, это немодальные диалоговые панели (modeless dialog boxes).

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

Немодальные диалоговые панели очень удобны для объединения различных инструментальных средств, предназначенных для работы с объектом, расположенным в главном окне или в дочернем окне, созданным главным окном приложения. На рис. 3.8 изображено главное приложение графического редактора PhotoFinish, созданного фирмой ZSoftCorporation, содержащее три немодальные диалоговые панели.

Рис. 3.8. Немодальные диалоговые панели в приложении PhotoFinish

С помощью диалоговой панели "Tools" вы можете выбирать средства для рисования или редактирования изображения, диалоговая панель "Width" предназначена для выбора размеров инструмента, а панель 'Palette" - для выбора цветовой палитры.

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

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

Создание и уничтожение немодальных диалоговых панелей

Для создания немодальных диалоговых панелей используются описанные нами ранее функции CreateDialog, CreateDialogParam, CreateDialogIndirect, CreateDialogIndirectParam.

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

Для обеспечения автоматического отображения немодальной диалоговой панели сразу после создания стиль панели, описанный в шаблоне, должен включать в себя константу WS_VISIBLE. В противном случае для отображения диалоговой панели придется вызывать функцию ShowWindow. Стиль DS_MODALFRAME используется только для модальных диалоговых панелей, поэтому его указывать не надо.

Завершение работы немодальной диалоговой панели следует выполнять с помощью функции DestroyWindow, указав ей в качестве параметра идентификатор окна панели, полученный от функции CreateDialog или от аналогичной функции, создающей немодальную диалоговую панель. Функция EndDialog должна использоваться только для завершения работы модальных диалоговых панелей.

Изменения в цикле обработки сообщений

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

Для разделения этих сообщений цикл обработки должен вызывать функцию IsDialogMessage:

BOOL WINAPI IsDialogMessage(HWND hwndDlg, MSG FAR* lpmsg);

Функция IsDialogMessage определяет, предназначено ли сообщение, определяемое параметром lpmsg, для немодальной диалоговой панели с идентификатором окна, равным hwndDlg. Если предназначено, функция сама выполняет обработку такого сообщения и возвращает значение TRUE. В противном случае возвращается значение FALSE.

Приложение DIALOGNM

Приложение DIALOGNM панель, аналогичную той, что создается приложением DIALOG. Но теперь эта диалоговая панель создается как немодальная (рис. 3.9).

Рис. 3.9. Немодальная диалоговая панель в приложении DIALOGNM

Главный файл приложения DIALOGNM приведен в листинге 3.15.


Листинг 3.15. Файл dialognm\dialognm.cpp


// ----------------------------------------
// Простейшая немодальная диалоговая панель
// ----------------------------------------

#define STRICT
#include <windows.h>
#include <mem.h>

#define IDB_Button1 1

// Прототипы функций
BOOL InitApp(HINSTANCE);
LRESULT CALLBACK _export WndProc(HWND, UINT, WPARAM, LPARAM);
BOOL CALLBACK    _export DlgProc(HWND, UINT, WPARAM, LPARAM);

// Имя класса окна
char const szClassName[]   = "DialogNMAppClass";

// Заголовок окна
char const szWindowTitle[] = "Modeless Dialog Box";

// Идентификатор окна диалоговой панели
HWND    hwndDlg;

// Идентификатор главного окна приложения
HWND    hwndMain;

HINSTANCE hInst;

// =====================================
// Функция WinMain
// =====================================
#pragma argsused

int PASCAL
WinMain(HINSTANCE hInstance,
        HINSTANCE hPrevInstance,
        LPSTR     lpszCmdLine, 
        int       nCmdShow)   
{
  MSG  msg;   // структура для работы с сообщениями

  HWND hButton1;

  // Инициализируем приложение
  if(!InitApp(hInstance))
      return FALSE;

  hInst = hInstance;

  // После успешной инициализации приложения создаем
  // главное окно приложения
  hwndMain = CreateWindow(
    szClassName,         // имя класса окна
    szWindowTitle,       // заголовок окна
    WS_OVERLAPPEDWINDOW, // стиль окна
    CW_USEDEFAULT,       // задаем размеры и расположение
    CW_USEDEFAULT,       // окна, принятые по умолчанию 
    CW_USEDEFAULT,
    CW_USEDEFAULT,
    0,                   // идентификатор родительского окна
    0,                   // идентификатор меню
    hInstance,           // идентификатор приложения
    NULL);               // указатель на дополнительные
                         // параметры

  // Если создать окно не удалось, завершаем приложение
  if(!hwndMain)
    return FALSE;

  // Рисуем главное окно
  ShowWindow(hwndMain, nCmdShow);
  UpdateWindow(hwndMain);

  // Создаем кнопку
  hButton1 = CreateWindow("button", "About...",
    WS_CHILD | WS_VISIBLE | BS_PUSHBUTTON,
    20, 20,
    90, 30,
    hwndMain,
    (HMENU) IDB_Button1,
    hInstance, NULL);

  if(!hButton1)
    return FALSE;

  // До запуска цикла обработки сообщений
  // идентификатор диалоговой панели должен
  // быть равен 0, т. к. панель еще не создана 
  hwndDlg = (HWND)0;

  // Запускаем цикл обработки сообщений
  while(GetMessage(&msg, 0, 0, 0))
  {
    // Выделяем сообщения для диалоговой панели,
    // если эта панель уже создана
    if(hwndDlg == 0 || !IsDialogMessage(hwndDlg, &msg))
    {
      TranslateMessage(&msg);
      DispatchMessage(&msg);
    }
  }
  return msg.wParam;
}

// =====================================
// Функция InitApp
// Выполняет регистрацию класса окна
// =====================================

BOOL
InitApp(HINSTANCE hInstance)
{
  ATOM aWndClass; // атом для кода возврата
  WNDCLASS wc;    // структура для регистрации
                  // класса окна

  memset(&wc, 0, sizeof(wc));

  wc.style = 0;
  wc.lpfnWndProc = (WNDPROC) WndProc;
  wc.cbClsExtra = 0;
  wc.cbWndExtra = 0;
  wc.hInstance = hInstance;
  wc.hIcon = LoadIcon(NULL, IDI_APPLICATION);
  wc.hCursor = LoadCursor(NULL, IDC_ARROW);
  wc.hbrBackground = (HBRUSH)(COLOR_WINDOW + 1);
  wc.lpszMenuName = (LPSTR)NULL;
  wc.lpszClassName = (LPSTR)szClassName;

  // Регистрация класса
  aWndClass = RegisterClass(&wc);

  return (aWndClass != 0);
}

// =====================================
// Функция WndProc
// =====================================

LRESULT CALLBACK _export
WndProc(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam)
{
  static DLGPROC lpfnDlgProc;

  switch (msg)
  {
    case WM_COMMAND:
    {
      // Если нажата кнопка, выводим
      // диалоговую панель
      if(wParam == IDB_Button1)
      {
        // Переходник для функции диалоговой панели
        lpfnDlgProc = 
          (DLGPROC)MakeProcInstance((FARPROC)DlgProc, hInst);

        // Создаем немодальную диалоговую панель
        hwndDlg = CreateDialog(hInst, "DIALOG_OK",
                  hwnd, lpfnDlgProc);
      }
      return 0;
    }

    case WM_DESTROY:
    {


// Удаляем диалоговую панель
      DestroyWindow(hwndDlg);

      PostQuitMessage(0);
      return 0;
    }
  }
  return DefWindowProc(hwnd, msg, wParam, lParam);
}

// =====================================
// Функция DlgProc
// =====================================
#pragma argsused

BOOL CALLBACK _export
DlgProc(HWND hdlg, UINT msg, WPARAM wParam, LPARAM lParam)
{
  switch(msg)
  {
    // Инициализация диалоговой панели
    case WM_INITDIALOG:
    {
      return TRUE;
    }

    case WM_COMMAND:
    {
      switch(wParam)
      {
        // Сообщение от кнопки "OK"
        case IDOK:

        // Отмена диалоговой панели.
        // Это сообщение приходит, когда пользователь
        // нажимает на клавишу <Esc>
        case IDCANCEL:
        {
          // Уничтожаем диалоговую панель
          DestroyWindow(hdlg);

          // Завершаем работу приложения
          DestroyWindow(hwndMain);
          return TRUE;
        }
      }
    }
  }
  return FALSE;
}

Функция WinMain приложения DIALOGNM аналогична функции WinMain приложения DIALOG. Она создает главное окно приложения, в котором располагает кнопку 'About...", вслед за чем запускает цикл обработки сообщений.

Так как наше приложение создает немодальную диалоговую панель, в цикле обработки сообщений мы вызываем функцию IsDialogMessage:

hwndDlg = (HWND)0;
while(GetMessage(&msg, 0, 0, 0))
{
  if(hwndDlg == 0 || !IsDialogMessage(hwndDlg, &msg))
  {
    TranslateMessage(&msg);
    DispatchMessage(&msg);
  }
}

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

Когда немодальная диалоговая панель будет создана, идентификатор ее окна будет записан в переменную hwndDlg. После этого в работу включится функция IsDialogMessage. Она будет "вылавливать" в очереди приложения сообщения, предназначенные для диалоговой панели hwndDlg и выполнять их обработку. Все остальные сообщения будут обработаны обычным образом функциями TranslateMessage и DispatchMessage.

Функция главного окна приложения обрабатывает сообщение WM_COMMAND, поступающее от кнопки 'About...". В ответ на это сообщение создается немодальная диалоговая панель, для чего вызывается функция CreateDialog:

hwndDlg = CreateDialog(hInst,"DIALOG_OK", hwnd, lpfnDlgProc);

Идентификатор окна созданной диалоговой панели записывается в глобальную переменную hwndDlg, после чего в цикле обработки сообщений начинает действовать функция IsDialogMessage.

При завершении работы приложения через системное меню главного окна мы удаляем диалоговую панель, вызывая функцию DestroyWindow:

case WM_DESTROY:
{
  DestroyWindow(hwndDlg);
  PostQuitMessage(0);
  return 0;
}

Функция диалоговой панели обрабатывает сообщения WM_INITDIALOG и WM_COMMAND.

Обработка сообщения WM_INITDIALOG сводится к возвращению значения TRUE. Если приходит сообщение WM_COMMAND с идентификаторами IDOK или IDCANCEL, функция диалоговой панели завершает диалоговую панель, уничтожая свое окно и главное окно приложения:

case IDOK:
case IDCANCEL:
{
  DestroyWindow(hdlg);
  DestroyWindow(hwndMain);
  return TRUE;
}

Файл ресурсов приложения, содержащий шаблон немодальной диалоговой панели, приведен в листинге 3.16.


Листинг 3.16. Файл dialognm\dialognm.rc


#include "g:\bc\include\windows.h"

APPICON ICON "appicon.ico"

DIALOG_OK DIALOG 25, 34, 152, 67
STYLE WS_POPUP | WS_CAPTION | WS_SYSMENU | WS_VISIBLE
CAPTION "Приложение DIALOGNM"
BEGIN
        CTEXT "Microsoft Windows Application\n"
            "Приложение DIALOGNM\n"
            "(C) Frolov A.V., 1994", -1, 28, 9, 117, 26,
              WS_CHILD | WS_VISIBLE | WS_GROUP
        ICON "APPICON", -1, 6, 14, 16, 16, WS_CHILD | WS_VISIBLE
        DEFPUSHBUTTON "OK", IDOK, 56, 43, 36, 14,
              WS_CHILD | WS_VISIBLE | WS_TABSTOP
END

Обратите внимание на стиль немодальной диалоговой панели:

STYLE WS_POPUP | WS_CAPTION | WS_SYSMENU | WS_VISIBLE

В отличие от стиля модальной диалоговой панели приложения DIALOG, стиль немодальной диалоговой панели приложения DIALOGNM содержит константу WS_VISIBLE и не содержит константы DS_MODALFRAME.

В файле ресурсов кроме шаблона диалоговой панели есть ссылка на пиктограмму (листинг 3.17).


Листинг 3.17. Файл dialognm\appicon.ico



Файл определения модуля приложения DIALOGNM приведен в листинге 3.18.


Листинг 3.18. Файл dialognm\dialognm.def


; =============================
; Файл определения модуля
; =============================
NAME DIALOGNM
DESCRIPTION 'Приложение DIALOGNM, (C) 1994, Frolov A.V.'
EXETYPE windows
STUB 'winstub.exe'
STACKSIZE 5120
HEAPSIZE 1024
CODE preload moveable discardable
DATA preload moveable multiple

3.4. Функция MessageBox

Теперь, когда вы знаете, что такое модальные и немодальные диалоговые панели, вспомним о нашем старом друге - функции MessageBox, с которой вы начали изучение программного интерфейса Windows:

int WINAPI MessageBox(HWND hwndParent, LPCSTR lpszText,
    LPCSTR lpszTitle, UINT fuStyle);

Напомним, что эта функция создает на экране диалоговую панель с текстом, заданным параметром lpszText и заголовком, заданным параметром lpszTitle. Если заголовок указан как NULL, используется заголовок по умолчанию - строка "Error".

Параметр hwndParent указывает идентификатор родительского окна, создающего диалоговую панель. Этот параметр можно указывать как NULL, в этом случае у диалоговой панели не будет родительского окна. Вы можете вызвать функцию MessageBox из функции диалога, в этом случае первый параметр должен содержать идентификатор окна диалоговой панели.

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

КонстантаОписание
MB_ABOTRETRYIGNOREДиалоговая панель содержит три кнопки с надписями "Abort", "Retry", "Ignore"
MB_OKДиалоговая панель содержит одну кнопку "OK"
MB_OKCANCELДве кнопки с надписями "OK", "Cancel"
MB_RETRYCANCELДве кнопки с надписями "Retry", "Cancel"
MB_YESNOДве кнопки с надписями "Yes", "No"
MB_YESNOCANCELТри кнопки с надписями "Yes", "No", "Cancel"

К этим константам при помощи логической операции ИЛИ можно добавлять другие константы.

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

КонстантаОписание
MB_DEFBUTTON1Первая кнопка используется по умолчанию
MB_DEFBUTTON2Вторая кнопка используется по умолчанию
MB_DEFBUTTON3Третья кнопка используется по умолчанию

С помощью следующих трех констант вы можете влиять на модальность диалоговой панели:

КонстантаОписание
MB_APPLMODALСоздается модальная диалоговая панель. Окно, указанное параметром hwndParent, переводится в неактивное состояние до тех пор, пока пользователь не завершит работу с диалоговой панелью. Пользователь может переключиться на другое приложение. Этот стиль используется по умолчанию
MB_SYSTEMMODALДо тех пор, пока пользователь не завершит работу с диалоговой панелью, все остальные приложения переводятся в неактивное состояние
MB_TASKMODALАналогично MB_APPLMODAL за исключением того, что если параметр hwndParent имеет значение NULL, блокируются все окна верхнего уровня, принадлежащие данной задаче. Этот стиль используется тогда, когда идентификатор родительского окна неизвестен, но тем не менее требуется перевести все окна текущего приложения в неактивное состояние до тех пор, пока пользователь не завершит работу с диалоговой панелью

С помощью следующих шести констант вы можете создать в диалоговой панели пиктограмму:

КонстантаВнешний вид пиктограммы
MB_ICONASTERISK
MB_ICONEXCLAMATION
MB_ICONHAND
MB_ICONINFORMATION
MB_ICONQUESTION
MB_ICONSTOP

Функция MessageBox возвращает нулевое значение при ошибке или одну из следующих констант в зависимости от того, какую кнопку нажал пользователь.

КонстантаНазвание кнопки
IDABORT"Abort"
IDCANCEL"Cancel"
IDIGNORE"Ignore"
IDNO"No"
IDOK"OK"
IDRETRY"Retry"
IDYES"Yes"

Назад       Содержание       Вперёд