Добавил:
Опубликованный материал нарушает ваши авторские права? Сообщите нам.
Вуз: Предмет: Файл:

Паппас К., Мюррей У. - Visual C++ 6. Руководство разработчика - 2000

.pdf
Скачиваний:
290
Добавлен:
13.08.2013
Размер:
4.96 Mб
Скачать

* // включается поддержка составных файлов EnablecompoundFile ( ) ;

//TODO: здесь добавьте код конструктора.

}

CCntDoc::-CCntDoc()

{

}

BOOLCCntDoc: : OnNewDocument( )

{

if ( ! COleDocument : : OnNewDocument ( ) ) return FALSE;

//TODO: здесь добавьте код повторной инициализации

//(специфика SDI-приложений)

return TRUE;

}

////////////////////////////////////////////////////////////////

// Сериализация класса CCntDoc

 

void CCntDoc::Serialize(CArchive sar)

{

if (ar. IsStoringO )

 

{

 

//TODO: здесь добавьте код сохранения.

}

else.

//TODO: здесь добавьте код загрузки.

}

//Вызов функции базового класса COleDocument обеспечивает

//сериализацию объектов COleClientltem, содержащихся

//в документе контейнера.

COleDocument : : Serialize (ar ) ;

}

////////////////////////////////////////////////////////////////

//Диагностика класса CCntDoc tifdef _DEBUG

void CCntDoc::AssertValid() const

{

COleDocument::AssertValid(); }

void CCntDoc: : Dump (CDumpContext .&dc) const { COleDocument: :Dump(dc) ; } #endif //_DEBUG

////////////////////////////////////////////////////////////////

//Другие функции класса CCntDoc

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

Обратите внимание на функцию EnableCompoundFile() , вызываемую в конструкторе. Эта функция осуществляет поддержку составных файлов, что позволяет сохранять документы с внедренными объектами в специальном структурированном формате.

Файл CNTVIEW.CPP

Файл CNTVIEW.CPPтакже имеет ряд существенных особенностей по сравнению

с соответствующим файлом приложения Graph.

// CntView.cpp : реализация класса CCntView

//

#include "stdafx.h" #include "Cnt.h"

401

#include "CntDoc.h" #include "Cntrltem.h" #include "CntView.h" #ifdef _DEBUG #define new DEBUG_NEW #undef THIS_FILE

static char THIS_FILE[] = _FILE_; #endif

////////////////////////////////////////////////////////////////

// CCntView IMPLEMENT_DYNCREATE(CCntView, CView)

BEGIN_MESSAGE_MAP(CCntView, CView) //{{AFX_MSG_MAP(CCntView)

//ПРИМЕЧАНИЕ: мастер классов будет добавлять и удалять здесь

//макросы схемы сообщений.

//НЕРЕДАКТИРУЙТЕ то, что здесь находится. ON_WM_DESTROY() ON_WM_SETFOCUS() ON_WM_SIZE()

ON_COMMAND (ID_OLE_INSERT_NEW, OnlnsertObject) ON_COMMAND(ID_CANCEL_EDIT_CNTR, OnCancelEditCntr) //}}AFX_MSG_MAP //

Стандартныекомандыпечати

ON_COMMAND(ID_FILE_PRINT, CView::OnFilePrint) ON_COMMAND(ID_FILE_PRINT_DIRECT, CView::OnFilePrint) ON_COMMAND(ID_FILE_PRINT_PREVIEW, CView::OnFilePrintPreview) END_MESSAGE_MAP ()

////////////////////////////////////////////////////////////////

//Конструктор и деструктор класса CCntView CCntView::CCntView() {

m_pSelection = NULL;

//TODO: здесь добавьте код конструктора.

}

CCntView::-CCntView()

}

