Современные операционные системы с графическим интерфейсом предоставляют приложениям богатый выбор элементов управления, с помощью которых организуется взаимодействие программы с пользователем:
1. меню;
2. диалоговые окна;
3. кнопки;
4. списки;
5. полосы прокрутки;
6. индикаторы;
7. переключатели разных типов;
8. и т.п.
Использование стандартных элементов позволяет любой программе выглядеть как все остальные программы под эту ОС.
На первый взгляд создание программы с такими элементами управления может показаться сложной задачей. Однако это не так, напротив, организация интерфейса даже почти не связана с программированием, а ближе к дизайну программного обеспечения. В создании интерфейса с помощью стандартных элементов управления можно выделить два основных момента:
1. Разработка внешнего вида программы, заключающая в размещении элементов управления в окне программы, диалоговых окнах.
2. Написание обработчиков команд элементов управления.
Первый шаг осуществляется с помощью специальных редакторов интерфейса, входящих в среду разработки программ. Разработчик создает меню, диалоговые окна, мышкой размещает элементы управления. Затем интерфейс несколькими командами (функциям Си) подключается к программе. После этого все элементы присутствуют в программе, однако при нажатии на них не происходит ничего.
Вторым шагом является написание обработчиков событий, которые генерируются при нажатии на соответствующие элементы управления, чтобы они “заработали”.
Такой подход называется визуальным программированием, от чего, например, и происходит название Visual Studio, Visual C++, Visual Basic и т.д.
На простом примере рассмотрим подключение меню к программе.
|
// Стандартный включаемый файл Windows
#include <windows.h>
#include «resource.h»
// Прототип функции обратного вызова для обработки сообщений
LRESULT CALLBACK WndProc(HWND, UINT, WPARAM, LPARAM);
int g_iShape = 0;
// Функция вызывается автоматически, когда программа запускается
int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, PSTR szCmdLine, int iCmdShow)
{
HWND hWnd;
MSG msg;
WNDCLASSEX wndclass;
// Настройка класса окна
wndclass.cbSize = sizeof(WNDCLASSEX);
wndclass.style = CS_HREDRAW | CS_VREDRAW;
wndclass.lpfnWndProc = WndProc;
wndclass.cbClsExtra = 0;
wndclass.cbWndExtra = 0;
wndclass.hInstance = hInstance;
wndclass.hIcon = LoadIcon(NULL, IDI_APPLICATION);
wndclass.hCursor = LoadCursor(NULL, IDC_ARROW);
wndclass.hbrBackground = (HBRUSH)GetStockObject(WHITE_BRUSH);
wndclass.lpszMenuName = MAKEINTRESOURCE(IDR_MENU1);
wndclass.lpszClassName = “Window Class”; // Имя класса
wndclass.hIconSm = LoadIcon(NULL, IDI_APPLICATION);
// Регистрация класса окна
if (RegisterClassEx(&wndclass) == 0)
{
// Сбой программы, выход
return 0;
}
// Создание окна
hWnd = CreateWindowEx(
WS_EX_OVERLAPPEDWINDOW,
«Window Class», // Имя класса
«Приложение Windows», // Текст заголовка
WS_OVERLAPPEDWINDOW,
CW_USEDEFAULT,
CW_USEDEFAULT,
320,
320,
NULL,
NULL,
hInstance,
NULL);
// Отображение окна
ShowWindow(hWnd, iCmdShow);
// Обработка сообщений, пока программа не будет прервана
while(GetMessage(&msg, NULL, 0, 0))
{
TranslateMessage(&msg);
DispatchMessage(&msg);
}
return (int)msg.wParam;
}
// Функция обратного вызова для обработки сообщений
LRESULT CALLBACK WndProc(HWND hWnd, UINT iMsg, WPARAM wParam, LPARAM lParam)
{
int wmId;
HDC hDC;
PAINTSTRUCT ps;
switch(iMsg)
{
// Вызыватся, когда пользователь выполняет некую команду, например из главного меню
case WM_COMMAND:
wmId = LOWORD(wParam);
Switch (wmId)
{
case ID_FILE_EXIT:
SendMessage(hWnd, WM_CLOSE, 0, 0);
break;
case ID_SHAPE_SQUARE:
g_iShape = 0;
InvalidateRect(hWnd, NULL, TRUE);
break;
case ID_SHAPE_CIRCLE:
g_iShape = 1;
InvalidateRect(hWnd, NULL, TRUE);
|
break;
}
break;
// Вызывается, когда пользователь отпускает левую кнопку мыши
case WM_LBUTTONUP:
MessageBox(hWnd, TEXT(«Вы кликнули!»), TEXT(«событие»), MB_OK);
break;
// Вызывается, когда окно обновляется
case WM_PAINT:
hDC = BeginPaint(hWnd, &ps);
If (g_iShape)
Ellipse(hDC, 50, 50, 200, 200);
Else
Rectangle(hDC, 50, 50, 200, 200);
EndPaint(hWnd, &ps);
break;
// Вызывается, когда пользователь закрывает окно
case WM_DESTROY:
PostQuitMessage(0);
break;
default:
return DefWindowProc(hWnd, iMsg, wParam, lParam);
}
return 0;
}
Как видно отличий от базового примера очень немного:
1. добавлено подключение заголовочного файла ресурсов resource.h;
2. добавлена глобальная переменная, задающая выводимую фигуру;
3. заполнено поле lpszMenuName класса окна, что подключает меню;
4. добавлен обработчик сообщения WM_COMMAND с внутренним переключателем switch для трех разных пунктов меню;
5. немного модифицирован обработчик WM_PAINT.
Помимо файла на языке программирования C++, в exe-файл должен быть слинкован специальный файл ресурсов с расширением rc (resource script):
// Microsoft Visual C++ generated resource script.
//
#include “resource.h”
#define APSTUDIO_READONLY_SYMBOLS
/////////////////////////////////////////////////////////////////////////////
//
// Generated from the TEXTINCLUDE 2 resource.
//
#include “afxres.h”
/////////////////////////////////////////////////////////////////////////////
#undef APSTUDIO_READONLY_SYMBOLS
/////////////////////////////////////////////////////////////////////////////
// Russian resources
#if!defined(AFX_RESOURCE_DLL) || defined(AFX_TARG_RUS)
#ifdef _WIN32
LANGUAGE LANG_RUSSIAN, SUBLANG_DEFAULT
#pragma code_page(1251)
#endif //_WIN32
/////////////////////////////////////////////////////////////////////////////
//
// Menu
//
IDR_MENU1 MENU
BEGIN
POPUP “&Файл”
BEGIN
MENUITEM “В&ыход”, ID_FILE_EXIT
END
POPUP “Ф&игура”
BEGIN
MENUITEM “&Квадрат”, ID_SHAPE_SQUARE
MENUITEM “&Окружность”, 104
END
END
#endif // Russian resources
|
/////////////////////////////////////////////////////////////////////////////
/////////////////////////////////////////////////////////////////////////////
// English (U.S.) resources
#if!defined(AFX_RESOURCE_DLL) || defined(AFX_TARG_ENU)
#ifdef _WIN32
LANGUAGE LANG_ENGLISH, SUBLANG_ENGLISH_US
#pragma code_page(1252)
#endif //_WIN32
#ifdef APSTUDIO_INVOKED
/////////////////////////////////////////////////////////////////////////////
//
// TEXTINCLUDE
//
1 TEXTINCLUDE
BEGIN
“resource.h\0”
END
2 TEXTINCLUDE
BEGIN
“#include “”afxres.h””\r\n”
“\0”
END
3 TEXTINCLUDE
BEGIN
“\r\n”
“\0”
END
#endif // APSTUDIO_INVOKED
#endif // English (U.S.) resources
/////////////////////////////////////////////////////////////////////////////
#ifndef APSTUDIO_INVOKED
/////////////////////////////////////////////////////////////////////////////
//
// Generated from the TEXTINCLUDE 3 resource.
//
/////////////////////////////////////////////////////////////////////////////
#endif // not APSTUDIO_INVOKED
Это обычный текстовый файл, зная синтаксис файлов, можно создать его в редакторе текстов. Однако, на практике этот файл автоматически генерируется визуальным редактором. Вторым сгенерированным файлом является resource.h для связи ресурсов с программой на Си:
//{{NO_DEPENDENCIES}}
// Microsoft Visual C++ generated include file.
// Used by menu.rc
//
#define IDR_MENU1 101
#define ID_FILE_EXIT 102
#define ID_SHAPE_SQUARE 103
#define ID_SHAPE_CIRCLE 104
// Next default values for new objects
//
#ifdef APSTUDIO_INVOKED
#ifndef APSTUDIO_READONLY_SYMBOLS
#define _APS_NEXT_RESOURCE_VALUE 105
#define _APS_NEXT_COMMAND_VALUE 40001
#define _APS_NEXT_CONTROL_VALUE 1001
#define _APS_NEXT_SYMED_VALUE 101
#endif
#endif
Результат работы программы показан на рисунке 2.3.1. Следует отметить, что программы, обладающие сложным интерфейсом пользователя, с большим количеством элементов управления, редко разрабатываются на Си с использованием исключительно функций, предоставляемых операционной системой. Чаще всего для этого используются или специальные библиотеки классов, например, MFC (Microsoft Foundation Classes, отсутствует в бесплатной, express версии студии), Qt или с помощью специализированных сред быстрой разработки, например, Borland C++ Builder, MATLAB Guide и т.д.
Рисунок 2.3.1 – Подключение главного меню к программе
Контрольные вопросы
- Что такое визуальное программирование?
- Что такое файл ресурсов?
- Зачем нужен символ & в названии пункта меню «В&ыход»?
- Как подключить главное меню в программу?
- Какие сообщения генерируются при выборе пользователем пункта меню, как определить какой конкретно пункт был выбран?