Рекомендации по написанию программы.




Задание 1. Разработка программы редактирования файлов специализированного формата.

 

Исходные данные:

  1. Программа является оконным Windows32 приложением.
  2. Программа написана на языке Си++ в среде Visual C++ 6.0, VS.NET или VS2005
  3. При написании программы используются функции, классы и структуры MFC и Win API32(если понадобиться). Никаких других технологий или «надстроек» не должно быть использовано.

 

Требования к внешнему виду программы:

 

  1. Типовой рекомендуемый внешний вид приложения:

 

  1. Основное назначение программы:

- отображение BMP файлов в двух форматах по выбору: как рисунок и как информация о рисунке (реальный размер);

- в случае просмотра рисунка его размер должен изменяться автоматически с изменением окна просмотра;

- количество одновременно открытых файлов не ограничено;

- если файл уже открыт и производится попытка его повторного открытия, то соответствующий файл вновь не открывается, а окно просто перемещается на передний план.

 

Рекомендации по написанию программы.

  1. Начальные действия.

Запустите Visual C++.

Menu -> File -> New -> Projects -> MFC AppWizard (exe)

Задайте Имя проекта и его расположение.

Выберите:

- Multiple Documents (step 1 of 6)

- Document/View architecture support (step 1 of 6)

- No database support (step 2 of 6)

- No compound document support (step 3 of 6)

- No Automation (step 3 of 6)

- Not an ActiveX control (step 3 of 6)

- Docking Toolbar (step 4 of 6)

- Initial Status bar (step 4 of 6)

- Printing and Print preview (step 4 of 6)

- NO Context-sensitive Help (step 4 of 6)

- 3D Controls (step 4 of 6)

- NO MAPI (step 4 of 6)

- NO Windows Socket (step 4 of 6)

- Toolbar looks Normal (step 4 of 6)

- MFC Standard project style (step 5 of 6)

- Do not change any file names (step 6 of 6)

В средах VS.NET, VS2005 последовательность действий практически идентична.

 

  1. Создание класса для поддержки BMP файлов.

Добавление чего-то, что вносит существенные изменения, требует создания дополнительного класса. В нашем случае это поддержка BMP файлов: загрузка и отображение.

Для добавления классов удобно использовать ClassWizard

- запускаем ClassWizard (Menu -> View -> ClassWizard)

- нам нужен новый класс, поэтому выбираем Add Class ->New…

- называем его MyBMP

- в качестве базового нам подходит самый простой класс CObject, однако такого в списке нет и приходится выбирать наипростейший, например, CStatic

В VS.NET и VS2005 ClassWizard отсутствует в явном виде, но функциональность вся тем не менее поддерживается. В частности добавить класс здесь можно в окне Solution Explorer в закладке Class View выбрать корневой элемент (название проекта) и в меню по правой кнопке мыши выбрать элемент “Add” -> “Add Class…”, далее в Categories выбрать MFC, в правой части окна MFC Class. Далее Open, задаем имя класса и в качестве базового выбираем CObject.

В результате в проекте появятся два файла MyBMP.cpp и MyBMP.h

Теперь «упростим» наш класс, т.е. уберем все лишнее.

- в качестве базового класса укажем CObject вместо CStatic

- удалим карту сообщений и оставим только Конструктор и деструктор

Теперь при любой попытке войти в ClassWizard, он будет ругаться, что не может найти информацию о данном классе и предложить найти ее вручную или исключить класс из списка классов, контролируемых Wizardом. Нам подходит последнее, т.к. все, что надо мы и вручную добавим.

Проблема не появилась бы, если использовать среду Visual C++ 7.0, т.к. там есть CObject класс в качестве непосредственного базового (так и есть).

Другим путем создания класса (впрочем с теми же трудностями) является использование закладки ClassView в проекте. Там можно также использовать контекстное меню Add Class.

Заметьте также, что добавление данного класса можно было осуществить и вручную, а не с использованием ClassWizard и результат был бы таким же.

 

  1. Наполнение MyBMP класса и первый тест