BOOL CCntView::PreCreateWindow(CREATESTRUCT Ses) { // TODO: здесь можно модифицировать класс окна,

//изменяя поля структуры сз. return CView::PreCreateWindow(cs); }

////////////////////////////////////////////////////////////////

//Отображение документа

void CCntView::OnDraw(CDC* pDC) { CCn.tDoc* pDoc = GetDocument () ; ASSERT_VALID(pDoc);

//TODO: здесь добавьте код для отображения собственных данных.

//TODO: должны отображаться все OLE-объекты,

//содержащиеся в документе.

//Выделенный элемент может быть нарисован в произвольном месте.

//Этот код следует удалить в том случае, если вы вводите

//собственный код рисования. Указанные ниже координаты

//вточности соответствуют координатам, возвращаемым

//объектом CCntCntrltem, что создает эффект непосредственного

//редактирования.

//TODO: удалите следующий код, если реализуете собственный // код рисования.

if (m_pSelection == NULL)

{

POSITION pos = pDoc-X3etStartPosition() ;

402

m_pSelection = (CCntCntrItem*) pDoc-XSetNextClientltern(pos); > if (m_pSelection != NULL)

m_pSelection->Draw(pDC, CRect(10, 10,210,210));

}

void CCntView::OnInitialUpdate() { CView::OnInitialUpdate();

//TODO: удалите следующий код, если реализуете собственный

//код инициализации.

m_pSelection = NULL;

// инициализация переменной, содержащей

//указатель на выделенный объект

}

////////////////////////////////////////////////////////////////

//Печать документа

BOOL CCntView: :OnPreparePrinting (CPrintlnf о* plnfo) {

//стандартные действия по подготовке к печати return DoPreparePrinting (plnfo) ;

}

void CCntView::OnBeginPrinting (CDC* /*pDC*/, CPrintlnfo* /*plnfo*/) {

//TODO: добавьте код дополнительной инициализации перед печатью. } void CCntView::OnEndPrinting(CDC* /*pDC*/, CPrintlnfo* /*plnfo*/) ""

{

//TODO: добавьте код очистки после печати.

}

void CCntView::OnDestroy()

{

//Деактивиэировать объект при удалении; это важно,

//если используется режим разделения окна просмотра. CView::OnDestroy(); COleClientltem* pActiveltem = GetDocument()-> GetlnPlaceActiveltem(this); if (pActiveltem != NULL && pActive!tem-> GetActiveView() == this) {

pActive!tem->Deactivate0; ASSERT(GetDocument()-> GetlnPlaceActiveltem(this) == NULL);

}

}

////////////////////////////////////////////////////////////////

//Поддержка OLE-клиентов

BOOL CCntView::IsSelected(const CObject* pDocItem) const

{

//Представленная ниже реализация подходит, если выделенными

//являются только объекты CCntCntrltem. В противном случае

//данный код следует заменить.

//TODO: реализуйте функцию, которая проверяет тип выделенного объекта.

return pDocItem == m_pSelection; } void CCntView::0nlnsert0bject() (

//Вызов стандартного диалогового окна InsertObject

//для получения информации о новом объекте CCntCntrltem. COlelnsertDialog dig;

if (dlg.DoModaK) != IDOK) return; BeginWaitCursor() ;

CCntCntrltem* pltem = NULL; TRY

{

403

//Создаем новый объект, связанный с этим документом. CCntDoc* pDoc = GetDocument();

ASSERT_VALID(pDoc);

pltem = new CCntCntrltem(pDoc); ASSERT_VALID(pltem);

//Инициализация объекта на основании данных,

//полученных из диалогового окна.

if (!dig.Createltem(pltem)) AfxThrowMemoryException();

//подойдет исключение любого типа

ASSERT_VALID(pltem);

//Если объект был выбран из списка классов, а не загружен

//из файла, запускаем сервер для редактирования объекта, if (dlg.GetSelectionType() ==

COlelnsertDialog::createNew!tem) p!tem->DoVerb(OLEIVERB_SHOW, this); ASSERT_VALID(pltem);

//Последний введенный объект выделяется.

//TODO: введите код, соответствующий требованиям вашего приложения. m_pSelection = pltem;

//Указатель устанавливается на

//последний введенный объект

pDoc->UpdateAHViews (NULL) ; CATCH(CException, e) if (pltem != NULL) ASSERT_VALID(pltem) ; p!tem->Delete() ; AfxMessageBox(IDP_FAILED_TO_CREATE); END_CATCH EndWaitCursor(); >

//Следующий обработчик позволяет с помощью клавиатуры

//прерывать сеанс непосредственного редактирования.

//Инициируется это контейнером, а не сервером, voidCCntView::OnCancelEditCntr()

//Редактируемый объект закрывается.

COleClientltem* pActiveltem= GetDocument()-> GetlnPlaceActiveltem(this); if (pActiveltem != NULL) pActive!tem->Close(); ASSERT(GetDocument()->GetInPlaceActiveItem(this) == NULL);

}

//Обработчики OnSetFocusи OnSize требуются контейнеру

//в случае непосредственного редактирования объекта. void CCntView: :OnSetFocus (CWnd* pOldWnd)

{

COleClientltem* pActiveltem = GetDocument ( ) -> GetlnPlaceActiveltem(this) ; if (pActiveltem != NULL && pActive!tem->GetItemState () ==

COleClientltem: :activeUIState) {

//Фокус необходимо установить на объект, если он находится

//в той же области просмотра.

CWnd* pWnd = pActiveltem- >GetInPlaceWindow( ); if (pWnd != NULL)

{

pWnd->SetFocus( ) ; // метод SetFocus базового класса не вызывается return;

}}

CView::OnSetFocus(pOldWnd); }

void CCntView: :OnSize (UINT nType, int ex, int cy)

404

{ • '

CView: :OnSize (nType, ex, cy) ; COleClientltem* pActiveltem = GetDocument ()->

GetlnPlaceActiveltem(this) ; if (pActiveltem != NULL) pActiveItem->SetItemRects () ;

}

/////////////////////////////////////////////////

// ДиагностикаклассаCCntView tifdef _DEBUG

void CCntView::AssertValid() const

{

CView::AssertValid(); }

void CCntView::Dump(CDumpContext Sdc) const CView::Dump(dc); }

