- •Овсянник в.Н. Лабораторные работы по курсу «Объектно-ориентированное программирование»
- •13. Разработка приложения с архитектурой «документ-вид». Часть 2 120
- •14. Разработка приложения с архитектурой «документ-вид». Часть 3 135
- •1.Разработка типового консольного приложения
- •1.1.Методические указания
- •1. Запуск и настройка ис mvs-2008.
- •2.Поиск данных в массиве
- •3.Обработка текстового файла
- •4.Задача «куча камней»
- •5.Реализация алгоритма полного перебора
- •6. Исследование погрешностей вычислений
- •7.Поиск экстремумов с ограничениями или «Брачное агенство»
- •Постановка задачи
- •Задание
- •Листинг файла mAgency.Cpp (с главной функцией)
- •Листинг файла Lib.H
- •Листинг файла Lib.Cpp
- •8.Приложение с окном вида
- •9.Разработка графического редактора
- •Задание.
- •9.1.Проектирование программы
- •9.2.Разработка интерфейса
- •Установка флагов
- •Создание меню и панели инструментов
- •Обработка сообщений о нажатии кнопки мыши
- •9.3.Рисование
- •9.4.Растягивание фигур
- •9.5.Обновление изображения
- •9.6.Сохранение метафайла на диске и его загрузка
- •10.Приложение, основанное на модальном диалоговом окне
- •Методические указания Модальные и немодальные диалоговые окна
- •Ресурсы и элементы управления
- •Сценарий создания приложения
- •Разбор приложения
- •Усовершенствование приложения
- •11.Приложение с контекстным меню и строкой состояния
- •11.1.Методические указания к первой части работы
- •11.2.Сценарий выполнения первой части работы
- •11.3.Методические указания ко второй части работы Строка состояния
- •Определение секций в строке состояния
- •Строка сообщений
- •Индикатор состояния
- •Управление строкой состояния
- •11.4.Сценарий выполнения второй части работы
- •12.Разработка приложения с архитектурой «документ-вид». Часть 1
- •12.1.Сценарий создания приложения
- •13.Разработка приложения с архитектурой «документ-вид». Часть 2
- •13.1.Методические указания Класс коллекций cObList
- •13.2.Сценарий выполнения работы
- •14.Разработка приложения с архитектурой «документ-вид». Часть 3
- •14.1.Методические указания
- •15.Класс вектор
- •15.1.Пример класса tVector
- •15.2.Класс tVector с перегруженными операциями
- •16.Приложение с таблицей
- •16.1.Вариант 1 Сценарий выполнения работы
- •16.2.Вариант 2 Сценарий выполнения работы
12.Разработка приложения с архитектурой «документ-вид». Часть 1
Невозможно создать программу с полной защитой от дураков, ибо дураки крайне изобретательны
Закон Мерфи
Цель работы – освоить разработку приложения, основанного на архитектуре «документ-вид» (4 час.)
Сценарий этой работы основан на материале глав 16 и 17 работы [1]. Характерной особенностью этого приложения является не только его ориентация на архитектуру «документ-представление», но и использование класса CFormView в качестве базового для окна вида.
Суть работы состоит в следующем. Создается каркас SDI-приложения с архитектурой «документ-вид» и в качестве базового класса для класса представления выбирается CFormView. Этот класс подобен классу CDialog в том смысле, что он может быть носителем управляющих элементов: полей ввода, переключателей, списков и т.п. – в отличие от базового класса CView. На следующем шаге Вы должны разработать свой собственный класс, предназначенный для обработки некоторых данных, отличающийся от описанного ниже класса CStudent количеством и типами компонент. В качестве примера в данном сценарии приводится класс CStudent, который содержит только фамилию студента и целочисленный балл. Далее необходимо разместить в окне представления управляющие элементы, предназначенные для ввода (и отображения) данных и реализовать сохранение введенных данных в классе документа.
Задание.
Ознакомьтесь с теоретической частью работы по конспекту лекций (разделы 9, 5.3-5.5).
Создайте приложение в соответствии с приведенным ниже сценарием. Разработайте свой собственный класс и, соответственно, свои управляющие элементы для ввода данных.
Выполните отладку и добейтесь работоспособности приложения.
Осмыслите полученный результат, примененный инструментарий и приемы программирования.
Ответьте на коварные вопросы преподавателя и получите наивысшую оценку. Если удастся продать свое приложение (а за ним уже давно гоняются шпиёны из Microsoft) – поделитесь доходами с кафедрой, например, купите хотя бы один новый компьютерный класс.
12.1.Сценарий создания приложения
Шаг 1 (решительный). Создание каркаса приложения.
Создайте новый проект, присвойте ему имя, выберите Project types=MFC и Templates=MFC Application. В данном сценарии я выбрал для проекта незамысловатое, т.е. гениальное, имя FRM. Придавите ОК. На вкладке Application type:
шлепните по радиокнопке Single document;
слегка щелкните по радиокнопке MFC Standard;
в списке Resource language не забудьте выбрать элемент со значением Русский;
установите переключатель Use Unicode libraries.
Значения остальных свойств не изменяйте под страхом смерти, т.е. незавершения отладки приложения при жизни.
Перейдите к вкладке Generated Classes и для класса представления CFRMView выберите в качестве базового класс CFormView вместо коварно предлагаемого по умолчанию класса CView (рис. 1). Завершите создание каркаса приложения торжественным шлепком по кнопке Finish. Постройте и запустите приложение на выполнение, чтобы посмотреть на его внешний вид. Окно вида напоминает диалоговое окно, но вместе с тем в нем присутствует меню и панель инструментов, что не может не радовать, так как досталось даром.
Шаг 2. Добавление в приложение собственного класса.
Свой собственный класс проще всего добавить в проект с помощью команды меню ProjectèAdd class, в результате выполнения которой появится диалоговое окно, посредством которого Вы и сможете создать свой класс. В этом окне выберите Categories=MFC и Templates=MFC Class и нажмите кнопку Add. В другом появившемся окне выберите в качестве базового (Base class) класс CObject и задайте имя своего класса, например, CStudent. Обратите внимание, что мастер предлагает имена файлов Student.h и Student.cpp, в которых будет располагаться объявление класса и его реализация соответственно. Если не боитесь проклятий возможных читателей вашего проекта – изменяйте имена файлов. Нажмите Finish, если не страшно.
|
Рис. 1. Выбор базового класса CFormView для класса вида CFRMView
Вы могли бы изменить имена файлов, сгенерированные мастером, но я бы этого не рекомендовал.
Файлы Student.h и Student.cpp добавлены в проект, но файл Student.h пока никуда не подключается с помощью директивы препроцессора include – это надо будет впоследствии сделать вручную, так как мастер не может догадаться, где мы собираемся использовать данный класс.
Содержимое файлов Student.h и Student.cpp должно быть таким:
// Файл Student.h #pragma once // CStudent command target class CStudent : public CObject { public: CStudent(); virtual ~CStudent(); };
|
// Student.cpp : implementation file // #include "stdafx.h" #include "FRM.h" #include "Student.h" // CStudent CStudent::CStudent() { } CStudent::~CStudent() { } // CStudent member functions
|
Шаг 3. Добавление членов в класс CStudent.
Для того чтобы класс CStudent был полезен, надо добавить в него член-данные, парочку конструкторов и функцию Dump, предназначенную для вывода отладочной информации. Кроме того, добавьте в файлы макросы DECLARE_DYNAMIC и IMPLEMENT_DYNAMIC, которые гарантируют, что имя класса будет доступно при выводе диагностической информации. Модифицируйте файлы Student.h и Student.cpp в соответствии с приведенными ниже листингами.
// Файл Student.h
#pragma once
class CStudent : public CObject
{
DECLARE_DYNAMIC(CStudent)
public:
CString m_strName; // фамилия студента
int m_nGrade; // отметка
CStudent(); // конструктор по умолчанию
CStudent(const char* szName, int nGrade); // конструктор с параметрами
virtual ~CStudent();
#ifdef _DEBUG
void Dump(CDumpContext& dc) const;
#endif // _DEBUG
};
// Student.cpp : implementation file
#include "stdafx.h"
#include "FRM.h"
#include "Student.h"
// CStudent
IMPLEMENT_DYNAMIC(CStudent, CObject)
CStudent::CStudent()
{
m_nGrade = 0;
}
CStudent::CStudent(const char* szName, int nGrade)
{
m_strName = szName;
m_nGrade = nGrade;
}
CStudent::~CStudent()
{
}
// CStudent member functions
#ifdef _DEBUG
void CStudent::Dump(CDumpContext& dc) const
{
CObject::Dump(dc);
dc << "m_strName = " << m_strName << "\nm_nGrade = " <<m_nGrade;
}
#endif // _DEBUG
Убедитесь в том, что приложение компилируется без ошибок, невзирая на Ваши старания по его усовершенствованию.
Шаг 4. Редактирование меню приложения.
Оставим пока класс CStudent и займемся интерфейсом программы. На вкладке Resource View выберите идентификатор IDR_MAINFRAME в ветви Menu и, незаметно для ИС, замените имеющиеся команды в меню Правка на единственную команду Clear АLL. Введите константу ID_EDIT_CLEARALL как идентификатор команды Clear АLL (см. вкладку Properties).
Шаг 5. В редакторе диалоговых окон измените диалоговое окно IDD_FRM_FORM. Открыв диалоговый ресурс IDD_FRM_FORM в ветви Dialog, сгенерированный мастером, добавьте элементы управления для ввода имени студента и целочисленного балла (рис. 2). Не изменяйте установленных по умолчанию свойств формы Style=Child и Visible=False.
|
Рис. 2. Окно представления с управляющими элементами
Присвойте элементам управления идентификаторы, указанные табл. 1.
Таблица 1.
-
Элемент управления
ID
Элемент ввода фамилии
IDC_NAME
Элемент ввода отметки
IDC_GRADE
Кнопка «Принять»
IDC_ENTER
Шаг 6. Добавьте обработчики сообщений для CFRMView. Выберите класс CFRMView (вкладка Class View) и добавьте указанные ниже обработчики сообщений (табл. 2). Оставьте имена функций по умолчанию.
Таблица 2.
-
Идентификатор ресурса
Сообщение
Обработчик сообщения
IDC_ENTER
BN_CLICKED
OnBnClickedEnter
ID_EDIT_CLEARALL
COMMAND
OnEditClearall
ID_EDIT_CLEARALL
UPDATE_COMMAND_UI
OnUpdateEditClearall
Шаг 7. Добавьте переменные для CFRMView.
Добавьте для элементов ввода Фамилии и Отметки студента переменные, свойства которых приведены в табл. 3. Напомню, что для добавления таких переменных надо в окне редактора ресурсов выбрать элемент ввода, с помощью ПКМ вызвать контекстное меню и в нем выбрать команду Add variable.
Таблица 3.
-
Идентификатор ресурса
Имя переменной
Категория
Тип переменной
IDC_GRADE
m_nGrade
Value
int
IDC_NAME
m_strName
Value
CString
Задайте для m_nGrade минимальное значение 0, максимальное — 100. Заметьте, что мастер генерирует код, необходимый для проверки данных, вводимых пользователем (найдите этот код).
Шаг 8. Добавьте прототип вспомогательной функции UpdateControlsFromDoc. В окне ClassView выберите класс CFrmView щелкните ПКМ и выберите команду AddèAdd Function (добавить функцию-член). Заполните диалоговую форму, чтобы добавить следующую функцию:
private:
void UpdateControlsFromDoc();
Шаг 9. Отредактируйте файл FRMView.cpp. Отредактируйте код функций так, как показано ниже:
|
Так как пока у нас в классе документа переменной m_student нет, то и программа компилироваться без ошибок не будет.
Функция OnBnClickedEnter() заменяет функцию OnOK(), которую можно было бы ожидать в классе диалогового окна. Она передает данные из полей ввода в переменные-члены объекта «вид», а потом в документ. Добавьте код, выделенный полужирным шрифтом:
|
В сложной программе, допускающей несколько представлений данных, команда Clear All передавалась бы непосредственно документу. В нашем же простом примере она доставляется объекту «вид». Обработчик сообщения обновления пользовательского интерфейса отключает элемент меню, если объект CStudent в документе уже пуст. Договоримся, что объект будет считаться пустым, если член m_nGrade==0. Отредактируйте обработчики команды меню в соответствии со следующим листингом:
|
Шаг 10. Добавьте переменную типа CStudent в класс CFRMDoc. Это можно сделать, используя окно ClassView и контекстное меню, тогда директива #include “Student.h”будет добавлена автоматически.
public:
CStudent m_student;
Вот теперь приложение должно компилироваться, хотя у компилятора может быть и свое мнение по этому поводу.
Конструктор класса CStudent вызывается при создании объекта «документ», а деструктор вызывается автоматически при уничтожении документа.
Шаг 11. Отредактируйте файл FRMDoc.cpp. Для инициализации объекта CStudent воспользуйтесь конструктором CFRMDoc:
CFRMDoc::CFRMDoc() : m_student("", 0)
{
TRACE("Document object constructed\n");
}
Мы не определим, правильно ли работает программа FRM, пока не выведем содержимое документа по завершении программы. Для этого воспользуемся деструктором, который вызовет функцию Dump() документа, а та — функцию CStudent::Dump(). А посему отредактируйте код деструктора класса документ и функции Dump() этого же класса. Вы, конечно, заметили, что функция Dump(afxDump) будет вызываться только в отладочной конфигурации приложения, так как ее вызов окружен директивой условной компиляции #ifdef _DEBUG … #endif.
|
Шаг 12. Соберите и протестируйте приложение FRM. Введите какое-нибудь имя и балл, а затем щелкните кнопку Принять. Закройте приложение. Появились ли в окне Debug сообщения, аналогичные показанным ниже?
a CFRMDoc at $00358FF0
m_strTitle = _CrtDbgReport: String too long or IO Error
m_strPathName =
m_bModified = 0
m_pDocTemplate = $00358DF8
a CStudent at $00359044
m_strName = Smith
m_nGrade = 34
Примечание. Чтобы увидеть эти сообщения, компилируйте программу для конфигурации Debug и запустите ее под управлением отладчика (F5, а не Ctrl+F5).
Некоторые пояснения к строке
m_strTitle = _CrtDbgReport: String too long or IO Error
В ней выводится заголовок окна приложения, который в нашем случае представляет собой строку «Без названия - FRM». Сообщение «String too long or IO Error» означает, что функция Dump не может вывести символы кириллицы. Попробуйте с помощью команды приложения ФайлèОткрыть открыть любой файл с именем, не содержащим символов кириллицы, и вы увидите имя этого файла в обсуждаемой строке.
Если Вы попробуете ввести фамилию символами кириллицы, Вы также в окне Debug увидите сообщение «m_strName = String too long or IO Error». В документе фамилия будет сохраняться корректно, но вот в окне отладки мы в лучшем случае увидим «крякозябры». Вывести символы кириллицы в окно отладки можно, например, так:
char Buf[300];
char * GetANSI(CString str)
{
WideCharToMultiByte(1251,WC_NO_BEST_FIT_CHARS,
str,-1,Buf,sizeof(Buf),0,0);
return Buf;
}
TRACE(GetANSI(m_student.m_strName));
А вы не забыли разработать свой собственный класс и добавить на форму элементы, предназначенные для ввода его член-данных?
