The EX28A Example -- An MFC In-Place_Activated Mini-Server

You don't need much OLE theory to build an MFC mini-server. This example is a good place to start, though, because you'll get an idea of how containers and components interact. This component isn't too sophisticated. It simply draws some text and graphics in a window. The text is stored in the document, and there's a dialog for updating it.

Here are the steps for creating the program from scratch:

  1. Run AppWizard to create the EX28A project in the \vcpp32\ex28a directory. Select Single Document interface. Click the Mini-Server option in the AppWizard Step 3 dialog shown here.

    click to view at full size.

  2. Examine the generated files. You've got the familiar application, document, main frame, and view files, but you've got two new files too.

    Header Implementation Class MFC Base Class
    SrvrItem.h SrvrItem.cpp CEx28aSrvrItem COleServerItem
    IpFrame.h IpFrame.cpp CInPlaceFrame COleIPFrameWnd

  3. Add a text member to the document class. Add the following public data member in the class declaration in ex28aDoc.h:

     CString m_strText; 

    Set the string's initial value to Initial default text in the document's OnNewDocument member function.

  4. Add a dialog to modify the text. Insert a new dialog template with an edit control, and then use ClassWizard to generate a CTextDialog class derived from CDialog. Don't forget to include the dialog class header in ex28aDoc.cpp. Also, use ClassWizard to add a CString member variable named m_strText for the edit control.

  5. Add a new menu command in both the embedded and in-place menus. Add a Modify menu command in both the IDR_SRVR_EMBEDDED and IDR_SRVR_INPLACE menus. To insert this menu command on the IDR_SRVR_EMBEDDED menu, use the resource editor to add an EX28A-EMBED menu item on the top level, and then add a Modify option on the submenu for this item. Next add an EX28A-INPLACE menu item on the top level of the IDR_SRVR_INPLACE menu and add a Modify option on the EX28A-INPLACE submenu.

    To associate both Modify options with one OnModify function, use ID_MODIFY as the ID for the Modify option of both the IDR_SRVR_EMBEDDED and IDR_SRVR_INPLACE menus. Then use ClassWizard to map both Modify options to the OnModify function in the document class. Code the Modify command handler as shown here:

     void CEx28aDoc::OnModify()  {     CTextDialog dlg;     dlg.m_strText = m_strText;     if (dlg.DoModal() == IDOK) {         m_strText = dlg.m_strText;         UpdateAllViews(NULL); // Trigger CEx28aView::OnDraw         UpdateAllItems(NULL); // Trigger CEx28aSrvrItem::OnDraw         SetModifiedFlag();     } } 

  6. Override the view's OnPrepareDC function. Use ClassWizard to generate the function, and then replace any existing code with the following line:

     pDC->SetMapMode(MM_HIMETRIC); 

  7. Edit the view's OnDraw function. The following code in ex28aView.cpp draws a 2-cm circle centered in the client rectangle, with the text wordwrapped in the window:

     void CEx28aView::OnDraw(CDC* pDC) {     CEx28aDoc* pDoc = GetDocument();     ASSERT_VALID(pDoc);     CFont font;     font.CreateFont(-500, 0, 0, 0, 400, FALSE, FALSE, 0,                     ANSI_CHARSET, OUT_DEFAULT_PRECIS,                     CLIP_DEFAULT_PRECIS, DEFAULT_QUALITY,                     DEFAULT_PITCH | FF_SWISS, "Arial");     CFont* pFont = pDC->SelectObject(&font);     CRect rectClient;     GetClientRect(rectClient);     CSize sizeClient = rectClient.Size();     pDC->DPtoHIMETRIC(&sizeClient);     CRect rectEllipse(sizeClient.cx / 2 - 1000,                      -sizeClient.cy / 2 + 1000,                       sizeClient.cx / 2 + 1000,                      -sizeClient.cy / 2 - 1000);     pDC->Ellipse(rectEllipse);     pDC->TextOut(0, 0, pDoc->m_strText);     pDC->SelectObject(pFont); } 

  8. Edit the server item's OnDraw function. The following code in the SrvrItem.cpp file tries to draw the same circle drawn in the view's OnDraw function. You'll learn what a server item is shortly.

     BOOL CEx28aSrvrItem::OnDraw(CDC* pDC, CSize& rSize) {     // Remove this if you use rSize     UNREFERENCED_PARAMETER(rSize);     CEx28aDoc* pDoc = GetDocument();     ASSERT_VALID(pDoc);     // TODO: set mapping mode and extent     //  (The extent is usually the same as the size returned from      //  OnGetExtent)     pDC->SetMapMode(MM_ANISOTROPIC);     pDC->SetWindowOrg(0,0);     pDC->SetWindowExt(3000, -3000);     CFont font;     font.CreateFont(-500, 0, 0, 0, 400, FALSE, FALSE, 0,                     ANSI_CHARSET, OUT_DEFAULT_PRECIS,                     CLIP_DEFAULT_PRECIS, DEFAULT_QUALITY,                     DEFAULT_PITCH | FF_SWISS, "Arial");     CFont* pFont = pDC->SelectObject(&font);     CRect rectEllipse(CRect(500, -500, 2500, -2500));     pDC->Ellipse(rectEllipse);     pDC->TextOut(0, 0, pDoc->m_strText);     pDC->SelectObject(pFont);     return TRUE; } 

  9. Edit the document's Serialize function. The framework takes care of loading and saving the document's data from and to an OLE stream named Contents that is attached to the object's main storage. You simply write normal serialization code, as shown here:

     void CEx28aDoc::Serialize(CArchive& ar) {     if (ar.IsStoring())     {         ar << m_strText;     }     else     {         ar >> m_strText;     } } 

    There is also a CEx28aSrvrItem::Serialize function that delegates to the document Serialize function.

  10. Build and register the EX28A application. You must run the application directly once to update the Registry.

  11. Test the EX28A application. You need a container program that supports in-place activation. Use Microsoft Excel 97 or a later version if you have it, or build the project in the MFC DRAWCLI sample. Choose the container's Insert Object menu item. If this option does not appear on the Insert menu, it might appear on the Edit menu instead. Then select Ex28a Document from the list.

You debug an embedded component the same way you debug an Automation EXE component. See the sidebar, "Debugging an EXE Component Program", for more information.

When you first insert the EX28A object, you'll see a hatched border, which indicates that the object is in-place active. The bounding rectangle is 3-by-3-cm square, with a 2-cm circle in the center, as illustrated here.

If you click elsewhere in the container's window, the object becomes inactive, and it's shown like this.

In the first case, you saw the output of the view's OnDraw function; in the second case, you saw the output of the server item's OnDraw function. The circles are the same, but the text is formatted differently because the server (component) item code is drawing on a metafile device context.

If you use the resize handles to extend the height of the object (click once on the object to see the resize handles; don't double-click), you'll stretch the circle and the font will get bigger, as shown below in the figure on the left. If you reactivate the object by double-clicking on it, it's reformatted as shown in the figure on the right.

Click elsewhere in the container's window, single-click on the object, and then choose Ex28a Object from the bottom of the Edit menu. Choose Open from the submenu. This starts the component program in embedded mode rather than in in-place mode, as shown here.

click to view at full size.

Notice that the component's IDR_SRVR_EMBEDDED menu is visible.

An MDI Embedded Component?

The EX28A example is an SDI mini-server. Each time a controller creates an EX28A object, a new EX28A process is started. You might expect an MDI mini-server process to support multiple component objects, each with its own document, but this is not the case. When you ask AppWizard to generate an MDI mini-server, it generates an SDI program, as in EX28A. It's theoretically possible to have a single process support multiple embedded objects in different windows, but you can't easily create such a program with the MFC library.

In-Place Component Sizing Strategy

If you look at the EX28A output, you'll observe that the metafile image does not always match the image in the in-place frame window. We had hoped to create another example in which the two images matched. We were unsuccessful, however, when we tried to use the Microsoft Office 97 applications as containers. Each one did something a little different and unpredictable. A complicating factor is the containers' different zooming abilities.

When AppWizard generates a component program, it gives you an overridden OnGetExtent function in your server item class. This function returns a hard-coded size of (3000, 3000). You can certainly change this value to suit your needs, but be careful if you change it dynamically. We tried maintaining our own document data member for the component's extent, but that messed us up when the container's zoom factor changed. We thought containers would make more use of another component item virtual function, OnSetExtent, but they don't.

You'll be safest if you simply make your component extents fixed and assume that the container will do the right thing. Keep in mind that when the container application prints its document, it prints the component metafiles. The metafiles are more important than the in-place views.

If you control both container and component programs, however, you have more flexibility. You can build up a modular document processing system with its own sizing protocol. You can even use other OLE interfaces.



Programming Microsoft Visual C++
Programming Microsoft Visual C++
ISBN: 1572318570
EAN: 2147483647
Year: 1997
Pages: 332

flylib.com © 2008-2017.
If you may any questions please contact us: flylib@qtcs.net