CCntDoc* CCntView::GetDocument() // отладочнаяверсия

ASSERT(m_pDocument->IsKindOf(RUNTIME_CLASS(CCntDoc))); return (CCntDoc*)m_pDocument;

#endlf //_DEBUG

////////////////////////////////////////////////

// Обработчики сообщений класса CCntView

Чтобы приложение могло отображать внедренные объекты, были внесены изменения в функцию OnDraw( ). Обратите внимание на такой фрагмент:

if (m_pSelection !=NULL)

m_pSeleotion->Draw(pDC, CRectdO, 10,210, 210));

}

По умолчанию объект размещается в окне приложения в заранее заданной области, которая определяется конструктором CRect( ) . Указанные координаты можно изменить вручную.

В файл были также добавлены функции OnlnitialUpdate() , IsSelectedO, OnlnsertObject() , OnCancelEditCntr() , OnSetFocusOИ OnSize(). В частности, функция OnlnsertObject() вызывает стандартное диалоговое окно класса COlelnsertDialog, предназначенное для вставки объектов.

Файл CNTRITEM.CPP

Файл CNTRITEM.CPP, листинг которого приведен ниже, содержит реализацию класса

CCntCntrltem.

//Cntrltem.cpp: реализация класса CCntCntrltem

#include "stdafx.h" #include "Cnt.h" #include "CntDoc.h" #include "CntView.h" #include "Cntrltem.h" #ifdef _DEBUG #define new DEBUG_NEW tundef THIS_FILE

static char THIS_FILE[] = _ FILE _ ; #endif

//////////////////////////////////////////////////

//CCntCntrltem

IMPLEMENT_SERIAL (CCntCntrltem, COleClientltem, 0)

CCntCntrltem: : CCntCntrltem (CCntDoc* pContainer)

