Процедура создания и обслуживания немодального диалога проиллюстрирована в примере 28-4; вывод этой программы показан на рис. 28.9.
//Приложение 28-4. Немодальный диалог
//Файл 28-4.h
#define Dlg 1
#define CM_VIEW 201
#define CM_EXIT 24310
#define IDC_POINTS 101
#define IDC_CURVE 102
#define IDC_HISTO 103
#define FILESIZE
//Файл 28-4.rc #include "28-4.h" MainMenu MENU{ POPUP "Файл"{
MENUITEM "Вид...",CM_VIEW
MENUITEM SEPARATOR
MENUITEM "Выход",CM_EXIT
} }
Dlg DIALOG 100,15,116,66 STYLE WS_CHILD|WS_VISIBLE|WS_CAPTION
266 Глава 28
CLASS "BorDlg_Gray"
CAPTION "Вид графика"
FONT 8, "MS Sans. Serif"{
CONTROL "Точки",IDC_POINTS,"BUTTON",BS_AUTORADIOBUTTON|WS_GROUP, 8,9,60,12
CONTROL "Огибающая",IDC_CURVE,"BUTTON",BS_AUTORADIOBUTTON,8,27,60,12
CONTROL "Гистограмма",IDC_HISTO,"BUTTON",BS_AUTORADIOBUTTON,8,45,60,12
CONTROL "", IDOK,"BorBtn",BS_PUSHBUTTON|WS_CHILD|WS_VISIBLE|WS_GROUP,72,29,37,25
}
//Файл 28-4.cpp
#include <owl\framewin.h>
#include <owl\dialog.h>
#include "28-4.h"
#include <stdio.h>
class MyWindow; //Объявим имя класса для последующих ссылок на него
MyWindow* myWin;//Объявим имя указателя на объект главного окна
/*Класс приложения, производный от Tapplication*/
class MyApp:public TApplication{
public:
void InitMainWindow();//Замещаем функцию InitMainWindow
|
|
};
/*Класс главного окна, производный от TframeWindow*/ class MyWindow:public TFrameWindow{ public:
int view;/ /Переключатель вида графика
int data[FILESIZE]; //Массив для данных из файла
MyWindow(TWindow*,char far*);
void SetupWindow(); //Замещаем функцию SetupWindow
void Paint(TDC&,bool,TRect&); //Замещаем функцию Paint
void GetWindowClass(WNDCLASS&);//Замещаем функцию GetWindowClass
void CmView(); //Функция отклика на нажатие кнопки "Вид"
DECLARE_RESPONSE_TABLE(MyWindow); //Объявляем таблицу откликов главного окна
};
/*Класс окна диалога, производный от Tdialog*/ class MyDialog:public TDialog{ public:
MyDialog(TWindow* parent,TResId resId):TDialog(parent,resId){}/ /Конструктор
EvInitDialog(HWND) ;//Замещаем функцию EvInitDialog
void CmPoints(); //Функции отклика
void CmCurve(); //на нажатие
void CmHisto(); //альтернативных кнопок
DECLARE_RESPONSE_TABLE(MyDialog); //Объявляем таблицу откликов диалога
};
/*Таблица откликов класса MyWindow*/ DEFINE_RESPONSE_TABLE1(MyWindow,TFrameWindow)
EV_COMMAND(CM_VIEW,CmView), END_RESPONSE_TABLE; /* Конструктор главного окна*/ MyWindow::MyWindow(TWindow*parent,char far*title):TFrameWindow(parent,title){
AssignMenu("MainMenu"); //Назначаем главному окну меню
}
/*3амещающая функция SetupWindow*/ void MyWindow::SetupWindow(){
TWindow::SetupWindow(); //Вызываем замещенную функцию SetupWindow
FILE* fp=fopen("28-4.dat","r"); //Открываем файл для чтения
for(int i=0;i<FILESIZE;i++)
fscanf(fp,"%d",&data[i]); //Читаем символьные данные в массив data,преобразуя в числа
fclose(fp);/ /Закрываем файл
}
/*3амещающая функция GetWindowClass*/ void MyWindow::GetWindowClass(WNDCLASS& wc){
TWindow::GetWindowClass(wc)///Вызываем замещенную функцию
wc.style=CS_VREDRAW;/ /Необходимо, т.к. график рисуется снизу
} void MyWindow::CmView(){
new MyDialog(this,Dig)->Create() ;//Открываем немодальный диалог!
} void MyWindow::Paint(TDC&dc,bool,TRect&){
TPen myPen(TColor::LtBlue,1); //Устанавливаем синее перо
dc.SelectObject(myPen);// для фигур графика
TBrush myBrush(TColor::LtBlue) ;//Устанавливаем синюю
|
|
Диалоговые окна 267
dc.SelectObject(myBrush);//кисть для внутренних областей фигур графика TRect wndRect=GetClientRect(); //Получим рабочую область главного окна int i;// Вспомогательная локальная переменная switch(view) {//Переключение формы отображения графика case(IDC_POINTS): //Вывод точек for(i=0;i<FILESIZE;i++)
dc.Ellipse(1*10-1-10-2,wndRect.bottom-data[i]-2,1*10+10+2, // Рисуем кружки
wndRect.bottom-data[i]+2); break; case(IDC_CURVE): //Вывод кривой линии
dc.MoveTo(10,wndRect.bottom-data[0]) ;//Устанавливаем начальную позицию for(i=1;i<FILESIZE;i++)
dc.LineTo(i*10+10,wndRect.bottom-data[i]); //Проводим линии между точками break; case(IDC_HISTO):
for(i=0;i<FILESIZE;i++)
dc.Rectangle(1*10+10,wndRect.bottom-data[i],
1*10+10+9,wndRect.bottom);// Рисуем прямоугольники break; } }
/*Реализация класса MyDialog*/ DEFINE_RESPONSE_TABLE1(MyDialog,TDialog) EV_COMMAND(IDC_POINTS,CmPoints), EV_COMMAND(IDC_CURVE,CmCurve), EV_COMMAND(IDC_HISTO,CmHisto), END_RESPONSE_TABLE; MyDialog::EvInitDialog(HWND hwnd){
TDialog::EvInitDialog(hwnd); //Вызываем замещенную функцию EvInitDialog SendDlgItemMessage(myWin->view,BM_SETCHECK,true,0L);/ /Нажмем кнопку } void MyDialog::CmPoints(){
myWin->view=IDC_POINTS; //Устанавливаем значение переключателя вида графика m yWin->Invalidate(); //Инициируем перерисовку главного окна } void MyDialog::CmCurve(){
myWin->view=IDC_CURVE; //Устанавливаем значение переключателя вида графика myWin->Invalidate(); //Инициируем перерисовку главного окна } void MyDialog::CmHisto(){
myWin->view=IDC_HISTO; //Устанавливаем значение переключателя вида графика myWin->Invalidate(); //Инициируем перерисовку главного окна }
/*Замещающая функция InitMainWindow класса приложения*/ void МуАрр::InitMainWindow(void){ EnableBWCC();
myWin=new MyWindow(0,"Программа 28-4"); //Сохранили указатель на главное окно myWin->view=IDC_POINTS; //Начальное значение переключателя вида графика SetMainWindow(myWin); //Установили главное окно }
/*Главная функция приложения OwlMain*/ int OwlMain(int,char*[]){
return MyApp().Run(); }
В файле 28-4.h определяются значения идентификаторов органов управления диалога и задается число точек на графике (константа FILESIZE). Из файла 28-4.rc видно, что в меню имеется единственный содержательный пункт "Вид...", который используется для активизации немодального диалога (для уничтожения диалогового окна служит его кнопка "ОК"). Три альтернативные кнопки, имеющие стиль BS_AUTORADIOBUTTON, объединены в группу флажками WS_GROUP. Первый флажок определяет первую альтернативную кнопку, второй - первый управляющий элемент за пределами альтернативной группы. Впрочем, при наличии единственной альтернативной группы флажки WS_GROUP можно опустить.
В файле 28-4.срр объявлены две глобальные переменные - класс MyWindow и указатель на объект этого класса myWin. Такое объявление (в котором, обратите внимание, фигурирует только имя класса, но нет никакой информации о его содержимом или подчиненности) позволяет в любом месте программы, в частности, в функции любого класса, сослаться на этот объявленный ранее класс или, через его указатель, на любые открытые функции-члены и данные-члены этого класса. В нашем примере такая необходимость возникнет в функциях отклика класса диалога, где устанавливается текущее значение переменной view и вызывается функция Invalidate() класса главного окна.
268 Глава 28
В классе главного окна MyWindow объявляется переменная view, которая будет содержать значение режима отображения графика (IDC_POINT, IDC_CURVE или IDC_HISTO). Было бы изящнее объявить ее не int, a enum; это не сделано для сокращения текста программы. Массив data служит для хранения отображаемых на графике данных. Для создания большей иллюзии полезности программы данные не определены в тексте программы, а читаются из файла с заданным заранее именем 28-4.dat; еще лучше было бы включить в программу стандартный диалог Windows для выбора файла и, тем самым, сделать ее гораздо более универсальной.
Далее в классе MyWindow объявляются замещаемые и новые функции-члены и таблица отклика, содержащая единственную строку-макрос для отклика на пункт главного меню "Вид.
Класс немодального диалога MyDialog содержит объявление замещаемой функции EvInitDialog(), которая удобна для выполнения необходимых инициализирующих действий, а также трех функций отклика на нажатие альтернативных кнопок диалога. Соответственно в таблицу откликов класса диалога входят три макроса EV_COMMAND.
|
|
Подготовительные для всей программы действия - чтение данных из файла - выполняются в замещающей функции SetupWindow(). Данные в файле хранятся в символьной форме, т.е. предполагается, что они были введены с клавиатуры. Для разделения таких данных в файле можно использовать различные символы - двоичный нуль, возврат каретки и др. В файле 28-4 dat данные разделяются символом пробела и для графика, изображенного на рис. 28.9, имеют следующий вид:
130 136 141 147 151 153 154 154 152 149 144 139 133 127 121 115 111 107 105 105
Для чтения данных из файла использована функция fscanf(), преобразующая, в процессе чтения, каждое данное в двоичную форму в соответствии с указанным в аргументах функции шаблоном "%d".
Существенным моментом является назначение классу окна стиля CS_VREDRAW. Это назначение выполняется в замещающей функции GetWindowClass(), использование было подробно описано в гл. 26. Стиль CS_VREDRAW определяет полную перерисовку окна (а не только его области вырезки) при любых манипуляциях с размером окна или перемещением по нему окна диалога. Как было показано в гл. 12, полная перерисовка окна, являющаяся неэффективной с точки зрения расходования процессорного времени, необходима в тех случаях, когда изображение в окне строится относительно его нижней (или правой) границы, что типично для вывода графиков.
Открытие немодального диалога осуществляется в функции отклика на нажатие кнопки "Вид..." CmView(). С помощью оператора new создается безымянный объект класса MyDialog и в том же предложении для него вызывается функция класса TDialog Create(), которая служит для вывода на экран немодального диалогового окна. Вспомним, что для активизации модального диалога мы использовали функцию Execute(), а для его завершения - функцию DestroyWindow(). Немодальный же диалог завершать нет особой необходимости, так как он, будучи выведен на экран, не препятствует использованию любых других органов управления главного окна.
|
|
Назначение диалога в данном примере заключается в установлении режима вывода графика, для чего предусмотрены три функции отклика: CmPoints(), CmCurves() и CmHisto(). В каждой из этих функций переключателю режима отображения view присваивается соответствующее значение, после чего вызовом функции Invalidate() для объекта главного окна инициируется полная его перерисовка.
Вывод графика осуществляется, как и положено, в функции Paint() главного окна. В ней прежде всего создаются и загружаются в контекст устройства синее перо и синяя кисть, а затем в зависимости от значения переменной view на экран выводятся либо маленькие (радиусом 2 пиксела) кружки, либо линии, соединяющие точки графика, либо прямоугольники, образующие гистограмму. Число 10, фигурирующее в координатах вывода, характеризует смещение графика на 10 пикселов относительно левой границы окна. Ради простоты в программе не выполняется никаких действий по масштабированию графика и не анализируется число введенных данных.
Как уже отмечалось, графики рисуются относительно нижней границы окна, реальное значение которой зависит от текущего размера окна на экране. Необходимым элементом такой техники является вызов функции GetClientRect(), которая возвращает структуру wndRect типа TRect с текущими размерами рабочей (клиентной) области окна. Значение wndRect.Bottom и определяет нижнюю границу.
Глава 29