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

         

Файл combo\combo.cpp


// ---------------------------------------- // Использование органа управления // класса "combobox" для просмотра // содержимого каталога // ----------------------------------------

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

#define ID_LIST 1 #define ID_BUTTON 2

BOOL InitApp(HINSTANCE);
LRESULT CALLBACK _export WndProc(HWND, UINT, WPARAM, LPARAM);

char const szClassName[] = "ComboAppClass"; char const szWindowTitle[] = "Выбор файла"; 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;

ShowWindow(hwnd, nCmdShow);
UpdateWindow(hwnd);

while(GetMessage(&msg, 0, 0, 0)) { 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 HWND hComboBox; static HWND hButton;

switch (msg) { case WM_CREATE: { hComboBox = CreateWindow("ComboBox", NULL, WS_CHILD | WS_VISIBLE | WS_VSCROLL | CBS_AUTOHSCROLL | CBS_SIMPLE, 30, 30, 200, 200, hwnd, (HMENU) ID_LIST, hInst, NULL);

SendMessage(hComboBox, CB_DIR, DDL_READWRITE | DDL_READONLY | DDL_HIDDEN | DDL_SYSTEM | DDL_DIRECTORY | DDL_DRIVES | DDL_ARCHIVE, (LPARAM)(LPSTR)"*.*");

hButton = CreateWindow("button", "OK", WS_CHILD | WS_VISIBLE | BS_PUSHBUTTON, 250, 30, 50, 20, hwnd, (HMENU) ID_BUTTON, hInst, NULL);

return 0; }

case WM_SETFOCUS: { SetFocus(hComboBox);
return 0; }

case WM_COMMAND: { if(wParam == ID_LIST) { if(HIWORD(lParam) == (unsigned)CBN_ERRSPACE) { MessageBox(hwnd, "Мало памяти", szWindowTitle, MB_OK);
}

else if(HIWORD(lParam) == CBN_DBLCLK) { SendMessage(hwnd, WM_COMMAND, ID_BUTTON, 0L);
return 0; }

else if(HIWORD(lParam) == CBN_SELCHANGE) { int uSelectedItem, nSize; char Buffer[256]; HDC hdc;

uSelectedItem = (int)SendMessage(hComboBox, CB_GETCURSEL, 0, 0L);

if(uSelectedItem != CB_ERR) { // Получаем строку SendMessage(hComboBox, CB_GETLBTEXT, uSelectedItem, (LPARAM)Buffer);

hdc = GetDC(hwnd);
nSize = lstrlen(Buffer);

TextOut(hdc, 250, 60, (LPSTR)" ", 25);
TextOut(hdc, 250, 60, (LPSTR)Buffer, nSize);

ReleaseDC(hwnd, hdc);
} } }

else if(wParam == ID_BUTTON) { int uSelectedItem; char Buffer[256];

uSelectedItem = (int)SendMessage(hComboBox, CB_GETCURSEL, 0, 0L);
if(uSelectedItem != LB_ERR) { SendMessage(hComboBox, CB_GETLBTEXT, uSelectedItem, (LPARAM)Buffer);
if(Buffer[0] == '[') { Buffer[lstrlen(Buffer) - 1] = '\0'; if(chdir(&Buffer[1]) != 0) { Buffer[3] = '\0'; lstrcat(Buffer, ":\\");
if(chdir(&Buffer[2]) == 0) { AnsiLowerBuff(&Buffer[2], 1);
setdisk(Buffer[2] - 'a');
} } SendMessage(hComboBox, CB_RESETCONTENT, 0, 0L);
SendMessage(hComboBox, CB_DIR, DDL_READWRITE | DDL_READONLY | DDL_HIDDEN | DDL_SYSTEM | DDL_DIRECTORY | DDL_DRIVES | DDL_ARCHIVE, (LPARAM)(LPSTR)"*.*");
} else { MessageBox(hwnd, Buffer, szWindowTitle, MB_OK);
} } } return 0; }

case WM_PAINT: { HDC hdc; PAINTSTRUCT ps;

hdc = BeginPaint(hwnd, &ps);
TextOut(hdc, 30, 10, "Выберите файл, каталог или диск", 31);
EndPaint(hwnd, &ps);
return 0; }

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

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


Файл combo\combo.def


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



Файл 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; } } }