: COleClientltem (pContainer) {

// TODO: здесь добавьте код конструктора.

CCntCntrltera::~CCntCntrItem () {

405

//TODO: здесь добавьте код очистки.

}

void CCntCntrltem::OnChange(OLE_NOTIFICATION nCode, DWORD dwParam) ASSERT_VALID(this); COleClientltem::OnChange(nCode, dwParam);

//Редактируемому объекту посылается уведомление OnChange,

//свидетельствующее об изменении его состояния или внешнего вида.

//TODO: обозначьте рабочую область объекта как недействительную

//путем вызова функции UpdateAllViews.

GetDocument()->UpdateAllViews(NULL);

BOOL CCntCntrltem::OnChangeItemPosition(const CRect SrectPos)

ASSERT_VALID(this);

//В процессе непосредственного редактирования данная функция

//вызывается сервером для изменения позиции окна редактирования.

//По умолчанию вызывается метод базового класса, который,

//в свою очередь, задействует функцию COleClientltem::SetItemRects

//для перемещения объекта в новую позицию.

if (!COleClientltem::OnChangeItemPosition(rectPos)) return FALSE;

//TODO: обновите всю кэшированную информацию,

//связанную с размерами объекта.

return TRUE; )

void CCntCntrltem::OnGetItemPosition(CRect SrPosition) ASSERT_VALID(this);

//В процессе непосредственного редактирования данная функция

//позволяет определить координаты области, занимаемой объектом.

//По умолчанию заданы фиксированные координаты.

//TODO: запишите правильные координаты (в пикселях)

//в переменную rPosition.

rPosition.SetRect(10, 10,210, 210);

}

void CCntCntrltem: :OnActivate() {

// Допускается только один сеанс редактирования в окне приложения. CCntView* pView = GetActiveView ( ) ;

ASSERT_VALID(pView) ;

COleClientltem* pltem = GetDocument ( ) ->

GetlnPlaceActiveltem(pView) ; if (pltem !- NULL ss-pltem != this) p!tem->Close () ;

COleClientltem: : OnAct ivate ( ) ;

}

void CCntCntrltem: :OnDeactivateUI (BOOL bUndoable) { COleClientltem: :OnDeactivateUI (bUndoable) ;

// Скрывает объект, если он бьш . активизирован обычным способом

DWORDdwMisc = 0;

m_lpObject->GetMiscStatus (GetDrawAspect () , SdwMisc) ; if (dwMisc & OLEMISC_INSIDEOUT)

DoVerb (OLEIVERB_HIDE, NULL) ;

}

void CCntCntrltem: : Serialize (CArchive Sar) { ASSERT_VALID(this) ;

//Вызов функции базового класса для считывания данных,

//связанных с объектом COleClientltem. При этом

//инициализируется указатель m_j>Document, возвращаемый

//функцией CCntCntrltem: :GetDocument, поэтому функцию базового класса

//лучше вызывать вначале.

COleClientltem: : Serialize (ar);

406

//Теперь сериализуем данные, специфичные для объекта CCntCntrltem. if (ar.IsStoringO )

{

//TODO: здесь добавьте код сохранения. }

else{

// TODO: здесь добавьте код загрузки.

}

}

///////////////////////////////////////////////

//Диагностика класса CCntCntrltem #if<fef _DEBUG

void CCntCntrltem: :AssertValid () const COleClientltem::&ssertValid0;

}

