| Tenouk | Home | Previous | Next | Download  |  Site Index  |  Disclaimer  |  Privacy | Contact | Tell friends |


 

 

 

ActiveX Document Servers and the Internet Part 5

Program examples compiled using Visual C++ 6.0 compiler on Windows XP Pro machine with Service Pack 2 and some Figure screen snapshots have been taken on Windows 2000 server. The Internet Information Services version is IIS 4.x/5.x/6.x on Windows 2000 Server SP 4 and Windows XP Pro SP 2. The Internet Explorer is 6.x. Topics and sub topics for this tutorial are listed below. A complete information about IIS installation, configuration and testing a Web site is dumped HERE and how to setup FTP server also included HERE. Both Web and FTP servers were done on Windows 2000 Server SP4. Don’t forget to read Tenouk’s small disclaimer.

 

 

 

Index:
 
CMyex35bView Class...Continue
The Story
Field Validation in an MFC Form View
Generating POST Requests Under Program Control
The MYEX35B View Class
Building and Testing MYEX35B
ActiveX Document Servers vs. VB Script
Going Further with ActiveX Document Servers

 

 

 

Complete the implementation part for the others as shown in the following Listing.

 

// myex35bView.cpp : implementation of the CMyex35bView class

//

 

#include "stdafx.h"

#include "myex35b.h"

 

#include "myex35bDoc.h"

#include "CntrItem.h"

#include "myex35bView.h"

#include "AddrDialog.h"

#include "PostThread.h"

 

#ifdef _DEBUG

#define new DEBUG_NEW

#undef THIS_FILE

static char THIS_FILE[] = __FILE__;

#endif

 

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

// CMyex35bView

 

IMPLEMENT_DYNCREATE(CMyex35bView, CValidForm)

 

BEGIN_MESSAGE_MAP(CMyex35bView, CValidForm)

       //{{AFX_MSG_MAP(CMyex35bView)

       ON_WM_SETFOCUS()

       ON_WM_SIZE()

       ON_COMMAND(ID_OLE_INSERT_NEW, OnInsertObject)

       ON_COMMAND(ID_CANCEL_EDIT_CNTR, OnCancelEditCntr)

       ON_COMMAND(ID_CANCEL_EDIT_SRVR, OnCancelEditSrvr)

       ON_BN_CLICKED(IDC_SUBMIT, OnSubmit)

       ON_BN_CLICKED(IDCANCEL, OnCancel)

       ON_COMMAND(ID_SERVER_ADDRESS, OnServerAddress)

       //}}AFX_MSG_MAP

       // Standard printing commands

       ON_COMMAND(ID_FILE_PRINT, CValidForm::OnFilePrint)

       ON_COMMAND(ID_FILE_PRINT_DIRECT, CValidForm::OnFilePrint)

       ON_COMMAND(ID_FILE_PRINT_PREVIEW, CValidForm::OnFilePrintPreview)

       ON_MESSAGE(WM_POSTCOMPLETE, OnPostComplete)

END_MESSAGE_MAP()

 

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

// CMyex35bView construction/destruction

 

CMyex35bView::CMyex35bView()

       : CValidForm(CMyex35bView::IDD)

{

       //{{AFX_DATA_INIT(CMyex35bView)

       m_dHours = 0.0;

       m_strEmployee = _T("");

       m_nJob = 0;

       m_nPeriod = 0;

       //}}AFX_DATA_INIT

       m_pSelection = NULL;

       // TODO: add construction code here

 

}

 

CMyex35bView::~CMyex35bView()

{

}

 

void CMyex35bView::DoDataExchange(CDataExchange* pDX)

{

       CValidForm::DoDataExchange(pDX);

       DDX_CBString(pDX, IDC_EMPLOYEE, m_strEmployee);

       DDV_MaxChars(pDX, m_strEmployee, 10);

       if(pDX->m_bSaveAndValidate && m_strEmployee.IsEmpty()) {

              AfxMessageBox("Must supply an employee name");

              pDX->Fail();

       }

       //{{AFX_DATA_MAP(CMyex35bView)

       DDX_Text(pDX, IDC_HOURS, m_dHours);

       DDV_MinMaxDouble(pDX, m_dHours, 0.1, 100.);

       DDX_Text(pDX, IDC_JOB, m_nJob);

       DDV_MinMaxInt(pDX, m_nJob, 1, 20);

       DDX_Text(pDX, IDC_PERIOD, m_nPeriod);

       //}}AFX_DATA_MAP

}

 

