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

SDI-приложение MFC

Самостоятельная работа 1: 12 || Самостоятельная работа 2 >
Аннотация: В первой лекции вы изучите, что такое SDI и MDI приложения. Выполните конструирование данных класса документа и класса представления. Для практической значимости выполните: отображение круга на экране, рисование круга в произвольной точке экрана. Сохранение и загрузка данных Документа. Изменение заголовка окна приложения и назначение фильтра. Изменение иконки приложения. Русификация меню. Текст в центре клиентской области.

Спецификация задачи

Файлы к данной лабораторной работе, Вы можете скачать здесь.
  1. При запуске SDI -приложения внутри окна представления в точке x=200, y=100 должен появиться круг радиусом 20 пикселов.
  2. Круг перерисовывается с центром в месте щелчка мыши.
  3. Можно сохранить круг в файле и считать его на экран.
  4. Предусмотреть возможность печати круга на принтер.
  5. Основные пункты меню должны дублироваться кнопками на панели инструментов.
  6. Пункты меню необходимо русифицировать.
  7. Оформить требуемые подcказки в строке состояния главного окна-рамки и всплывающие подсказки.
  8. Вывести текст в центре окна документа.

Приступим к решению поставленной задачи.

Создание заготовки приложения

На последней вкладке цифра 4 означает размер списка последних обработанных документов.

  • Просмотрите все вкладки мастера и нажмите кнопку Finish, чтобы мастер создал заготовку приложения
  • В раскрывающемся списке Solution Configurations поменяйте режим работы оболочки с Debug на Release, чтобы отключить генерацию отладочного кода при сборке проекта
  • Запустите заготовку, созданную мастером, на компиляцию и линковку командой Build/Build Solution, а затем на выполнение командой Debug/Start Debugging (F5)

Появится полноценное окно Windows с однодокументным интерфейсом ( SDI )


Конструирование данных класса документа

В классе документа нам нужно добавить поля, в которых будут храниться координаты центра будущего круга.

  • Вызовите панель View/Solution Explorer и в дереве файлов проекта двойным щелчком откройте на редактирование заголовочный файл CircleDoc.h
  • Найдите объявление класса CCircleDoc и добавьте в него объявления полей, которые будут хранить координаты центра круга
    Объявления полей в классе документа CCircleDoc 
    class CCircleDoc : public CDocument
    {
    protected: // create from serialization only
        CCircleDoc();
        DECLARE_DYNCREATE(CCircleDoc)
    	
    // Attributes
    public:
        int m_PosX; // координата x (по горизонтали) центра круга
        int m_PosY; // координата y (по вертикали) центра круга
    		
    // Operations
    public:
    	...................................................
    }

В объявлении класса язык C++ не допускает инициализацию членов-данных (кроме перечисления enum ). Поэтому инициализацию поместим в функцию-член класса, которая выполняется при создании нового документа. Для этого

  • В раскрывающемся списке Functions in CCircleDoc (сведения о CCircleDoc ) выберите функцию-метод OnNewDocument()

Именно функция OnNewDocument() срабатывает при создании нового документа.

  • Внесите в функцию OnNewDocument() код инициализации объявленных координат круга
    Инициализация полей в функции OnNewDocument() класса CCircleDoc
    BOOL CCircleDoc::OnNewDocument()
    {
    	if (!CDocument::OnNewDocument())
    		return FALSE;
    	
    	// TODO: add reinitialization code here
    	// (SDI documents will reuse this document)
    	m_PosX = 200;
    	m_PosY = 100;
    	
    	return TRUE;
    }

Конструирование данных класса представления

Схема "Документ-Представление" реализует отношение "один ко многим", т.е. одни и те же данные класса Документа можно по разному отображать пользователю в классах Представления. В нашем случае будет только одно представление данных Документа. Класс Представления CCircleView будет выполнять непосредственное взаимодействие с пользователем и в нем тоже понадобятся поля для хранения координат центра круга.

  • Через панель Solution Explorer откройте заголовочный файл CircleView.h, найдите объявление класса CCircleView и внесите в него объявления переменных-членов, которые будут хранить координаты центра круга
    Объявления полей в классе представления CCircleView
    class CCircleView : public CView
    {
    protected: // create from serialization only
    	CCircleView();
    	DECLARE_DYNCREATE(CCircleView)
    	
    // Attributes
    public:
    	CCircleDoc* GetDocument() const;
    	int m_PosX; // координата x (по горизонтали) центра круга
    	int m_PosY; // координата y (по вертикали) центра круга
    	
    // Operations
    public:
    	.....................................................
    };

Значения полей, хранящих координаты центра круга, в классе Документа и в классе Представления всегда должны совпадать, поэтому удобнее назначить им одинаковые имена. Обратите внимание на то, что здесь уже имеется прототип функции GetDocument(), которая будет возвращать указатель на объект класса CCircleDoc, чтобы можно было вызывать его члены . Эта функция нам понадобится дальше, чтобы адресоваться из класса-представления CCircleView к данным, хранящимся в документе, представленном классом CCircleDoc. Мастер уже создал и реализацию функции GetDocument(), которая имеет вид

Реализация функции GetDocument()
CCircleDoc* CCircleView::GetDocument() const // non-debug version is inline
{
	ASSERT(m_pDocument->IsKindOf(RUNTIME_CLASS(CCircleDoc)));
	return (CCircleDoc*)m_pDocument;
}