Первым делом убедимся в работоспособности нашего класса. Для этого добавим в него функцию прорисовки:

BOOL Draw(CDC *pDC,RECT *pRect);

Типы параметров должны быть вам знакомы. Первый – класс контекста дисплея и второй – прямоугольная область (где осуществлять прорисовку).

В качестве тела (в cpp файл) добавим прорисовку прямоугольника (пока):

pDC->FillSolidRect(0,0,100,100,0xFF0000); return TRUE;

Класс документа должен содержать структуру нашего объекта, коим является BMP рисунок. Так как поддержка BMP у нас возложена на MyBMP класс, то логично добавить в класс формата документа (…Doc) объект класса MyBMP:

MyBMP ozbmp; // в публичную секцию (не забудьте также про h файл)

Теперь у нас две задачи. В классе Doc осуществить загрузку информации, а в классе View отображать ее корректно. В этом основной принцип Documrnt/View модели. С загрузкой пока подождем, а прорисовку добавим. Нам необходим класс …View. Там есть метод OnDraw (вы можете найти его напрямую или через ClassWizard).

Там уже есть код получения объекта документа – это тот объект, который описан в Doc классе. Добавим прорисовку:

pDoc->ozbmp.Draw(pDC,NULL);

Проверьте, теперь в каждом окне при запуске программы будет прорисован квадрат синего цвета 100 на 100.

 

  1. Уточнение области прорисовки.

Теперь займемся определением области вывода. Проблема в том, что вывод может осуществляться как в окно, так и на внешнее растровое устройство, например принтер. Пути «прохождения» сообщений для прорисовки в этих случаях различны. Попробуем это решить.

Добавим в публичную секцию класса View прямоугольник:

RECT m_rectDraw;

В методе прорисовки класса View чуть поменяем код:

GetClientRect(&m_rectDraw);

pDoc->ozbmp.Draw(pDC, &m_rectDraw);

и, соответственно в методе прорисовки класса MyBMP:

pDC->FillSolidRect(pRect,0xFF0000);

Проверим. Теперь при смене габаритов окна прямоугольник всегда принимает нужный размер. Однако, если вы попытаетесь просмотреть как рисунок будет выглядеть на странице принтера (Menu->File->Print Preview), то увидите, что там рисуется лишь маленький прямоугольник и он не масштабируется на всю страницу. Займемся этим.

При печати на принтере (и при просмотре макета страницы) используется метод OnPrint, обрабатывающий соответствующее сообщение от Windows. У нас в классе View такого метода нет. Добавим его. Запустите ClassWizard, выбирите класс View, в списке Messages выберите OnPrint. Теперь нажмите кнопку AddFunction. Достаточно просто.

В VS.NET и VS2005 это следует делать так. Переходим в обзор Class View, выбираем наш класс, открываем его и там находим элемент Maps. Это карты класса MFC. Нам нужна карта сообщений MESSAGE (она там единственная). Выберите ее. Теперь вам надо перейти к реализации карты в исходном файле (участок кода в *.cpp файле-реализации класса между макросами BEGIN_MESSAGE_MAP и END_MESSAGE_MAP). Обычно это можно сделать двойным кликом на элементе MESSAGE. Ваша задача расположить курсор ввода внутри кода карты сообщений. Тогда в тулбаре окна Properties появится кнопка Messages. Нажмите на нее и выберите сообщение, обработчик которого надо добавить/изменить. Однако, если обработчик сообщения уже реализован в базовом классе, вам следует искать обработчик в списке методов базового класса, а не обработчиков сообщений. Для этого нажмите другую кнопку в тулбаре окна Properties. Она называется Overrides. В списке найдите нужный метод и в опциях выберите <Add> … В нашем случае найдите метод OnPrint и выберите опцию <Add> OnPrint. В ваш код будут добавлен соответствующий метод.

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

m_rectDraw = pInfo->m_rectDraw;

OnDraw(pDC);

