In Chapter 24, a simple single-document interface (SDI) application named “Graph” was developed. The container application in this chapter will be patterned closely after that example. We’ll call this application “Cnt.”
This application uses two important OLE classes, COleClientItem and COleDocument. COleDocument manages a list of COleClientItem items. COleClientItem itself manages the embedded or linked objects and the required communications.
The important thing to remember as you view the container code in the next sections is that the code is completely generated by the AppWizard. This container template code can be enhanced with your specific application features to turn it into a full-blown product. In this chapter, however, no additional features were added to the basic template.
Working with the AppWizard
The AppWizard is used here in the same way it was used in Chapter 24. You might want to review that chapter for a more detailed explanation of each step in the creation process. This section will examine the most important steps, in an abbreviated form, for building the container application, Cnt:Chapter 24. You might want to review that chapter for a more detailed explanation of each step in the creation process. This section will examine the most important steps, in an abbreviated form, for building the container application, Cnt:
•
Use the Microsoft Visual C++ File menu to start a new project.
•
Select the MFC AppWizard (.EXE) to start the six-step process:
1.
Create a single-document interface.
2.
Do not include database support.
3.
Select a Container as the OLE compound document. Include support for ActiveX controls.
4.
Include all of the default features for the container application.
5.
Select a MFC Standard project, source code comments, and then pick a statically linked library.
6.
Examine the list of classes to be generated, making any necessary changes. Click on the Finish button to complete the specification process.
•
The AppWizard will generate a summary screen. If this information is correct, click on the OK button to generate the project’s code.
•
The panel, to the left of your display, should now show the various classes and files created for the project.
Finally, build the executable file by selecting the Rebuild All option from the compiler’s Build menu. When the process is complete, the appropriate subdirectory will contain an executable file named CNT.EXE. We will test this container in another section of this chapter.
AppWizard Files
The files generated by the AppWizard produce a fully operable container application named Cnt. When the files have been generated by the AppWizard, your subdirectory will contain the following unique C++ files: CNT.CPP, MAINFRM.CPP, CNTDOC.CPP, CNTVIEW.CPP, and CNTRITEM.CPP. Also in this subdirectory will be a host of supporting header files, resource files, and so on.
The Container File: CNT.CPP
The code used in the container CNT.CPP file is almost identical to that used in the Graph example in Chapter 24. Compare the two files and notice the similarities. You might also want to return to Chapter 24 if you need more details on the message map and the classes used in this file.
The file is listed here, so the application’s code for this chapter will be complete:
// Cnt.cpp : Defines the class behaviors for the application. //
BEGIN_MESSAGE_MAP(CCntApp, CWinApp) //{{AFX_MSG_MAP(CCntApp) ON_COMMAND(ID_APP_ABOUT, OnAppAbout) // NOTE - the ClassWizard will add\remove mapping macros. // DO NOT EDIT these blocks of generated code! //}}AFX_MSG_MAP // Standard file based document commands ON_COMMAND(ID_FILE_NEW, CWinApp::OnFileNew) ON_COMMAND(ID_FILE_OPEN, CWinApp::OnFileOpen) // Standard print setup command ON_COMMAND(ID_FILE_PRINT_SETUP, CWinApp::OnFilePrintSetup) END_MESSAGE_MAP()
////////////////////////////////////////////////////////////// // CCntApp construction
CCntApp::CCntApp() { // TODO: add construction code here, // Place all significant initialization in InitInstance }
////////////////////////////////////////////////////////////// // The one and only CCntApp object
{ // Initialize OLE libraries if (!AfxOleInit()) { AfxMessageBox(IDP_OLE_INIT_FAILED); return FALSE; }
AfxEnableControlContainer();
// Standard initialization // If not using these features you can reduce the size // of the final executable; remove from the following // the specific initialization routines you do not need.
#ifdef _AFXDLL Enable3dControls(); //Call when using MFC in shared DLL #else Enable3dControlsStatic(); //Call when linking statically #endif
// Change the registry key under which settings are stored. // Modify this string to be something appropriate // such as the name of your company or organization. SetRegistryKey(_T(“Local AppWizard-Generated Applications”));
LoadStdProfileSettings(); //Load standard options
// Register the application’s document templates. // Document templates serve as the connection between // documents, frame windows and views.
CSingleDocTemplate* pDocTemplate; pDocTemplate = new CSingleDocTemplate( IDR_MAINFRAME, RUNTIME_CLASS(CCntDoc), RUNTIME_CLASS(CMainFrame), // main SDI frame window RUNTIME_CLASS(CCntView)); pDocTemplate->SetContainerInfo(IDR_CNTR_INPLACE); AddDocTemplate(pDocTemplate);
// Parse command line for standard shell commands CCommandLineInfo cmdInfo; ParseCommandLine(cmdInfo);
// Dispatch commands specified on the command line if (!ProcessShellCommand(cmdInfo)) return FALSE;
// The window has been initialized, show and update. m_pMainWnd->ShowWindow(SW_SHOW); m_pMainWnd->UpdateWindow();
return TRUE; }
////////////////////////////////////////////////////////////// // CAboutDlg dialog used for App About
class CAboutDlg : public CDialog { public: CAboutDlg();
There is an interesting section of code in this file that deserves a mention. Under OLE, in-place editing is supported. In-place editing means that when an object is embedded in a container, such as our Cnt application, its menu replaces the container’s menu. For example, if an Excel spreadsheet object is embedded in Cnt, Cnt’s menu will change to that of Excel’s!
This menu change is handled by MFC, via OLE, almost automatically. MFC makes this possible by having three menu sources available: IDR_MAINFRAME, IDR_DOCTYPE, and IDR_CNTR_INPLACE (the name of the last IDR is specific to your application). When no object is embedded in the container application, IDR_MAINFRAME is used. When a document is opened, IDR_DOCTYPE is used. Finally, when an object has been embedded in the container, IDR_CNTR_INPLACE is used.
The Container File: MAINFRM.CPP
The code used in the container MAINFRM.CPP file is the same as that used in the Graph example in Chapter 24. Again, return to Chapter 24 if you need more details on the message map and classes used in this file.
For completeness, the file is listed here:
// MainFrm.cpp : implementation of the CMainFrame class //
BEGIN_MESSAGE_MAP(CMainFrame, CFrameWnd) //{{AFX_MSG_MAP(CMainFrame) // ClassWizard will add and remove mapping macros here. // DO NOT EDIT these blocks of generated code ! ON_WM_CREATE() //}}AFX_MSG_MAP END_MESSAGE_MAP()
static UINT indicators[] = { ID_SEPARATOR, // status line indicator ID_INDICATOR_CAPS, ID_INDICATOR_NUM,
CMainFrame::CMainFrame() { // TODO: add member initialization code here
}
CMainFrame::~CMainFrame() { }
int CMainFrame::OnCreate(LPCREATESTRUCT lpCreateStruct) { if (CFrameWnd::OnCreate(lpCreateStruct) == -1) return -1;
if (!m_wndToolBar.CreateEx(this, TBSTYLE_FLAT, WS_CHILD | WS_VISIBLE | CBRS_TOP | CBRS_GRIPPER | CBRS_TOOLTIPS | CBRS_FLYBY | CBRS_SIZE_DYNAMIC) || !m_wndToolBar.LoadToolBar(IDR_MAINFRAME)) { TRACE0(“Failed to create toolbar\n”); return -1; // fail to create }
if (!m_wndStatusBar.Create(this) || !m_wndStatusBar.SetIndicators(indicators, sizeof(indicators)/sizeof(UINT))) { TRACE0(“Failed to create status bar\n”); return -1; // fail to create }
// TODO: Delete these lines to if toolbar is not to // be dockable m_wndToolBar.EnableDocking(CBRS_ALIGN_ANY);
BOOL CMainFrame::PreCreateWindow(CREATESTRUCT& cs) { if( !CFrameWnd::PreCreateWindow(cs) ) return FALSE; // TODO: Modify the Window class or styles here by modifying // the CREATESTRUCT cs
The code used in the container CNTDOC.CPP file, shown here, contains some additional code that does not appear in the Graph application in Chapter 24. Compare the two files and notice the differences.Chapter 24. Compare the two files and notice the differences.
// CntDoc.cpp : implementation of the CCntDoc class //
The most significant change in this file comes with the expansion of the message map, as shown in bold type.
The message map will now allow the implementation of the default OLE container. You can also see that the constructor calls the EnableCompoundFile( ) function. This is required for a container application.
The Container File: CNTVIEW.CPP
The container CNTVIEW.CPP file also has some major changes in comparison to the Graph application in Chapter 24. Examine the following file and note the additions to the message map.
// CntView.cpp : implementation of the CCntView class //
// TODO: add draw code for native data here // TODO: also draw all OLE items in the document // Draw the selection at an arbitrary position. // This code should be removed once your real drawing // code is implemented. This position corresponds exactly // to the rectangle returned by CCntCntrItem, // to give the effect of in-place editing. // TODO: remove this code when final draw code is complete. if (m_pSelection == NULL) { POSITION pos = pDoc->GetStartPosition(); m_pSelection = (CCntCntrItem*)pDoc->GetNextClientItem(pos); } if (m_pSelection != NULL) m_pSelection->Draw(pDC, CRect(10, 10, 210, 210)); }
void CCntView::OnInitialUpdate()
{ CView::OnInitialUpdate();
// TODO: remove this code when final code is written m_pSelection = NULL; // initialize selection
void CCntView::OnDestroy() { // Deactivate the item on destruction; this is important // when a splitter view is being used. CView::OnDestroy(); COleClientItem* pActiveItem = GetDocument()-> GetInPlaceActiveItem(this); if (pActiveItem != NULL && pActiveItem-> GetActiveView() == this) {
////////////////////////////////////////////////////////////// // OLE Client support and commands
BOOL CCntView::IsSelected(const CObject* pDocItem) const { // The implementation below is adequate if your selection // consists of only CCntCntrItem objects. To handle // different selection mechanisms, the implementation // here should be replaced.
// TODO: implement this function that tests for a selected // OLE client item
return pDocItem == m_pSelection; }
void CCntView::OnInsertObject() { // Invoke standard Insert Object dialog box for information // for new CCntCntrItem object. COleInsertDialog dlg; if (dlg.DoModal(COleInsertDialog::DocObjectsOnly) != IDOK) return;
BeginWaitCursor();
CCntCntrItem* pItem = NULL; TRY { // Create new item connected to this document. CCntDoc* pDoc = GetDocument(); ASSERT_VALID(pDoc); pItem = new CCntCntrItem(pDoc); ASSERT_VALID(pItem);
// Initialize the item from the dialog data. if (!dlg.CreateItem(pItem)) AfxThrowMemoryException(); // any exception will do ASSERT_VALID(pItem);
// If item created from class list, not from file, launch // the server to edit the item. if (dlg.GetSelectionType() == COleInsertDialog::createNewItem) pItem->DoVerb(OLEIVERB_SHOW, this);
ASSERT_VALID(pItem);
// As an arbitrary interface, this sets the selection // to the last item inserted.
// TODO: reimplement selection as for your application
m_pSelection = pItem; // set to last inserted item pDoc->UpdateAllViews(NULL); } CATCH(CException, e) { if (pItem != NULL) { ASSERT_VALID(pItem); pItem->Delete(); } AfxMessageBox(IDP_FAILED_TO_CREATE); } END_CATCH
EndWaitCursor(); }
// The following command handler provides standard keyboard // user interface to cancel an in-place editing session. // The container (not the server) causes the deactivation. void CCntView::OnCancelEditCntr() {
// Close any in-place active item on this view. COleClientItem* pActiveItem = GetDocument()-> GetInPlaceActiveItem(this); if (pActiveItem != NULL) { pActiveItem->Close(); } ASSERT(GetDocument()->GetInPlaceActiveItem(this) == NULL); }
// Special handling of OnSetFocus and OnSize is required for // a container when an object is being edited in-place. void CCntView::OnSetFocus(CWnd* pOldWnd) { COleClientItem* pActiveItem = GetDocument()-> GetInPlaceActiveItem(this); if (pActiveItem != NULL && pActiveItem->GetItemState() == COleClientItem::activeUIState) { // need to set focus to this item if in the same view CWnd* pWnd = pActiveItem->GetInPlaceWindow(); if (pWnd != NULL) { pWnd->SetFocus(); // don’t call the base class return; } }
CView::OnSetFocus(pOldWnd); }
void CCntView::OnSize(UINT nType, int cx, int cy) { CView::OnSize(nType, cx, cy); COleClientItem* pActiveItem = GetDocument()-> GetInPlaceActiveItem(this); if (pActiveItem != NULL) pActiveItem->SetItemRects(); }
So that it can handle drawing (i.e., inserted objects) for CCntView container, the OnDraw( ) member function has to be altered by the AppWizard. This portion of code was shown in bold type in the file. Here is just a small portion of that code:
if (m_pSelection != NULL) m_pSelection->Draw(pDC, CRect(10, 10, 210, 210)); }
This code, by default, places the object at a prearranged location designated by CRect( ) at 10,10 and 210,210. These values can be changed manually or automatically.
Other additions include OnInitialUpdate( ), IsSelected( ), OnInsertObject( ), OnCancelEdit( ), OnSetFocus( ), and OnSize( ). These signal when an OLE object is selected or otherwise being manipulated. OnInsertObject( ) runs COleInsertDialog. Any additional code for these functions must be supplied by you, the programmer.
The Container File: CNTRITEM.CPP
The container CNTVIEW.CPP file also has some major changes in comparison to the Graph application in Chapter 24. Examine the following file and note the additions to the message map.
// CntrItem.cpp : implementation of the CCntCntrItem class //
// When an item is being edited (in-place or fully open) // it sends OnChange notifications for changes in the state // of the item or visual appearance of its content.
// TODO: invalidate the item by calling UpdateAllViews // (with hints appropriate to your application)
GetDocument()->UpdateAllViews(NULL); // for now just update ALL views/no hints }
// In-place activation CCntCntrItem::OnChangeItemPosition // is called to change the position of the in-place // window. This is a result of the data in the server // document changing when the extent has changed or as a // result of in-place resizing. // // The default is to call the base class, which will call // COleClientItem::SetItemRects to move the item // to the new position.
if (!COleClientItem::OnChangeItemPosition(rectPos)) return FALSE;
// TODO: update any cache of the item’s rectangle/extent
return TRUE; }
void CCntCntrItem::OnGetItemPosition(CRect& rPosition) { ASSERT_VALID(this); // In-place activation, CCntCntrItem::OnGetItemPosition // will determine the location of this item. The default
// implementation simply returns a hard-coded // rectangle. This rectangle would reflect the current // position of the item relative to the view used // for activation. // Obtain the view by calling CCntCntrItem::GetActiveView. // TODO: return correct rectangle (in pixels) in rPosition rPosition.SetRect(10, 10, 210, 210); }
void CCntCntrItem::OnActivate() { // Allow only one inplace activate item per frame CCntView* pView = GetActiveView(); ASSERT_VALID(pView); COleClientItem* pItem = GetDocument()-> GetInPlaceActiveItem(pView); if (pItem != NULL && pItem != this) pItem->Close();
// Hide the object if it is not an outside-in object DWORD dwMisc = 0; m_lpObject->GetMiscStatus(GetDrawAspect(), &dwMisc); if (dwMisc & OLEMISC_INSIDEOUT) DoVerb(OLEIVERB_HIDE, NULL); }
// Call base class to read in COleClientItem data. // This sets up the m_pDocument pointer returned from // CCntCntrItem::GetDocument, it is a good idea to call // the base class Serialize first. COleClientItem::Serialize(ar);
// now store/retrieve data specific to CCntCntrItem if (ar.IsStoring()) { // TODO: add storing code here } else { // TODO: add loading code here } }
The main purpose of this file is to help monitor the position and size of the item in the drawing. Examine the portion of code set in bold type. Have you seen these coordinates before?
In the next section, you’ll see how to use the container application to accept an object from a server.