Файл 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), на которую есть ссылка в шаблоне диалоговой панели.



Файл 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.



Файл 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



Файл 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). Этот файл необходимо включить как в главный файл приложения, так и в файл описания ресурсов.


Файл dlgcombo\dlgcombo.hpp


#define IDC_COMBO 101 #define IDC_STATIC 102

Файл описания ресурсов представлен в листинге 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).



Файл dlgcombo\appicon.ico


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

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

Файл определения модуля приложения DLGCOMBO приведен в листинге 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.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).


Файл 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.



Файл 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.


Файл dlgtab\dlgtab.ico


Файл определения модуля приложения DLGTAB представлен в листинге 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



Файл 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.


Файл 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).



Файл dialognm\appicon.ico


Файл определения модуля приложения DIALOGNM приведен в листинге 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



Литература


Charles Petzold. Programming Windows 3.1. Microsoft Press. One Microsoft Way. Redmont, Washington, 1992

Brent Rector. Developing Windows 3.1 Applications with Microsoft C/C++. SAMS, 1992

Daniel A. Norton. Writing Windows Device Drivers. Addison-Wesley Publishing Company, 1992

А. В. Фролов, Г. В. Фролов. Библиотека системного программиста. Том 6. Защищенный режим процессоров Intel 80286/80386/80486. Москва, "ДИАЛОГ-МИФИ", 1993

Windows. Справочник для программистов. Версия 3.0. Часть 1, 2. Москва, "Научный центр", 1991

The Windows Interface: An Application Design Guide. Microsoft Windows Software Development Kit. Microsoft, 1992

М. Эллис, Б. Строуструп. Справочное руководство по языку программирования C++ с комментариями. Москва, МИР, 1992



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


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

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

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

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

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

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

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



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


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

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.
Приведем список обозначений органов управления и возможных стилей для первого формата.

Обозначение органа управления Класс окна Описание и стиль, используемый по умолчанию
CHECHBOX button Переключатель в виде прямоугольника BS_CHECKBOX, WS_TABSTOP
CTEXT static Строка текста, выровненная по центру SS_CENTER, WS_GROUP
DEFPUSHBUTTON button Кнопка, выбираемая в диалоговой панели по умолчанию BS_DEFPUSHBUTTON, WS_TABSTOP
GROUPBOX button Прямоугольник, объединяющий группу органов управления BS_GROUPBOX
ICON static ПиктограммаSS_ICON Параметры width, height и style можно не указывать
LTEXT static Строка текста, выровненная по левой границе органа управления SS_LEFT, WS_GROUP
PUSHBUTTON button КнопкаBS_PUSHBUTTON, WS_TABSTOP
RADIOBUTTON button Переключатель в виде кружка (радиопереключатель) BS_RADIOBUTTON, WS_TABSTOP
RTEXT static Строка текста, выровненная по правой границе органа управления SS_RIGHT, WS_GROUP
<


Стили WS_TABSTOP и WS_GROUP будут описаны позже.
Второй формат используется для описания списков, редакторов текста и полос просмотра:
CtlType ID, x, y, width, height [,style]
В этом формате нет параметра "Текст", остальные параметры используются так же, как и в первом формате.
Приведем список обозначений органов управления и возможных стилей для второго формата.

Обозначение органа управления Класс окна Описание и стиль, используемый по умолчанию
COMBOBOX combobox Список с окном редактирования CBS_SIMPLE, WS_TABSTOP
LISTBOX listbox Список LBS_NOTIFY, WS_BORDER
EDITTEX edit Редактор текста ES_LEFT, WS_BORDER, WS_TABSTOP
SCROLLBARS scrollbar Полоса просмотраSBS_HORZ

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

Определение полос просмотра при создании окна


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

Для того чтобы у окна появились вертикальная и горизонтальная полосы просмотра, при регистрации класса окна в третьем парамере функции CreateWindow необходимо указать стили окна WS_VSCROLL или WS_HSCROLL (для того чтобы окно имело и вертикальную, и горизонтальную полосы просмотра, следует указать оба стиля):