Вызов же базовой функции закоментируем.

Однако, в этом случае мы получаем m_rectDraw здесь, а не в функции прорисовки, поэтому и там необходимо внести изменения:

if(pDC->IsPrinting()){ pDoc->ozbmp.Draw(pDC, &m_rectDraw); }

else{ GetClientRect(&m_rectDraw); pDoc->ozbmp.Draw(pDC, &m_rectDraw); }

 

  1. Загрузка и отображение BMP файлов.

Добавим функцию загрузки BMP файла в наш класс MyBMP:

BOOL ZBmp::Load(const char *szFileName);

параметр – имя файла для загрузки (с путем)

Для подгрузки и дальнейшего отображения BMP файла удобно поступать так:

- загрузить BMP файл с помощью LoadImage (вам уже знакомо)

- преобразовать хэндл в класс CBitmap с помощью метода CBitmap::FromHandle()

- запросить совместимый контекст дисплея CreateCompatibleDC()

- выбрать объект CBitmap в качестве активного SelectObject()

- использовать функцию StretchBlt для масштабирования CBitmap до нужных размеров вывода

Кроме метода Load вам понадобиться изменить и метод Draw класса MyBMP. Сделайте это самостоятельно. Добейтесь эффекта автоматического масштабирования картинки под окно вывода

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

ozbmp.Load(ar.GetFile()->GetFilePath());

 

  1. Добавление второго типа окна просмотра.

Document/View модель позволяет добавлять любое количество просмотрщиков (View) к любому документу (Doc). Добавим дополнительный View класс для отображения технической информации о BMP файле (размер по X и по Y).

Для этого легче всего скопировать cpp и h файлы уже существующего класса View, добавить их в проект и изменить там везде имя класса на новый, скажем View2. Того же эффекта можно добиться путем добавления класса с помощью ClassWizard, однако это может потребовать больше усилий. Выберите любой вариант и добавьте класс самостоятельно. Добейтесь компиляции проекта без ошибок. Не забудьте, что, если вы копируете вручную, то вы должны заменять имя класса не только к коде C++, но и в спец Макро и спес Комментариях.

Теперь нам необходимо добавить еще одну константу в ресурсах. В строковых ресурсах скопируйте текущую строку с описание типа ресурса (IDR_…TYPE) под новым именем (IDR_…TYPE2). В тексте представлена спец. Последовательность, которая определяет тип документа, его расширение, его название и т.д. Подробную информацию о формате данной строки вы можете найти в Помощи по методу GetDocString класса CdocTemplate. Попробуйте варьировать значения строки, чтобы типы отображения были различны.

В основном файле проекта найдите место, где формируется шаблон данного Document/View взаимодействия и добавьте туда еще один шаблон для того же документа:

CMultiDocTemplate* pDocTemplate2;

pDocTemplate2 = new CMultiDocTemplate(

IDR_…TYPE2,

RUNTIME_CLASS(C…Doc),

RUNTIME_CLASS(CChildFrame), // custom MDI child frame

RUNTIME_CLASS(C…View2));

AddDocTemplate(pDocTemplate2);

здесь многоточие означает часть имени классов, зависящее от имени вашего проекта.

Теоретически теперь будут работать оба вьюера. Проверить это легко. При запуске программы она спросит у вас какой тип вьюера использовать для отображения файла.

В дальнейшем нам понадобится получать доступ к зарегистрированным шаблонам. Для этого следует шаблонные переменные сделать не локальными, а членами класса приложения. Перенесите их описание в класс C…App:

CMultiDocTemplate* pDocTemplate;

CMultiDocTemplate* pDocTemplate2;

Последний необязательный шаг – добавление иконки для нового вьюера. В ресурсах добавьте иконку с идентификатором, соответствующем нашему новому типу вьюера.

 

  1. Отображение текстовой информации.

В новом классе-вьюере достаточно лишь сменить вызов функции в функции перерисовки с Draw на DrawText. Все остальное может оставаться в нем так же как и в первом вьюере (не зря же мы его писали).

