Паппас К., Мюррей У. - Visual C++ 6. Руководство разработчика - 2000
.pdf* // включается поддержка составных файлов 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
- #13.08.201334.52 Mб91Липман С., Лажойе Ж., Му Б. - Язык программирования C++. Вводный курс - 2007.djvu
- #13.08.201347.18 Mб128Литвиненко Н.А. - Технология программирования на C++. Win32 API-приложения - 2010.djvu
- #
- #
- #13.08.201317.53 Mб104Оберг Р., Торстейсон П. - Архитектура .NET и программирование на Visual C++ - 2002.pdf
- #
- #
- #
- #
- #
- #