Стоит Windows 8 Pro, Visual Studio 2010 Express Edition . |
Однодокументный интерфейс MFC
Сохранение и загрузка рисунков
Когда в классе документа CDrawSDIDoc срабатывает функция OnNewDocument(), то данные в массиве m_Lines необходимо уничтожить и освободить занимаемую ими память. Это ни в коем случае нельзя делать в функции OnNewDocument(), (хотя принципиально это и можно сделать), потому, что по логике своего предназначения эта функция должна инициализировать данные нового документа.
Удаление текущего рисунка
В данной ситуации для удаления объектов линий воспользуемся обработчиком сообщения операционной системы о событии DeleteContents(). Переопределим этот обработчик, выполнив следующие действия:
- В панели Class View выберите класс CDrawSDIDoc
- Во вкладке Properties включите режим Overrides (Переопределение)
-
Найдите виртуальную функцию DeleteContents класса CDocument и создайте переопределение этой функции
-
Заполните переопределенную функцию DeleteContents() так
Код переопределения виртуальной функции DeleteContents() в файле DrawSDIDoc.cpp void CDrawSDIDoc::DeleteContents() { // Получить количество линий в массиве объектов int count = (int)m_Lines.GetCount(); // Попытка выполнить удаление объектов try { if(count){ // есть что удалять // Освободить память на куче по каждому объекту for(int i = 0; i < count; i++) delete (MyLine*)m_Lines.GetAt(i); // Установить массив в исходное состояние m_Lines.RemoveAll(); } } // Ловушка исключения о нарушении памяти catch(CMemoryException* pErr) { // Отобразить сообщение о причине исключения pErr->ReportError(); // Удалить объект, вызвавший исключение pErr->Delete(); } CDocument::DeleteContents(); }
Сохранение документа в файл и загрузка из файла
Добавим возможность сохранения документа. Воспользуемся функцией сериализации Serialize() класса CDrawSDIDoc, которую перепишем так, чтобы она только передавала сообщение о преобразовании в последовательную форму объекту-массиву m_Lines. В самом же объекте-массиве m_Lines предусмотрим свою функцию сериализации, в которой и осуществим сохранение документа с использованием функциональных возможностей потока ввода-вывода C++.
- В классе CDrawSDIDoc найдите функцию Serialize() преобразования в последовательную форму
-
Удалите все содержимое функции и перепишите ее так
Функция сериализации класса CDrawSDIDoc void CDrawSDIDoc::Serialize(CArchive& ar) { // Передать объекту-массиву сообщение // о преобразовании в последовательную форму m_Lines.Serialize(ar); }
-
Добавьте новую функцию-член к классу MyLine, выделив класс MyLine в панели Class View и заполнив мастер
Не забудьте нажать кнопку Add перед нажатием кнопки Finish
-
Отредактируйте функцию так
Функция сериализации класса MyLine // Преобразование в последовательную форму объекта-массива void MyLine::Serialize(CArchive & ar) { CObject::Serialize(ar); if(ar.IsStoring()) ar << m_pointBegin << m_pointEnd; // Сохранить else ar >> m_pointBegin >> m_pointEnd; // Восстановить }
-
Постройте приложение и попробуйте сохранить документ на диск. Система выдаст сообщение, что невозможно сохранить документ
Исправим это...
Зарегистрируем класс MyLine, как способный самостоятельно выполнять сериализацию. Для этого:
-
В начало объявления класса MyLine в файле MyLine.h и перед конструктором класса в файле MyLine.cpp добавьте по строке регистрации и конструктор по умолчанию
Конструктор по умолчанию теперь нужно определить самим, поскольку определен общий конструктор, который делает невозможным вызов автоматического конструктора по умолчанию.
Добавление в объявление класса MyLine в файле MyLine.h class MyLine : public CObject { public: DECLARE_SERIAL(MyLine); MyLine(){}; // Констуктор по умолчанию MyLine(CPoint, CPoint); ~MyLine(void); private: // Начальная точка линии CPoint m_pointBegin; // Конечная точка линии CPoint m_pointEnd; public: // Функция-член класса MyLine для рисования линий void Draw(CDC* pDC); private: // Массив линий CObArray m_Lines; public: // Преобразование в последовательную форму объекта-массива void Serialize(CArchive & ar); };
Добавление вначало файла MyLine.cpp #include "StdAfx.h" #include ".\myline.h" IMPLEMENT_SERIAL(MyLine, CObject, 1) MyLine::MyLine(CPoint pointFrom, CPoint pointTo) { m_pointBegin = pointFrom; m_pointEnd = pointTo; } MyLine::~MyLine(void) { } ....................................................
-
Постройте приложение и убедитесь, что документы сохраняются на диске и загружаются с диска. Вот один из восстановленных шедевров
Реконструкция приложения для добавления цвета и толщины линий
Добавим в приложение возможность изменять цвет линий и их толщину. Для этого изменим меню, сгенерированное мастером, а также внесем необходимые изменения в уже имеющийся код.
Изменения в классе MyLine
-
Добавьте переменные в класс MyLine и функцию доступа
Добавления в файл MyLine.h class MyLine : public CObject { public: DECLARE_SERIAL(MyLine); MyLine(){} MyLine(CPoint, CPoint); ~MyLine(void); private: // Начальная точка линии CPoint m_pointBegin; // Конечная точка линии CPoint m_pointEnd; public: // Функция-член класса MyLine для рисования линий void Draw(CDC* pDC); private: // Массив линий CObArray m_Lines; public: // Преобразование в последовательную форму объекта-массива void Serialize(CArchive & ar); private: // Для хранения цвета COLORREF color; // Для хранения толщины линий int widthLine; public: // Функция доступа void Set(COLORREF c, int w) { color = c; widthLine = w; } };
-
Инициализируйте переменные в общем конструкторе
Добавления в файл MyLine.cpp MyLine::MyLine(CPoint pointFrom, CPoint pointTo) { m_pointBegin = pointFrom; m_pointEnd = pointTo; color = RGB(0, 0, 0); widthLine = 1; }
-
Дополните функцию рисования Draw класса MyLine
Добавления в файл MyLine.cpp // Функция-член класса MyLine для рисования линий void MyLine::Draw(CDC* pDC) { // Создать перо CPen currPen(PS_SOLID, widthLine, color);// Конструктор пера // Установить новое перо в качестве рисующего объекта CPen* pOldPen = pDC->SelectObject(&currPen); // Нарисовать линию pDC->MoveTo(m_pointBegin); pDC->LineTo(m_pointEnd); // Восстановить прежнее перо pDC->SelectObject(pOldPen); }
-
Измените функцию MyLine::Serialize()
Добавления в файл MyLine.cpp // Преобразование в последовательную форму объекта-массива void MyLine::Serialize(CArchive & ar) { CObject::Serialize(ar); if(ar.IsStoring()) // Сохранить ar << m_pointBegin << m_pointEnd << (DWORD)color << widthLine; else // Восстановить ar >> m_pointBegin >> m_pointEnd >> (DWORD)color >> widthLine; }
- Постройте приложение и проверьте - пока ничего измениться не должно
Изменения в классе CDrawSDIDoc
-
Добавьте в класс документа CDrawSDIDoc массив таблицы цветов, индекс массива таблицы цветов и толщину линии. Код приведен ниже
Добавления в файл DrawSDIDoc.h class CDrawSDIDoc : public CDocument { ................................................ private: // Массив линий CObArray m_Lines; public: // Добавить линию MyLine* AddLine(CPoint pointBegin, CPoint pointEnd); // Количество нарисованных линий int GetLineCount(void); // Возвращает указатель на объект-линию по индексу MyLine* GetLine(int index); virtual void DeleteContents(); private: int m_iColor; // индекс массива таблицы цветов int m_iWidth; // толщина линии public: // Объявление таблицы цветов static const COLORREF m_Colors[8]; };
-
Добавьте инициализацию переменных m_iColor и m_iWidth в функции OnNewDocument()
Добавление в функцию OnNewDocument() файла DrawSDIDoc.cpp BOOL CDrawSDIDoc::OnNewDocument() { if (!CDocument::OnNewDocument()) return FALSE; // TODO: add reinitialization code here // (SDI documents will reuse this document) // Инициализировать начальный цвет черным // Толщину линии 1 m_iColor = 0; m_iWidth = 1; return TRUE; }
-
Добавьте в файл DrawSDIDoc.cpp таблицу цветов сразу после таблицы сообщений
Добавления таблицы цветов в файл DrawSDIDoc.cpp BEGIN_MESSAGE_MAP(CDrawSDIDoc, CDocument) END_MESSAGE_MAP() const COLORREF CDrawSDIDoc::m_Colors[] = { RGB(0, 0, 0), // Black - черный RGB(0, 0, 255), // Blue - синий RGB(0, 255, 0), // Green - зеленый RGB(0, 255, 255), // Cyan - голубой RGB(255, 0, 0), // Red - красный RGB(255, 0, 255), // Magenta - сиреневый RGB(255, 255, 0), // Yellow - желтый RGB(255, 255, 255) // White - белый };
-
Измените функцию AddLine() класса CDrawSDIDoc так, чтобы перед сохранением объекта MyLine в него добавлялась информация о цвете и толщине линии
Добавления в функцию AddLine() файла DrawSDIDoc.cpp // Добавить линию MyLine* CDrawSDIDoc::AddLine(CPoint pointBegin, CPoint pointEnd) { MyLine* pLine = NULL; // Создали указатель на стеке try // Попытка { // Создать экземпляр объекта-линии на куче pLine = new MyLine(pointBegin, pointEnd); // Конструктор // Добавить информацию о текущем цвете и толщине линии pLine->Set(m_Colors[m_iColor], m_iWidth); // Добавить указатель на новую линию в массив объектов m_Lines.Add(pLine); // Отметить документ как измененный SetModifiedFlag(); } catch(CMemoryException* pErr) // Ловушка на недостаток памяти { // Сообщили пользователю AfxMessageBox("Недостаточно памяти", MB_ICONSTOP | MB_OK); // Уничтожим объект-линию, на котором сгенерировалось исключение if(pLine){ delete pLine; pLine = NULL; } // Удалить объект с исключением pErr->Delete(); } return pLine; // Вернуть указатель на объект-линию }