Простейший графический редактор
В предыдущем разделе мы создали при помощи средств MFC AppWizard работающий шаблон приложения. Теперь мы усовершенствуем его, чтобы пользователь смог рисовать в окне приложения и сохранять свои рисунки в файле на диске.
За основу нашего нового приложения мы возьмем проект Single и внесем в него все необходимые исправления и добавления. Доработаем приложение Single так, что когда пользователь нажимает левую клавишу мыши в окне отображается окружность, а когда пользователь нажимает правую кнопку - то отображается квадрат.
В момент нажатия на клавиши мыши создаются соответствующие сообщения, которые передаются классу окна просмотра. Нажатие левой клавиши мыши вызывает сообщение WM_LBUTTONDOWN, а нажатие правой - сообщение WM_RBUTTONDOWN.
Чтобы класс окна просмотра CSingleView мог отреагировать на это сообщение, вы должны создать метод для его обработки. Лучше всего для этого воспользоваться средствами ClassWizard.
Откройте страницу Message Maps на панели ClassWizard. Выберите из списков Class name и Object IDs класс CSingleView. В списке Messages появится названия виртуальных методов, которые вы можете переопределить и сообщений, для которых можно создать методы обработки.
Выберите из списка Messages сообщение WM_LBUTTONDOWN и нажмите кнопку Add Function. ClassWizard добавляет новую строку в таблицу сообщений класса CSingleView, вставляет в класс описание нового метода обработчика сообщения и создает шаблон этого метода.
Нажмите кнопку Edit Code. В окне редактирования появится шаблон метода, предназначенного для обработки сообщения WM_LBUTTONDOWN.
void CSingleView::OnLButtonDown(UINT nFlags, CPoint point)
{
// TODO: Здесь вы можете разместить код метода
CView::OnLButtonDown(nFlags, point);
}
Название этого метода ClassWizard выбирает автоматически на основе сообщения WM_LBUTTONDOWN. Для этого префикс WM_ в названии сообщения заменяется префиксом On и происходит замена некоторых прописных букв строчными.
Шаблон метода OnLButtonDown содержит вызов метода OnLButtonDown базового класса CView. Вы должны добавить свой код перед вызовом этого метода, сразу после коментария // TODO:.
void CSingleView::OnLButtonDown(UINT nFlags, CPoint point)
{
// TODO: Здесь вы можете разместить код метода
CClientDC dc(this);
dc.Ellipse(point.x-10, point.y-10, point.x+10,point.y+10);
CView::OnLButtonDown(nFlags, point);
}
Чтобы нарисовать в окне просмотра окружность, сначала необходимо получить контекст отображения. Для этого создается объект dc класса CClientDC. Конструктору передается указатель this, который указывает на объект класса CSingleView.
Затем вызывается метод Ellipse, который и отображает на экране небольшую окружность с центром, совпадающим с координатами указателя мыши.
Повторите только что проделанную процедуру для сообщения WM_RBUTTONDOWN. Создайте метод обработчик этого сообщения и добавьте в него команды отображения квадрата.
void CSingleView::OnRButtonDown(UINT nFlags, CPoint point)
{
// TODO: Здесь вы можете разместить код метода
CClientDC dc(this);
dc.Rectangle(point.x-10, point.y-10,
point.x+10,point.y+10);
CView::OnRButtonDown(nFlags, point);
}
Постройте проект и запустите полученное приложение. Нажимайте правую и левую кнопку мыши. Вы увидите, что на экране появляются окружности и квадраты (рис. 5.13). Поэкспериментируйте с приложением. Вы заметите, что изображение на экране пропадает, если оно перекрывается другими окнами, а также в некоторых других случаях.
Рис. 5.13. Отображение фигур в окне приложения Single
Это происходит потому, что наше приложение не обрабатывает одно из самых важных сообщений операционной системы Windows - сообщение WM_PAINT. Когда в окно приложения поступает сообщение WM_PAINT, приложение должно обновить информацию, отображаемую в данном окне.
Мы должны сохранить координаты и размеры нарисованных окружностей и квадратов, чтобы приложение могло воспроизвести их на экране, когда придет сообщение WM_PAINT.
Так как эти координаты в конечном счете представляют документ с которым работает приложение, то хранить их надо в классе документа приложения - CSingleDoc. Пользователь может нарисовать в документе любое количество фигур. Поэтому для хранения их координат лучшее всего подходит список или массив. Так как мы изучаем объектно-ориентированное программирование, мы воспользуемся для этого специальными классами. В состав библиотеки входят шаблоны для создания таких классов и несколько готовых классов. В нашей программе мы используем шаблон класса CArray.
Создадим новый класс CFigure, который будет представлять геометрические фигуры - окружности и квадраты. Координаты этих фигур мы будем определять по координатам их центра. Для этого в состав класса включим элемент xyFigCenter класса CPoint. Класс CPoint определяет координаты точки и содержит два элемента x и y, соответствующие координатам точки по оси ординат и абсцисс. Краткое описание класса CPoint представлено в разделе “Класс CPoint - точка на плоскости” главы “Некоторые классы MFC”.
Второй элемент cType типа char определяет форму геометрической фигуры. Если cType содержит значение 'E' значит данный объект представляет окружность, а если 'R' - квадрат.
Вы можете создать для класса CFigure отдельный файл, но сейчас мы просто добавим его в самое начало файла SingleDoc.h. Вот определение класса CFigure.
//////////////////////////////////////////////////////////////
// Класс определяет геометрическую фигуру
class CFigure
{
public:
// Координаты центра фигуры
CPoint xyFigCenter;
// Тип фигуры: 'E' - оокружность, 'R' - кволрат
char cType;
};
Один объект класса CFigure представляет одну геометрическую фигуру. Так как документ нашего приложения может содержать несколько фигур, мы воспользуемся шаблоном CArray, чтобы определить массив объектов класса CFigure. Вы можете получить дополнительную информацию о шаблоне CArray в разделе “Коллекции” главы “Некоторые классы MFC”.
Определение этого массива, который получил название arrayFig, помещаем в класс документа CSingleDoc, в атрибутах класса.
//////////////////////////////////////////////////////////////
// Класс CSingleDoc
class CSingleDoc : public CDocument
{
protected:
CSingleDoc();
DECLARE_DYNCREATE(CSingleDoc)
// Attributes
public:
CArray<CFigure, CFigure&> arrayFig;
Если вы используете шаблоны классов CArray, CMap или CList, вы должны включить в исходный текст приложения файл afxtempl.h. В данном файле содержатся определения этих шаблонов.
Так как мы работаем с объектами класса CArray в различных файлах, удобнее всего включить его в самом конце файла stdafx.h.
// Включаемый файл stdafx.h
// ...
// Включаемый файл для шаблона CArray
#include <afxtempl.h>
Теперь у нас есть структура для хранения геометрических фигур, нарисованных в окне. Мы должны ее заполнить. Так как за взаимодействие с пользователем отвечает класс окна просмотра, мы изменяем определенные нами ранее методы OnLButtonDown и OnRButtonDown таким образом, чтобы одновременно с выводом на экран они сохраняли параметры новой фигуры в массиве arrayFig.
//////////////////////////////////////////////////////////////
// Метод OnLButtonDown класса CSingleView
// Обрабатывает сообщения левой кнопки мыши
void CSingleView::OnLButtonDown(UINT nFlags, CPoint point)
{
// Получаем указатель на документ (объект класса CSingleDoc)
CSingleDoc* pDoc = GetDocument();
// Проверяем указатель pDoc
ASSERT_VALID(pDoc);
// Отображаем на экране окружность
CClientDC dc(this);
dc.Ellipse(point.x-10, point.y-10,
point.x+10,point.y+10);
// Сохраняем характеристики окружности
CFigure OneFigure;
OneFigure.xyFigCenter = point;
OneFigure.cType = 'E';
// Добавляем к массиву, определяющему документ, новый
// элемент
pDoc->arrayFig.Add(OneFigure);
// Вызываем метод OnLButtonDown базового класса CView
CView::OnLButtonDown(nFlags, point);
}
//////////////////////////////////////////////////////////////
// Метод OnRButtonDown класса CSingleView
// Обрабатывает сообщения правой кнопки мыши
void CSingleView::OnRButtonDown(UINT nFlags, CPoint point)
{
// Получаем указатель на документ (объект класса CSingleDoc)
CSingleDoc* pDoc = GetDocument();
// Проверяем указатель pDoc
ASSERT_VALID(pDoc);
// Отображаем на экране квадрат
CClientDC dc(this);
dc.Rectangle(point.x-10, point.y-10,
point.x+10,point.y+10);
// Сохраняем характеристики квадрата
CFigure OneFigure;
OneFigure.xyFigCenter = point;
OneFigure.cType = 'R';
// Добавляем к массиву, определяющему документ, новый
// элемент
pDoc->arrayFig.Add(OneFigure);
// Вызываем метод OnRButtonDown базового класса CView
CView::OnRButtonDown(nFlags, point);
}
Теперь координаты и форма всех нарисованных фигур запоминаются в классе документа. Следующим шагом надо определить, как отображать эти фигуры на экране. Для этого следует внести изменения в метод OnDraw класса окна просмотра CSingleView.
//////////////////////////////////////////////////////////////
// Метод OnDraw класса окна просмотра
void CSingleView::OnDraw(CDC* pDC)
{
CSingleDoc* pDoc = GetDocument();
ASSERT_VALID(pDoc);
// TODO:
int i;
for (i=0; i<pDoc->arrayFig.GetSize(); i++)
{
if(pDoc->arrayFig[i].cType == 'E')
pDC->Ellipse(pDoc->arrayFig[i].xyFigCenter.x-10,
pDoc->arrayFig[i].xyFigCenter.y-10,
pDoc->arrayFig[i].xyFigCenter.x+10,
pDoc->arrayFig[i].xyFigCenter.y+10);
else if (pDoc->arrayFig[i].cType == 'R')
pDC->Rectangle(pDoc->arrayFig[i].xyFigCenter.x-10,
pDoc->arrayFig[i].xyFigCenter.y-10,
pDoc->arrayFig[i].xyFigCenter.x+10,
pDoc->arrayFig[i].xyFigCenter.y+10);
}
}
Постройте проект и запустите полученное приложение. Вы можете свободно изменять размеры окна приложения, перекрывать его окно окнами других приложений, минимизировать и восстанавливать размеры окна. Изображение документа, которое вы нарисуете, не пропадет.
Вы даже можете распечатать нарисованный документ на принтере. А ведь вы не написали для этого не единой строки кода. Перед печатью документа его можно проверить в режиме предварительного просмотра (рис. 5.14). Для этого выберите из меню File строку Print Preview
Рис. 5.14. Режим предварительного просмотра документа перед печатью