Инициализацию объявленных в классе представления CCircleView переменных поместим в функцию-член OnInitialUpdate() класса представления, которая выполняется один раз при первом отображении документа на экране. В ней мы предусмотрим начальную перекачку данных из класса Документа в класс Представления. Для этого

  • Откройте панель Class View оболочки и выделите класс CCircleView

  • В панели Properties нажмем кнопку Overriders и в появившемся списке виртуальных функций класса CCircleView, унаследованных от базового класса CView, выделите функцию OnInitialUpdate()
  • В появившемся раскрывающемся списке выполните команду Add, чтобы мастер добавил в файл CCircleView.cpp заготовку для переопределения виртуальной функции OnInitialUpdate()
  • Дополните функцию OnInitialUpdate() кодом, копирующим содержимое переменных из объекта Документ в объект Представление
    Копирование координат центра круга из Документа в Представление
    void CCircleView::OnInitialUpdate()
    {
    	CView::OnInitialUpdate();
    	
    	// TODO: Add your specialized code here and/or call the base class
    	m_PosX = GetDocument()->m_PosX;
    	m_PosY = GetDocument()->m_PosY;
    }

Отображение круга на экране

Для рисования круга на экране в окне Документ нужно поместить соответствующий код в функцию-член OnDraw() класса CCircleView. Эта функция выполняется всякий раз, когда нужно вывести окно документа из-за возникших в нем изменений

  • В панели Solution Explorer откройте файл CircleView.cpp и через раскрывающийся список в верхней части рабочей области редактора позиционируйтесь на метод OnDraw()
  • Освободите от комментариев указатель на контекст устройства в аргументах метода OnDraw() и дополните тело кодом рисования круга
    Код рисования круга в объекте Документа
    void CCircleView::OnDraw(CDC* pDC)
    {
    	CCircleDoc* pDoc = GetDocument();
    	ASSERT_VALID(pDoc);
    	if (!pDoc)
    		return;
    	
    	// TODO: add draw code for native data here
    	
    	// Задать границы описывающего прямоугольника, 
    	// в котором будет нарисован круг
    	CRect rect;
    	rect.left = m_PosX - 20;
    	rect.top = m_PosY - 20;
    	rect.right = m_PosX + 20;
    	rect.bottom = m_PosY + 20;
    		
    	// Нарисовать круг на контексте устройства
    	pDC->Ellipse(&rect);
    }
  • Постройте приложение и убедитесь, что получился примерно такой результат

Рисование круга в произвольной точке экрана

Результатом предыдущих действий было появление круга в фиксированной точке с программно заданными координатами x=200, y=100. Сделаем так, чтобы по щелчку мыши в произвольном месте клиентской области окна, представленного объектом CCircleView, круг перерисовывался с центром в месте щелчка.

При этом, новое положение центра круга необходимо немедленно продублировать в классе Документа. Это нужно для того, что если пользователь сохранит Документ и закроет приложение, то при следующем открытии документа последнее состояние Представления полностью восстановится.

  • В панели Class View вызовите контекстное меню для класса CCircleView и выполните команду Properties для вызова одноименной панели
  • Щелкните на пиктограмме Messages в верхней части панели Properties, найдите событие WM_LBUTTONDOWN, которое генерируется операционной системой всякий раз при щелчке по объекту класса представления CCircleView, и в раскрывающемся списке добавьте обработчик OnLButtonDown()

    Оболочка сразу внесет необходимые изменения в файлы CCircleView.h и CCircleView.cpp: добавит записи в объявление класса и таблицу сообщений, а также создаст заготовку обработчика. Эти автоматически созданные изменения будут следующими

    Объявление обработчика в файле CCircleView.h
    class CCircleView : public CView
    {
    	...............................................................
    // Generated message map functions
    protected:
    	DECLARE_MESSAGE_MAP()
    public:
    	virtual void OnInitialUpdate();
    public:
    	afx_msg void OnLButtonDown(UINT nFlags, CPoint point);
    };
    Регистрация события в таблице сообщений класса CircleView
    BEGIN_MESSAGE_MAP(CCircleView, 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()
    END_MESSAGE_MAP()
    Заготовка обработчика в файле CircleView.cpp
    void CCircleView::OnLButtonDown(UINT nFlags, CPoint point)
    {
    	// TODO: Add your message handler code here and/or call default
    	
    	CView::OnLButtonDown(nFlags, point);
    }
  • Дополните обработчик OnLButtonDown() класса CCircleView следующим кодом
    Код обработчика щелчка мыши на Представлении
    void CCircleView::OnLButtonDown(UINT nFlags, CPoint point)
    {
    	// TODO: Add your message handler code here and/or call default
    	
    	// Обновить координаты центра круга
    	// координатами места щелчка курсора 
    	m_PosX = point.x;
    	m_PosY = point.y;
    	
    	// Инициировать перерисовку экрана вызовом функции OnDraw()
    	Invalidate();
    	
    	// Передать новые координаты щелчка в класс CCircleDoc 
    	CCircleDoc* pDoc = GetDocument();
    	pDoc->m_PosX = m_PosX;			// Через промежуточный указатель
    	GetDocument()->m_PosY = m_PosY; // Напрямую
    	
    	// Установить флажок изменения Документа
    	pDoc->SetModifiedFlag(TRUE);
    	
    	CView::OnLButtonDown(nFlags, point);	// Выполнить базовый метод
    }
  • Постройте приложение и убедитесь, что круг перерисовывается по каждому щелчку мыши на новом месте
Самостоятельная работа 1: 12 || Самостоятельная работа 2 >
Александр Даниленко
Александр Даниленко
Стоит Windows 8 Pro, Visual Studio 2010 Express Edition .