BOOL CMyex35bView::PreCreateWindow(CREATESTRUCT& cs)

{

       // TODO: Modify the Window class or styles here by modifying

       //  the CREATESTRUCT cs

 

       return CValidForm::PreCreateWindow(cs);

}

 

void CMyex35bView::OnInitialUpdate()

{

       CMyex35bDoc* pDoc = GetDocument();

       m_nPeriod = pDoc->m_nPeriod;

       UpdateData(FALSE);

 

       // TODO: remove this code when final selection model code is written

       m_pSelection = NULL;    // initialize selection

 

}

 

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

// CMyex35bView printing

 

BOOL CMyex35bView::OnPreparePrinting(CPrintInfo* pInfo)

{

       // default preparation

       return DoPreparePrinting(pInfo);

}

 

void CMyex35bView::OnBeginPrinting(CDC* /*pDC*/, CPrintInfo* /*pInfo*/)

{

       // TODO: add extra initialization before printing

}

 

void CMyex35bView::OnEndPrinting(CDC* /*pDC*/, CPrintInfo* /*pInfo*/)

{

       // TODO: add cleanup after printing

}

 

void CMyex35bView::OnPrint(CDC* pDC, CPrintInfo*)

{

       // TODO: add code to print the controls

}

 

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

// OLE Client support and commands

 

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