hwnd = CreateWindow(szClassName, szWindowTitle, // стиль окна WS_OVERLAPPEDWINDOW | WS_VSCROLL | WS_HSCROLL, CW_USEDEFAULT, CW_USEDEFAULT, CW_USEDEFAULT, CW_USEDEFAULT, 0, 0, hInstance, NULL);



Органы управления


2.1.

2.2.

2.3.

2.4.

2.5.

2.6.

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

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

Например, приложение может создать дочернее окно на базе предопределенного класса "button". Окна этого класса - хорошо знакомые вам кнопки. Внешне поведение кнопки выглядит достаточно сложным. Когда вы устанавливаете на кнопку курсор мыши и нажимаете левую клавишу мыши, кнопка "уходит вглубь". Когда вы отпускаете клавишу мыши или выводите курсор мыши из области кнопки, кнопка возвращается в исходное положение. Если в диалоговой панели имеется несколько кнопок, вы можете при помощи клавиши <Tab> выбрать нужную, при этом на ней появится пунктирная рамка. Кнопку можно нажимать не только мышью, но и клавиатурой. Кнопка может находиться в активном и пассивном состоянии, причем в последнем случае вы не можете на нее нажать, а надпись, расположенная на кнопке, выводится пунктирным шрифтом. Кстати, надпись на кнопке может изменяться в процессе работы приложения.

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

Но, к счастью, Windows создана как объектно-ориентированная система. Для таких объектов, как органы управления, в Windows созданы классы окна.
Например, в Windows имеется класс окна "button", на базе которого можно создавать кнопки. Функция окна, определенная внутри Windows для класса "button", обеспечивает все функциональные возможности кнопки, описанные выше. Поэтому программисту достаточно создать собственное дочернее окно на базе класса "button". Это окно сразу становится кнопкой и начинает вести себя как кнопка.

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

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

Изучение органов управления, встроенных в Windows, мы начнем с кнопок, создаваемых на базе предопределенного класса окна "button".


Передача сообщений органу управления


В операционной системе Windows широко используется практика передачи сообщений от одного объекта к другому. Приложение BUTTON демонстрирует передачу сообщения WM_COMMAND от кнопки родительскому окну, создавшему эту кнопку. Однако родительское окно может само послать сообщение кнопке или любому другому органу управления, если оно знает его идентификатор.

Существует два способа передачи сообщений.

Первый способ - запись сообщения в очередь приложения. Он основан на использовании функции PostMessage:

BOOL WINAPI PostMessage(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam);

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

Параметр uMsg задает идентификатор передаваемого сообщения. Параметры wParam и lParam используются для передачи параметров сообщения.

Второй способ - непосредственная передача сообщения функции окна минуя очередь сообщений. Этот способ реализуется функцией SendMessage:

LRESULT WINAPI SendMessage(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam);

Параметры функции SendMessage используются аналогично параметрам функции PostMessage. Но в отличие от последней функция SendMessage вызывает функцию окна и возвращает управление только после возврата из функции окна.

Возвращаемое функцией SendMessage значение зависит от обработчика сообщения в функции окна.



Переключатели


Очень часто в приложениях Windows требуется организовать выбор различных режимов работы. На рис. 2.3. показана диалоговая панель "Options", которая используется в текстовом процессоре Microsoft Word for Windows для настройки параметров клавиатуры.

Рис. 2.3. Диалоговая панель для настройки параметров клавиатуры в текстовом процессоре Microsoft Word for Windows

В этой диалоговой панели есть множество различных управляющих органов. В правом нижнем углу расположен орган управления (группа) в виде прямоугольника с заголовком "Context". Это окно класса "button", имеющее стиль BS_GROUPBOX. Внутри него расположены два переключателя. Они созданы также на базе класса "button", но имеют стиль BS_RADIOBUTTON (или BS_AUTORADIOBUTTON).

Группа с названием "Short Key" содержит два переключателя, созданных на базе окна "button", но имеющих стиль BS_CHECKBOX (или BS_AUTOCHECKBOX).

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

Переключатели BS_CHECKBOX, BS_AUTOCHECKBOX, BS_3STATE, BS_AUTO3STATE используются как отдельные независимые переключатели. Из них можно сделать переключатель с независимой фиксацией, в котором одновременно могут быть включены несколько переключателей.

Разумеется, все сказанное выше относительно использования переключателей имеет скорее характер рекомендаций, чем обязательное требование. Однако при разработке приложения вам необходимо позаботиться о том, чтобы интерфейс пользователя соответствовал стандарту, изложенному в руководстве по разработке пользовательского интерфейса (это руководство поставляется в составе SDK и называется The Windows Interface: An Application Design Guide).
В этом случае органы управления вашего приложения будут делать то, что ожидает от них пользователь, освоивший работу с другими приложениями Windows.

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

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

При изменении состояния переключателя родительское окно получает сообщение WM_COMMAND с кодом извещения BN_CLICKED.

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

Слово "AUTO" в названии стиля переключателя используется для обозначения режима автоматической перерисовки переключателя при изменении его состояния. О чем здесь идет речь?

Когда вы нажимаете кнопку, имеющую стиль BS_PUSHBUTTON или BS_DEFPUSHBUTTON, она автоматически уходит "вглубь", т. е. автоматически перерисовывается в соответствии со своим текущим состоянием. Переключатели BS_CHECKBOX, BS_RADIOBUTTON, а также BS_3STATE не перерисовываются при их переключении. Вы должны их перерисовывать сами, посылая им сообщение BM_SETCHECK:



SendMessage(hButton, BM_SETCHECK, 1, 0L);

Параметр wParam сообщения BM_SETCHECK определяет состояние переключателя, которое необходимо установить:

Значение Описание
0 Установка переключателя в выключенное состояние (прямоугольник не перечеркнут, в кружке нет точки)
1 Установка переключателя во включенное состояние (прямоугольник перечеркнут, в кружке имеется точка)
2 Установка переключателя в неактивное состояние. Это значение используется только для переключателей, имеющих стиль BS_3STATE или BS_AUTO3STATE. При этом переключатель будет изображен серым цветом
Параметр lParam сообщения BM_SETCHECK должен быть равен 0.

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

WORD nState; nState = (WORD) SendMessage(hButton, BM_GETCHECK, 0, 0L);

Парамеры wParam и lParam сообщения BM_GETCHECK должны быть равны 0.

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

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


Пиктограмма


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

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

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

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

Приложения Windows активно работают с графическими изображениями, состоящими из отдельных пикселов и имеющих прямоугольную или квадратную форму. Такие изображения называются bitmap (битовый образ). Для представления цвета отдельного пиксела в изображении может быть использовано различное количество бит памяти. Например, цвет пиксела черно-белого изображения может быть представлен одним битом, для представления цвета 16-цветного изображения нужно 4 бита. Цвет одного пиксела изображения в режиме TrueColor представляется 24 битами памяти.

Можно считать, что пиктограмма является упрощенным вариантом изображения bitmap. Пиктограммы хранятся в файлах с расширением имени *.ico (хотя можно использовать любое расширение). В одном файле обычно находится несколько вариантов пиктограмм. Когда Windows рисует пиктограмму, он выбирает из файла наиболее подходящий для текущего режима работы видеоадаптера.



Пиктограммы


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



Полоса просмотра


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

Полоса просмотра представляет собой орган управления, созданный на базе предопределенного класса "scrollbar". Горизонтальная и вертикальная полоса просмотра посылают в функцию родительского окна сообщения WM_HSCROLL и WM_VSCROLL, соответственно. Параметр WParam этих сообщений несет в себе информацию о действии, которое вы выполнили над полосой просмотра.

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

Рис. 2.7. Вертикальная полоса просмотра

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

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

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

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

Горизонтальная полоса просмотра состоит из тех же объектов, что и вертикальная. Она обеспечивает свертку документа в горизонтальном направлении.


Приложение BRUSH


Приложение BRUSH демонстрирует использование изображения типа bitmap для закрашивания внутренней области окна (рис. 1.15).

Рис. 1.15. Закрашивание окна в приложении BRUSH

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



Приложение BUTNCTL


Приложение BUTNCTL создает в своем главном окне пять кнопок (рис. 2.2).

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

Кнопка "Button 1" работает также, как и в предыдущем приложении. А именно, если на нее нажать, появится диалоговая панель с сообщением о том, что нажата первая кнопка.

Остальные кнопки управляют первой кнопкой. С помощью кнопки "PUSH" вы можете нажимать первую кнопку (и она останется в нажатом состоянии), с помощью кнопки "POP" вы сможете вернуть первую кнопку в исходное состояние. Кнопка "OFF" блокирует первую кнопку, не пропуская в ее функцию окна сообщения от клавиатуры и мыши, кнопка "ON" разблокирует первую кнопку.

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

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



Приложение BUTTON


Приложение BUTTON демонстрирует способ создания стандартных кнопок и обработку сообщений от них. В главном окне приложения создается две кнопки с названием "Button 1" и "Button 2". Если нажать на одну из них, на экране появится диалоговая панель с сообщением о номере нажатой кнопки (рис. 2.1).

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

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



Приложение COMBO


Для иллюстрации методов работы со списком "combobox" приведем исходные тексты приложения COMBO (листинг 2.30). Это приложение создает в своем главном окне (рис. 2.22) список, предназначенный для выбора файлов, каталогов и дисков (аналогично предыдущему приложению LISTDIR).

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

В некоторых случаях орган управления "combobox" удобнее, чем "listbox". Например, если вам надо выбрать имя файла из каталога, содержащего сотни файлов, простой просмотр списка может отнять много времени. Типичный пример - поиск файлов win.ini и system.ini в каталоге операционной системы Windows. В нашем приложении COMBO создается список, имеющий стиль CBS_SIMPLE. Этот стиль позволяет упростить поиск, если вы знаете хотя бы несколько первых букв имени файла. Наберите начало имени в окне редактирования, и имя нужного файла окажется перед вашими глазами (рис. 2.22).

Для экономии места в книге мы удалили все комментарии из исходного текста, так как мы уже разбирали аналогичное приложение LISTDIR. Исходные тексты с комментариями вы можете найти на дискете, которая прилагается к книге.



Приложение CURSOR


Приложение CURSOR демонстрирует использование курсора, нарисованного при помощи приложения Resource Workshop, при регистрации класса окна. Это приложение рисует в главном окне приложения все встроенные курсоры и один курсор из файла описания ресурсов (рис. 1.12).

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

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



Приложение DIALOG


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

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

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

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

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



Приложение DIALOGNM


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

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

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



Приложение DLGCOMBO


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

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

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



Приложение DLGTAB


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

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

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

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

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

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

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



Приложение EDIT


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

Приложение EDIT (рис. 2.11) демонстрирует работу с простейшим однострочным текстовым редактором.

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

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

После ввода текста нажмите клавишу "OK". На экране появится сообщение, состоящее из введенного вами текста (рис. 2.12).

Рис. 2.12. Сообщение приложения EDIT

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



Приложение ICO


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

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



Приложение LISTBOX


А теперь перейдем к практике. Приложение LISTBOX (рис. 2.17) создает простейший одноколоночный список.

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

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

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

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



Приложение LISTDIR


Приложение LISTDIR использует список для выбора файла (рис. 2. 18).

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

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

Строго говоря, для выбора файлов лучше пользоваться стандартными диалоговыми панелями "Open" и "Save As" (как мы это делали в наших предыдущих приложениях). В этом случае внешний вид диалоговой панели будет привычен для пользователя. Однако для выбора файлов, каталогов или дисков вы можете использовать и свои средства.

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



Приложение OEM3ANSI


В предыдущем томе "Библиотеки системного программиста" мы привели исходные тексты приложения OEM2ANSI, преобразующего файла из кодировки OEM (принятую в MS-DOS) в кодировку ANSI (принятую в Windows). В некоторых случаях нужны дополнительные таблицы перекодировки. Например, при переносе текстовых файлов из среды операционных систем ЕС ЭВМ (IBM 370) в среду Windows иногда требуется выполнять замену сходных по начертанию букв латинского алфавита на буквы русского алфавита или наоборот, букв русского алфавита на буквы латинского алфавита. Это связано с тем, что в некоторых случаях русские тексты, подготовленные в системах ЕС ЭВМ, содержат латинские буквы. Например, вместо русской заглавной буквы "А" используется латинская заглавная буква "A". Есть шрифты, где эти буквы имеют разное начертание. Кроме того, при смешивании латинских и русских букв могут появится проблемы с сортировкой.

Поэтому для таких текстов перед преобразованием из OEM в ANSI требуется выполнять дополнительные преобразования.

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

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



Приложение OWNBUT


Приложение OWNBUT создает кнопку со стилем BS_OWNERDRAW. Функция главного окна приложения рисует кнопку при помощи трех изображений bitmap, загружая их при необходимости из ресурсов. Первое изображение предназначено для рисования отжатой кнопки, второе - нажатой, и третье - неактивной.

Внешний вид главного окна приложения показан на рис. 2.5.

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

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

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



Приложение SCRLMET


Наше следующее приложение демонстрирует использование вертикальной полосы просмотра, определенной в стиле окна, для свертки окна. Оно выводит в окно метрики текста (рис. 2.9).

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

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

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



Приложение SCROLL


Приложение SCROLL представляет собой простейший пример использования горизонтальной полосы просмотра для изменения горизонтального размера (ширины) статического органа управления (рис. 2.8).

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

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



Приложение SIZEBOX


Наше следующее приложение создает полосу просмотра, имеющую стиль SBS_SIZEBOX (рис. 2.10).

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

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

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

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



Приложение STATIC


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

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

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



Приложение STRING


Приложение STRING (листинг 1.1) демонстрирует использование таблицы строк для хранения названия приложения, заголовка окна и строки формата для вывода текста сообщения.



Приложение TEDIT


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

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

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

Кнопка "New" предназначена для создания нового текста. Если перед тем как нажать на эту кнопку вы загрузили в редактор текст (или набрали его при помощи клавиатуры), на экран будет выдано предупреждающее сообщение о том, что содержимое редактируемого файла была изменено и его нужно сохранить (рис. 2.14).

Рис. 2.14. Предупреждающее сообщение

Если нажать на кнопку "Yes", вы вернетесь в режим редактирования и сможете сохранить текст, нажав кнопку "Save". Если же нажать на кнопку "No", содержимое окна редактирования будет стерто и вы сможете набирать новый текст.

Кнопка "Open" предназначена для загрузки в редактор текстового файла. Если нажать на эту кнопку, на экране появится стандартная диалоговая панель "Open", с помощью которой вы сможете загрузить файл (рис. 2.15).

Рис. 2.15. Диалоговая панель "Open"

Если перед загрузкой нового файла окно редактирования содержало несохраненный текст, на экран будет выведено предупреждающее сообщение (рис. 2.14).

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

Кнопка "Save" предназначена для сохранения текста в файле. Если нажать на эту кнопку, на экране появится стандартная диалоговая панель "Save As" (рис. 2.16), с помощью которой можно выбрать файл для сохранения текста.

Рис. 2.16. Диалоговая панель "Save As"

И, наконец, последняя кнопка с надписью "Exit" завершает работу приложения. Если перед завершением в окне редактирования имеется несохраненный текст, на экране появляется знакомое вам предупреждающее сообщение (рис. 2.14).

Из приведенного выше описания видно, что наш редактор текста выполняет достаточно сложные функции. Однако тем не менее листинг основного файла исходного текста приложения (листинг 2.24) занимает чуть больше десяти страниц, а размер загрузочного модуля составляет примерно 7 Кбайт. Это возможно благодаря тому, что вся основная работа выполняется не приложением, а модулями, расположенными в библиотеках динамической загрузки операционной системы Windows.



Прямоугольные рамки


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

Цвета рамки соответствуют системным цветам, определенным в Windows. Эти цвета можно изменить при помощи стандартного приложения Windows с названием Control Panel. Черный цвет соответствует системному цвету COLOR_WINDOWFRAME, используемому для изображения рамок окон Windows. Белый цвет соответствует цвету COLOR_WINDOW. Это цвет внутренней области окон Windows. И, наконец, серый цвет соответствует цвету фона экрана COLOR_BACKGROUND.

При создании статических органов управления со стилями SS_BLACKFRAME, SS_GRAYFRAME и SS_WHITEFRAME текст заголовка окна не используется. Соответствующий параметр функции CreateWindow следует указать как NULL.