void CCntCntrltem: :Dump(CDumpContext' &dc) const COleClientltem::Dump(dc);

#endif

Основное назначение этого файла состоит в отслеживании координат и размеров области, занимаемой внедренным объектом. Обратите внимание на фрагмент, выделенный полужирным шрифтом. Именно здесь задаются координаты, о которых говорилось при рассмотрении файла CNTVIEW.CPP.

Проверка работы контейнера

А теперь давайте проверим, как функционирует разработанное нами приложение-контейнер. Вспомните, что в данном случае мы имеем дело лишь с шаблоном приложения. Никакие дополнительные функциональные возможности в него не добавлялись, тем не менее такая программа является вполне работоспособной.

Окно нашей программы показано на рис. 21.8. В нее можно внедрять объекты, созданные в любом другом приложении Windows, поддерживающем технологию OLE. В качестве примера мы внедрим в программу электронную таблицу Excel. Выберите в меню Edit команду InsertNewObject..., в результате чего откроется стандартное диалоговое окно вставки объекта. В этом окне выделите элемент, соответствующий электронной таблице Excel(рис. 21.9)

Рис.21.8. Окно приложения Cnt

407

.

Рис. 21.9. Выбор типа внедряемого объекта

Рис. 21.10. В документ приложения Cnt внедряется электронная таблица Excel

На рис 21.10 показан результат внедрения в документ электронной таблицы Excel со значениями, введенными пользователем.

Еще раз напомним, что все функциональные возможности программы были заданы автоматически. Мы не изменили ни единой строчки в тексте, сгенерированном мастером AppWizard. Итак, вы убедились, что с помощью данного мастера можно легко создавать OLEконтейнеры. Теперь попробуйте самостоятельно создать простое приложение-сервер, а затем внедрите его объект в контейнер Cnt.

Таким образом технология OLE действительно является непростым предметом для изучения, но, безусловно, достойным вашего внимания. Как вы могли убедиться, с помощью мастера

408

приложений и библиотеки MFC совсем не трудно создать простое приложение, поддерживающее технологию OLE. Собственно говоря, существенная помощь в создании OLE-приложений — это основное достоинство мастера AppWizard.

В следующей главе мы поговорим о таких важных средствах создания современных приложений, как элементы управления ActiveX.

409

Глава 22. Основы создания элементов управления ActiveX

Основные концепции

o Критерии разработки элементов управления

o Класс COleControl

oКонтейнеры

Создание простого элемента управления ActiveX с использованием MFC

oСоздание ActiveX-шаблона

oКод, сгенерированный мастером

Модификация шаблона

oИзменение формы, размеров и цвета элемента управления TDCtrl

oРеакция на события мыши

Тестирование элемента управления TDCtrl

Вы уже знакомы с разнообразными элементами управления среды Windows, такими как переключатели, флажки, списки и т.д. Но многие разработчики предпочитают наряду с ними использовать и собственные элементы управления, известные как элементы управления ActiveX. Впервые они появились в языке VisualBasic и изначально носили название VBXэлементов (таковым было расширение соответствующих файлов). По сути, эти элементы управления являлись небольшими библиотеками динамической компоновки (DLL), только имевшими расширение VBX. Возможности современных 32-разрядных элементов управления значительно расширены, а для их файлов теперь применяется расширение OCX.

Многие программисты использовали VisualBasic для разработки собственных элементов управления, а затем включали их в приложения, написанные на C/C++. Стало очевидным, что языки C/C++ неплохо было бы снабдить своим средством разработки элементов управления. Однако в то время, когда компания Microsoft подошла к решению данной проблемы, началась смена поколений операционной системы — переход от 16-разрядной Windows3.1 к 32разрядным Windows95 и NT. Но оказалось, что аппаратно-зависимые 16-разрядные элементы управления VBX не могли служить так же хорошо на новых 32-разрядных платформах. Специалисты Microsoft решили, что целесообразнее будет заняться не расширением спецификации VBX, а разработкой для 32-разрядных платформ принципиально новой архитектуры элементов управления. Для их файлов было выбрано расширение OCX, а сами элементы получили название элементов управления ActiveX.

Хорошей новостью для программистов, работающих с C++, стало включение в среду MicrosoftVisualC++ специального мастера, предназначенного для построения элементов управления ActiveX. Мастер создает код на основе библиотеки MFC . Элемент управления в процессе разработки можно протестировать с помощью утилиты ActiveXControlTestContainer. Готовый элемент можно внедрить в любое приложение, поддерживающее технологию OLE,

скажем в MicrosoftWord или Excel.

Основные концепции

Элементы управления ActiveX часто используются совместно с обычными элементами управления, такими как переключатели, кнопки и флажки; все они могут одновременно содержаться в диалоговом окне. Однако реализация элементов управления ActiveX на порядок сложнее.

Вся ответственность за функционирование элемента управления ложится на его разработчика. При этом необходимо учитывать два аспекта. С одной стороны, на стадии разработки следует написать, отладить и скомпилировать весь код, управляющий выводом элемента управления и реализующий все, его свойства и методы. В результате будет создана небольшая динамическая библиотека с расширением OCX. С другой стороны, приложение, использующее элемент управления ActiveX, должно будет взаимодействовать с ним, вызывая его методы,

410

Соседние файлы в предмете Программирование на C++