Опубликован: 17.08.2010 | Доступ: свободный | Студентов: 999 / 59 | Оценка: 4.11 / 3.89 | Длительность: 29:38:00
Самостоятельная работа 9:

Однодокументный интерфейс 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:

  1. GetLineCount() - возвращает количество объектов-линий, хранящихся в массиве m_Lines класса CObArray
  2. 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);
        }
      } 
    }
  • Постройте приложение и убедитесь, что теперь все восстанавливается после разрушения окна приложения
Александр Даниленко
Александр Даниленко
Стоит Windows 8 Pro, Visual Studio 2010 Express Edition .