{

       // The implementation below is adequate if your selection consists of

       //  only CMyex35bCntrItem 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 CMyex35bView::OnInsertObject()

{

       // Invoke the standard Insert Object dialog box to obtain information

       //  for new CMyex35bCntrItem object.

       COleInsertDialog dlg;

       if (dlg.DoModal() != IDOK)

              return;

 

       BeginWaitCursor();

 

       CMyex35bCntrItem* pItem = NULL;

       TRY

       {

              // Create new item connected to this document.

              CMyex35bDoc* pDoc = GetDocument();

              ASSERT_VALID(pDoc);

              pItem = new CMyex35bCntrItem(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) then launch

              //  the server to edit the item.

              if (dlg.GetSelectionType() == COleInsertDialog::createNewItem)

                     pItem->DoVerb(OLEIVERB_SHOW, this);

 

              ASSERT_VALID(pItem);

 

              // As an arbitrary user interface design, this sets the selection

              //  to the last item inserted.

 

              // TODO: re-implement selection as appropriate for your application

 

              m_pSelection = pItem;   // set selection 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 the standard keyboard

//  user interface to cancel an in-place editing session.  Here,

//  the container (not the server) causes the deactivation.

void CMyex35bView::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 are required for a container

//  when an object is being edited in-place.

void CMyex35bView::OnSetFocus(CWnd* pOldWnd)

{

       COleClientItem* pActiveItem = GetDocument()->GetInPlaceActiveItem(this);

       if (pActiveItem != NULL &&

              pActiveItem->GetItemState() == COleClientItem::activeUIState)

       {

              // need to set focus to this item if it is in the same view

              CWnd* pWnd = pActiveItem->GetInPlaceWindow();

              if (pWnd != NULL)

              {

                     pWnd->SetFocus();   // don't call the base class

                     return;

              }

       }

 

       CValidForm::OnSetFocus(pOldWnd);

}

 

void CMyex35bView::OnSize(UINT nType, int cx, int cy)

{

       CValidForm::OnSize(nType, cx, cy);

       COleClientItem* pActiveItem = GetDocument()->GetInPlaceActiveItem(this);

       if (pActiveItem != NULL)

              pActiveItem->SetItemRects();

}

 

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

// OLE Server support

 

// The following command handler provides the standard keyboard

//  user interface to cancel an in-place editing session.  Here,

//  the server (not the container) causes the deactivation.

void CMyex35bView::OnCancelEditSrvr()

{

       GetDocument()->OnDeactivateUI(FALSE);

}

 

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

// CMyex35bView diagnostics

 

#ifdef _DEBUG

void CMyex35bView::AssertValid() const

{

       CValidForm::AssertValid();

}

 

void CMyex35bView::Dump(CDumpContext& dc) const

{

       CValidForm::Dump(dc);

}

 

CMyex35bDoc* CMyex35bView::GetDocument() // non-debug version is inline

{

       ASSERT(m_pDocument->IsKindOf(RUNTIME_CLASS(CMyex35bDoc)));

       return (CMyex35bDoc*)m_pDocument;

}

#endif //_DEBUG

 

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

// CMyex35bView message handlers

 

void CMyex35bView::OnCancel()

{

       CMyex35bDoc* pDoc = GetDocument();

       m_dHours = 0;

       m_strEmployee = "";

       m_nJob = 0;

       m_nPeriod = pDoc->m_nPeriod;

       UpdateData(FALSE);

}

 

void CMyex35bView::OnSubmit()

{

       if(UpdateData(TRUE) == TRUE) {

              GetDlgItem(IDC_SUBMIT)->EnableWindow(FALSE);

              CString strHours, strJob, strPeriod;

              strPeriod.Format("%d", m_nPeriod);

              strHours.Format("%3.2f", m_dHours);

              strJob.Format("%d", m_nJob);

              g_strParameters = "Period=" + strPeriod + "&Employee=" +

                          m_strEmployee + "&Hours=" + strHours +

                          "&Job=" + strJob + "\r\n";

              TRACE("parameter string = %s", g_strParameters);

              AfxBeginThread(PostThreadProc, GetSafeHwnd(), THREAD_PRIORITY_NORMAL);

       }

}

 

// overridden CValidForm function

void CMyex35bView::ValidateDlgItem(CDataExchange* pDX, UINT uID)

{

       ASSERT(this);

       TRACE("CMyex35bView::ValidateDlgItem\n");

       switch (uID) {

       case IDC_EMPLOYEE:

              DDX_CBString(pDX, IDC_EMPLOYEE, m_strEmployee);

              // need custom DDV for empty string

              DDV_MaxChars(pDX, m_strEmployee, 10);

              if(m_strEmployee.IsEmpty()) {

                     AfxMessageBox("Must supply an employee name");

                     pDX->Fail();

              }

              break;

       case IDC_HOURS:

              DDX_Text(pDX, IDC_HOURS, m_dHours);

              DDV_MinMaxDouble(pDX, m_dHours, 0.1, 100.);

              break;

       case IDC_JOB:

              DDX_Text(pDX, IDC_JOB, m_nJob);

              DDV_MinMaxInt(pDX, m_nJob, 1, 20);

              break;

       default:

              break;

       }

       return;

}

 

void CMyex35bView::OnServerAddress()

{

       CAddrDialog dlg;

       dlg.m_strServerName = g_strServerName;

       dlg.m_strFile = g_strFile;

       if(dlg.DoModal() == IDOK) {

              g_strFile = dlg.m_strFile;

              g_strServerName = dlg.m_strServerName;

       }

}

 

// thread sends this message when it's done wParam = 1 for success

LONG CMyex35bView::OnPostComplete(UINT wParam, LONG lParam)

{

       TRACE("CMyex35bView::OnPostComplete - %d\n", wParam);

       if(wParam == 0) {

              AfxMessageBox("Server did not accept the transaction");

       }

       else OnCancel();

       GetDlgItem(IDC_SUBMIT)->EnableWindow(TRUE);

       return 0;

}

 

Listing 30.

 

Build and run MYEX35B. The following is the output. Activate the Server Address menu.

 

Figure 87: MYEX35B program output - ActiveX document and Internet.

 

Figure 87: MYEX35B program output.

 

Figure 88: The Address sub menu displaying the server address and DLL file location settings. You can change this - ActiveX document and Internet.

 

Figure 88: The Address sub menu displaying the server address and DLL file location settings. You can change this.

 

Figure 89: The validation message box for the Name field - ActiveX document and Internet.

 

Figure 89: The validation message box for the Name field.

 

Figure 90: The validation message box for the Hours field - ActiveX document and Internet.

 

Figure 90: The validation message box for the Hours field.

 

Enter some data a shown below and click the Submit button.

 

Figure 91: Entering some data to myex35b form - ActiveX document and Internet.

 

Figure 91: Entering some data to myex35b form.

 

Figure 92: Saving the form - ActiveX document and Internet.

 

Figure 92: Saving the form.

 

Figure 93: Saving the form, test.35b under wwwroot sub directory - ActiveX document and Internet.

 

Figure 93: Saving the form, test.35b under wwwroot sub directory.

 

Figure 94: Opening test.35b in internet Explorer browser - ActiveX document and Internet.

 

Figure 94: Opening test.35b in internet Explorer browser.

 

Figure 95: Testing the validation in the browser - ActiveX document and Internet.

 

Figure 95: Testing the validation in the browser.

 

The Story

Field Validation in an MFC Form View

 

Problem: MFC's standard validation scheme validates data only when CWnd::UpdateData(TRUE) is called, usually when the user exits the dialog. Applications often need to validate data the moment the user leaves a field (edit control, list box, and so on). The problem is complex because Windows permits the user to freely jump between fields in any sequence by using the mouse. Ideally, standard MFC DDX/DDV (data exchange/validation) code should be used for field validation and the standard DoDataExchange() function should be called when the user finishes the transaction.

Solution: Derive your field validation form view classes from the class CValidForm, derived from CFormView, with this header:

 

// valform.h

#ifndef _VALIDFORM

#define _VALIDFORM

 

#define WM_VALIDATE WM_USER + 5

 

class CValidForm : public CFormView

{

DECLARE_DYNAMIC(CValidForm)

private:

    BOOL m_bValidationOn;

public:

    CValidForm(UINT ID);

    // override in derived dlg to perform validation

    virtual void ValidateDlgItem(CDataExchange* pDX, UINT ID);

    //{{AFX_VIRTUAL(CValidForm)

    protected:

    virtual BOOL OnCommand(WPARAM wParam, LPARAM lParam);

    //}}AFX_VIRTUAL

 

    //{{AFX_MSG(CValidForm)

    afx_msg LONG OnValidate(UINT wParam, LONG lParam);

    //}}AFX_MSG

    DECLARE_MESSAGE_MAP()

};

#endif // _VALIDFORM

 

This class has one virtual function, ValidateDlgItem(), which accepts the control ID as the second parameter. The derived form view class implements this function to call the DDX/DDV functions for the appropriate field. Here is a sample ValidateDlgItem() implementation for a form view that has two numeric edit controls:

 

void CMyForm::ValidateDlgItem(CDataExchange* pDX, UINT uID)

{

    switch (uID) {

    case IDC_EDIT1:

        DDX_Text(pDX, IDC_EDIT1, m_nEdit1);

        DDV_MinMaxInt(pDX, m_nEdit1, 0, 10);

        break;

    case IDC_EDIT2:

        DDX_Text(pDX, IDC_EDIT2, m_nEdit2);

        DDV_MinMaxInt(pDX, m_nEdit2, 20, 30);

        break;

    default:

        break;

    }

}

 

Notice the similarity to the wizard-generated DoDataExchange() function:

 

void CAboutDlg::DoDataExchange(CDataExchange* pDX)

{

    //{{AFX_DATA_MAP(CMyForm)

    DDX_Text(pDX, IDC_EDIT1, m_nEdit1);

    DDV_MinMaxInt(pDX, m_nEdit1, 0, 10);

    DDX_Text(pDX, IDC_EDIT2, m_nEdit2);

    DDV_MinMaxInt(pDX, m_nEdit2, 20, 30);

    //}}AFX_DATA_MAP

}

How does it work? The CValidForm class traps the user's attempt to move away from a control. When the user presses the Tab key or clicks on another control, the original control sends a killfocus command message (a control notification message) to the parent window, the exact format depending on the kind of control. An edit control, for example, sends an EN_KILLFOCUS command. When the form window receives this killfocus message, it invokes the DDX/DDV code that is necessary for that field, and if there's an error, the focus is set back to that field.

There are some complications, however. First, we want to allow the user to freely switch the focus to another application - we're not interested in trapping the killfocus message in that case. Next, we must be careful how we set the focus back to the control that produced the error. We can't just call SetFocus() in direct response to the killfocus message; instead we must allow the killfocus process to complete. We can achieve this by posting a user-defined WM_VALIDATE message back to the form window. The WM_VALIDATE handler calls our ValidateDlgItem() virtual function after the focus has been moved to the next field. Also, we must ignore the killfocus message that results when we switch back from the control that the user tried to select, and we must allow the IDCANCEL button to abort the transaction without validation.

Most of the work here is done in the view's virtual OnCommand() handler, which is called for all control notification messages. We could, of course, individually map each control's killfocus message in our derived form view class, but that would be too much work. Here is the OnCommand() handler:

 

BOOL CValidForm::OnCommand(WPARAM wParam, LPARAM lParam)

{

    // specific for WIN32 — wParam/lParam processing different for

    //  WIN16

    TRACE("CValidForm::OnCommand, wParam = %x, lParam = %x\n",

        wParam, lParam);

    TRACE("m_bValidationOn = %d\n", m_bValidationOn);

    if(m_bValidationOn) {   // might be a killfocus

        UINT notificationCode = (UINT) HIWORD(wParam);

        if((notificationCode == EN_KILLFOCUS)  ||

           (notificationCode == LBN_KILLFOCUS) ||

           (notificationCode == CBN_KILLFOCUS)) {

            CWnd* pFocus = CWnd::GetFocus(); // static function call

            <