Стоит Windows 8 Pro, Visual Studio 2010 Express Edition . |
Однодокументный интерфейс MFC
Создание класса линии MyLine
Создадим свой класс, который будет обеспечивать поддержку хранения координат отрезка прямой линии и отображения ее в окне представления документа.
- В панели Class View найдите верхний узел с названием DrawSDI и для него выполните опцию Add Class контекстного меню
- В диалоговом окне Add Class выделите шаблон Generic C++ Class (Универсальный класс C++ ), как показано на рисунке, и щелкните по кнопке Open
- Появившееся окно мастера добавления класса заполните так
- Щелкните по кнопке Finish
- Мастер создаст заготовки для объявления и определения класса и разместит их в отдельных файлах MyLine.h и MyLine.cpp
- Отредактируйте эти заготовки так, чтобы класс мог хранить координаты начальной и конечной точек линии. Для этого воспользуйтесь мастером добавления переменных, предварительно выделяя в панели Class View класс MyLine. Переменные определите с модификатором доступа private
- Для того, чтобы класс мог нарисовать линию между двумя точками, добавьте в него с помощью мастера Add Function функцию-член Draw()
Не забудьте нажать кнопку Add перед нажатием кнопки Finish
В итоге должно получиться следующее
Файл MyLine.h #pragma once #include "afx.h" #include "atltypes.h" class MyLine : public CObject { public: MyLine(CPoint, CPoint); ~MyLine(void); private: // Начальная точка линии CPoint m_pointBegin; // Конечная точка линии CPoint m_pointEnd; public: // Функция-член класса MyLine для рисования линий void Draw(CDC* pDC); };
Файл MyLine.cpp #include "StdAfx.h" #include ".\myline.h" MyLine::MyLine(CPoint pointFrom, CPoint pointTo) { m_pointBegin = pointFrom; m_pointEnd = pointTo; } MyLine::~MyLine(void) { } // Функция-член класса MyLine для рисования линий void MyLine::Draw(CDC* pDC) { // Нарисовать линию pDC->MoveTo(m_pointBegin); pDC->LineTo(m_pointEnd); }
Конструирование класса документа
Экземпляры объекта MyLine можно сохранять в объекте документа в простом динамическом массиве, реализуемом в MFC -библиотеке классом CObArray. Объект этого класса массива изменяет свой размер динамически в зависимости от количества находящихся в нем элементов. Этот класс-массив может содержать в себе любой объект, являющийся наследником класса CObject, размер массива ограничивается только количеством свободной памяти в компьютере.
Создадим в классе CDrawSDIDoc приложения переменную-экземпляр класса CObArray для хранения линий-экземпляров созданного класса MyLine. Назовем ее m_Lines. Для этого:
-
В панели Class View выделите класс CDrawSDIDoc, через опцию Add Variable вызовите мастер и заполните его так
-
Таким же образом добавьте в класс CDrawSDIDoc функцию-член AddLine() для добавления объектов линий в массив, идентифицируемый только что созданной переменной. Мастер заполните так
Мастер внесет следующие добавления в класс CDrawSDIDoc
Файл DrawSDIDoc.h class CDrawSDIDoc : public CDocument { .............................................. private: // Массив линий CObArray m_Lines; public: // Добавить линию MyLine* AddLine(CPoint pointBegin, CPoint pointEnd); };
Файл DrawSDIDoc.cpp // Добавить линию MyLine* CDrawSDIDoc::AddLine(CPoint pointBegin, CPoint pointEnd) { return NULL; }
-
Отредактируйте заготовку функции AddLine() так
Файл DrawSDIDoc.cpp // Добавить линию MyLine* CDrawSDIDoc::AddLine(CPoint pointBegin, CPoint pointEnd) { MyLine* pLine = NULL; // Создали указатель на стеке try // Попытка { // Создать экземпляр объекта-линии на куче pLine = new MyLine(pointBegin, pointEnd); // Конструктор // Добавить указатель на новую линию в массив объектов m_Lines.Add(pLine); // Отметить документ как измененный SetModifiedFlag(); } catch(CMemoryException* pErr) // Ловушка на недостаток памяти { // Сообщили пользователю AfxMessageBox("Недостаточно памяти", MB_ICONSTOP | MB_OK); // Уничтожим объект-линию, на котором сгенерировалось исключение if(pLine){ delete pLine; pLine = NULL; } // Удалить объект с исключением pErr->Delete(); } return pLine; // Вернуть указатель на объект-линию }
-
Подключите объявление класса MyLine к классу CDrawSDIDoc вначале файла DrawSDIDoc.h так
Файл DrawSDIDoc.h // DrawSDIDoc.h : interface of the CDrawSDIDoc class // #pragma once #include "afxcoll.h" #include "MyLine.h" class CDrawSDIDoc : public CDocument { ........................................... };
Добавим в класс CDrawSDIDoc еще две публичные функции-члена, играющих роль функций доступа к закрытой переменной m_Lines:
- GetLineCount() - возвращает количество объектов-линий, хранящихся в массиве m_Lines класса CObArray
- GetLine() - возвращает указатель на линию, хранящийся в массиве m_Lines, по индексу массива
Обе функции доступа будут нужны в классе представления CDrawSDIView для отображения линий, хранящихся как закрытые в классе документа CDrawSDIDoc.
Заполните мастера добавления функций так
Не забудьте нажать кнопку Add перед нажатием кнопки Finish
Добавления, выполненные мастерами, должны быть такими
Добавления в файле DrawSDIDoc.h class CDrawSDIDoc : public CDocument { .......................................... private: // Массив линий CObArray m_Lines; public: // Добавить линию MyLine* AddLine(CPoint pointBegin, CPoint pointEnd); // Количество нарисованных линий int GetLineCount(void); // Возвращает указатель на объект-линию по индексу MyLine* GetLine(int index); };
Добавления в файле DrawSDIDoc.cpp // Количество нарисованных линий int CDrawSDIDoc::GetLineCount(void) { return 0; } // Возвращает указатель на объект-линию по индексу MyLine* CDrawSDIDoc::GetLine(int index) { return NULL; }
-
Заполните код функций так
Код функций доступа в файле DrawSDIDoc.cpp // Количество нарисованных линий int CDrawSDIDoc::GetLineCount(void) { // Возвратить количество элементов в массиве return (int)m_Lines.GetCount(); } // Возвращает указатель на объект-линию по индексу MyLine* CDrawSDIDoc::GetLine(int index) { // Возвратить указатель на объект-линию, хранящийся // в указанной позиции в массиве CObArray m_Lines return (MyLine*)m_Lines[index]; }
- Постройте приложение, чтобы своевременно выловить возможные ошибки
Конструирование класса представления
Таким образом, мы выполнили подготовительные мероприятия, позволяющие классу документа сохранять и выдавать информацию о координатах линий. Теперь необходимо реконструировать заготовку класса представления для манипулирования с данными класса документа и представления их в окне приложения. Для этого
-
Введите частную переменную m_pointPrevPos в класс CDrawSDIView для хранения предыдущей точки рисования
- В панели Class View выберите класс CDrawSDIView
-
Используя вкладку Properties в режиме Messages, добавьте к классу CDrawSDIView функции для обработки сообщений операционной системы
- WM_LBUTTONDOWN - для начала рисования и захвата мыши
- WM_LBUTTONUP - для завершения рисования и освобождения мыши
- WM_MOUSEMOVE - для выполнения рисования и запоминания координат линий в классе документа
Добавление переменной и обработчиков в класс CDrawSDIView class CDrawSDIView : public CView { ................................................... private: // Предыдущая точка рисования CPoint m_pointPrevPos; public: afx_msg void OnLButtonDown(UINT nFlags, CPoint point); afx_msg void OnLButtonUp(UINT nFlags, CPoint point); afx_msg void OnMouseMove(UINT nFlags, CPoint point); };
Регистрация обработчиков в карте сообщений DrawSDIView.cpp BEGIN_MESSAGE_MAP(CDrawSDIView, CView) // Standard printing commands ON_COMMAND(ID_FILE_PRINT, CView::OnFilePrint) ON_COMMAND(ID_FILE_PRINT_DIRECT, CView::OnFilePrint) ON_COMMAND(ID_FILE_PRINT_PREVIEW, CView::OnFilePrintPreview) ON_WM_LBUTTONDOWN() ON_WM_LBUTTONUP() ON_WM_MOUSEMOVE() END_MESSAGE_MAP()
-
Отредактируйте функции-обработчики, как показано ниже
Код функций-обработчиков в файле DrawSDIView.cpp void CDrawSDIView::OnLButtonDown(UINT nFlags, CPoint point) { // Захватим мышь, чтобы другое приложение не могло // перезахватить мышь в момент, когда она // покинет пределы окна приложения SetCapture(); // мышь только для нас!!! // Сохраним точку начала рисования m_pointPrevPos = point; CView::OnLButtonDown(nFlags, point); } void CDrawSDIView::OnLButtonUp(UINT nFlags, CPoint point) { // Если мышь была ранее захвачена нашим приложением, // то отдадим ее, чтобы другие приложения // тоже могли использовать мышь if(GetCapture() == this) ReleaseCapture(); CView::OnLButtonUp(nFlags, point); } void CDrawSDIView::OnMouseMove(UINT nFlags, CPoint point) { // Если нажата левая кнопка мыши, то рисовать if((nFlags & MK_LBUTTON) == MK_LBUTTON){ // Если мышь захвачена нами, то рисовать if(GetCapture() == this){ // Получить контекст устройства // клиентской области окна приложения CClientDC dc(this); // Добавить линию к документу и вернуть на нее // указатель как на объект, чтобы воспользоваться // ее функцией-членом для рисования MyLine* pLine = GetDocument()->AddLine(m_pointPrevPos, point); // Нарисовать текущую линию pLine->Draw(&dc); // Сохранить текущую точку в качестве предыдущей m_pointPrevPos = point; } // end of if } // end of if CView::OnMouseMove(nFlags, point); }
-
Постройте приложение, порисуйте и поперемещайте клиента заграницы экрана
Видно, что рисунок не восстанавливается, хотя все линии у нас сохранены в классе документа. Функция OnDraw() класса представления срабатывает всякий раз даже при частичном разрушении окна представления или когда данные документа были отмечены как измененные с помощью функции SetModifiedFlag().
-
Добавьте в функцию OnDraw() код прорисовки всех сохраненных линий (не забудьте раскомментировать аргумент указателя CDC* в заголовке функции), как показано в листинге
Код функции OnDraw() в файле DrawSDIView.cpp void CDrawSDIView::OnDraw(CDC* pDC) { CDrawSDIDoc* pDoc = GetDocument(); ASSERT_VALID(pDoc); if (!pDoc) return; // TODO: add draw code for native data here // Получить количество линий в документе int count = pDoc->GetLineCount(); // Если есть линии, то нарисовать их все if(count){ for(int i = 0; i < count; i++){ MyLine* pLine = pDoc->GetLine(i); pLine->Draw(pDC); } } }
- Постройте приложение и убедитесь, что теперь все восстанавливается после разрушения окна приложения