Понятно, что необходимо добавить эту функцию в MyBMP класс:

BOOL DrawText(CDC *pDC, RECT *pRect);

Здесь вам понадобиться вывести текст (форматированный). Для этого вам могут понадобиться:

- CDC::GetTextMetrics()

- wsprintf()

- CDC::DrawText()

 

  1. Последние шаги.

Запретим открытие пустого окна при старте и, напротив, будем открывать окно, если файл указан в командной строке. Найдите след. строку в главном файле проекта:

if (!ProcessShellCommand(cmdInfo)) return FALSE;

закомментируйте его и поместите туда другой код:

if (m_lpCmdLine[0]!= '\0'){

// open an existing document

OpenDocumentFile(m_lpCmdLine);

}

// Enable drag/drop open

m_pMainWnd->DragAcceptFiles();

Попутно мы подключили поддержку Drag&Drop. Просто, верно?

 

Добавим поддержку в меню переключения с одного вьюера на другой. В редакторе ресурсов скопируйте меню под именем IDR_…TYPE и установите имя нового меню в IDR_…TYPE2. Как видите константа меню соответствует типу вьюера (см. параграф 6).

Теперь в обоих этих меню добавьте по два элемента:

Menu->View->Show Picture с константой ID_VIEW_PIC и

Menu->View->Show Info с константой ID_VIEW_TEXT

Теперь надо добавить функции – реакции на новые элементы меню.

В ClassWizard выберите класс CMainFrame, в поле Object IDs найдите ID_VIEW_PIC, в поле Messages выберите COMMAND, нажмите Add Function и добавьте функцию OnViewPic. Аналогичные действия проведите для второго элемента и добавьте функцию OnViewText.

Теперь несколько «нечеткая» последовательность.

Заполните ваши обработчики новых команд меню так:

void CMainFrame::OnViewPic(){

CreateOrActivateFrame(theApp.pDocTemplate,RUNTIME_CLASS(CTest2View));

}

void CMainFrame::OnViewText(){

CreateOrActivateFrame(theApp.pDocTemplate2,RUNTIME_CLASS(CTextBView));

}

Как видите – они только вызывают некую функцию, которую также необходимо вручную добавить в класс CMainFrame:

void CMainFrame::CreateOrActivateFrame(CDocTemplate* pTemplate,CRuntimeClass* pViewClass)

{

CMDIChildWnd* pMDIActive = MDIGetActive(); ASSERT(pMDIActive!= NULL);

CDocument* pDoc = pMDIActive->GetActiveDocument(); ASSERT(pDoc!= NULL);

CView* pView;

POSITION pos = pDoc->GetFirstViewPosition();

while (pos){

pView = pDoc->GetNextView(pos);

if (pView->IsKindOf(pViewClass))

{

pView->GetParentFrame()->ActivateFrame();

return;

}

}

CMDIChildWnd* pNewFrame=(CMDIChildWnd*)(pTemplate->CreateNewFrame(pDoc, NULL));

if (pNewFrame == NULL) return; // not created

ASSERT(pNewFrame->IsKindOf(RUNTIME_CLASS(CMDIChildWnd)));

pTemplate->InitialUpdateFrame(pNewFrame, pDoc);

}

Если вы разберете текст функции, то увидите, что основное ее назначение – избежать повторного открытия окна для файла, который уже открыт в одном из окон. Разберитесь с этим самостоятельно.

И последний штрих. Как видите здесь используется переменная theApp, однако она не видна от сюда. Сделаем ее видимой – добавим ее описание (она уже создается в главном файле проекта) прямо за описанием класса – в h файл:

extern C…App theApp;

 



Поделиться:




Поиск по сайту

©2015-2024 poisk-ru.ru
Все права принадлежать их авторам. Данный сайт не претендует на авторства, а предоставляет бесплатное использование.
Дата создания страницы: 2019-03-15 Нарушение авторских прав и Нарушение персональных данных


Поиск по сайту: