Электронная библиотека книг Александра Фролова и Григория Фролова.
Shop2You.ru Создайте свой интернет-магазин
Библиотека
Братьев
Фроловых

Операционная система Microsoft Windows 3.1 для программиста

© Александр Фролов, Григорий Фролов
Том 12, М.: Диалог-МИФИ, 1993, 255 стр.

[Назад] [Содеожание] [Дальше]

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

[Назад] [Содеожание] [Дальше]