Microsoft KB Archive/175503

From BetaArchive Wiki
Knowledge Base


How To Write a Dialog-based ActiveX Control Using ATL

Article ID: 175503

Article Last Modified on 7/15/2004



APPLIES TO

  • Microsoft ActiveX Template Library 2.0
  • Microsoft ActiveX Template Library 2.1
  • Microsoft ActiveX Template Library 3.0



This article was previously published under Q175503

SUMMARY

When writing ActiveX controls with the Active Template Library (ATL), it may be useful to create your control based on a dialog box template. This allows you to take advantage of the Visual C++ resource editor to lay out contained controls or to reuse existing MFC dialog box templates and Visual Basic forms.

You add an ActiveX control to your ATL project by clicking New ATL Object on the Insert menu, and then clicking either Full Control or Internet Explorer Control. On completion, the ATL Object Wizard generates code that includes a class derived from CComControl, which further derives from CWindowImpl.

This article demonstrates how to derive your control class from CDialogImpl and associate a dialog box template with this class. This article also demonstrates providing support for UI activation and navigation keys.

MORE INFORMATION

  1. Declare your own class, based on CComControl, which is derived from CDialogImpl instead of CWindowImpl. The original definition of CComControl can be found in ATLCTL.H. For example:

          template <class T>
          class ATL_NO_VTABLE CComDlgCtrl : public CComControlBase,
             public CDialogImpl<T>
          {
          public:
             CComDlgCtrl() : CComControlBase(m_hWnd) {}
             HRESULT FireOnRequestEdit(DISPID dispID)
             {
                T* pT = static_cast<T*>(this);
                return T::__ATL_PROP_NOTIFY_EVENT_CLASS::FireOnRequestEdit
                   (pT->GetUnknown(), dispID);
             }
             HRESULT FireOnChanged(DISPID dispID)
             {
                T* pT = static_cast<T*>(this);
                return T::__ATL_PROP_NOTIFY_EVENT_CLASS::FireOnChanged
                   (pT->GetUnknown(), dispID);
             }
             virtual HRESULT ControlQueryInterface(const IID& iid, void** ppv)
             {
                T* pT = static_cast<T*>(this);
                return pT->_InternalQueryInterface(iid, ppv);
             }
             virtual HWND CreateControlWindow(HWND hWndParent, RECT& rcPos)
             {
                T* pT = static_cast<T*>(this);
                return pT->Create(hWndParent);
                // CDialogImpl::Create differs from CWindowImpl
             }
          };
                            
  2. Derive your control from this new class, instead of CComControl. For example:

          class ATL_NO_VTABLE CMyDlgCtrl :
          ...
             public CComDlgCtrl<CMyDlgCtrl>, // Replaced CComControl
          ...
                            
  3. Create your dialog template and make sure it is marked as a "Child" template. This will set up correctly if you use the IDD_FORMVIEW dialog subtype when creating the dialog box by clicking Resource on the Insert menu.

    Update class definition to identify this resource. For example:

          class ATL_NO_VTABLE CMyDlgCtrl :
          ...
          {
          public:
             enum { IDD = IDD_MYDIALOG };
          ...
                            
  4. Declare and implement a message handler for WM_INITDIALOG and any required message, command, and notify handlers. For example:

          BEGIN_MSG_MAP(CMyDlgCtrl)
             MESSAGE_HANDLER(WM_INITDIALOG, OnInitDialog)
             COMMAND_ID_HANDLER(IDC_SOMECOMMAND, OnSomeCommand)
    
           // The next 3 lines are only for Visual C++ 6.0, and should
           // replace the CHAIN_MSG_MAP() entry.
             MESSAGE_HANDLER(WM_SETFOCUS, CComControlBase::OnSetFocus)
             MESSAGE_HANDLER(WM_KILLFOCUS, CComControlBase::OnKillFocus)
             MESSAGE_HANDLER(WM_MOUSEACTIVATE,
                CComControlBase::OnMouseActivate)
          END_MSG_MAP()
    
          ...
          LRESULT OnInitDialog(UINT uMsg,WPARAM wParam,LPARAM lParam,
             BOOL&bHandled)
          {
             InPlaceActivate( OLEIVERB_UIACTIVATE );
             // Perform any dialog initialization
             return FALSE;
          }
          LRESULT OnSomeCommand(WORD wNotifyCode,WORD wID,HWND hWndCtl,
             BOOL&bHandled)
          {
             // Perform operation for this command.
             return 0;
          }
                            
  5. Remove the OnDraw declaration and implementation.
  6. If you want to handle UI activation of your control correctly, add a WM_MOUSEACTIVATE message handler to call CComControl::InPlaceActivate. For example:

          BEGIN_MSG_MAP(CMyDlgCtrl)
             ...
             MESSAGE_HANDLER(WM_MOUSEACTIVATE, OnMouseActivate)
          END_MSG_MAP()
          ...
          LRESULT OnMouseActivate(UINT uMsg,WPARAM wParam,LPARAM lParam,
             BOOL&bHandled)
          {
             // UI-Activate control
             InPlaceActivate( OLEIVERB_UIACTIVATE );
             return FALSE;
          }
                            

    This step is required only for Visual C++ 5.0 and earlier. The MESSAGE_HANDLER entry in step 4 above for WM_MOUSEACTIVATE calls CComControlBase::OnMouseActivate(), which UI activates your control.

  7. If you want to handle tabbing and other navigation keys correctly, override IOleInPlaceActiveObjectImpl::TranslateAccelerator. For example:

          STDMETHOD(TranslateAccelerator)(MSG *pMsg)
          {
             if ( ( pMsg->message < WM_KEYFIRST
                 || pMsg->message > WM_KEYLAST )
               && ( pMsg->message < WM_MOUSEFIRST
                 || pMsg->message > WM_MOUSELAST ) )
                return S_FALSE;
             return ( IsDialogMessage( m_hWnd, pMsg ) ) ? S_OK : S_FALSE;
          }
                            
  8. Depending on how you created the control, you need to set the m_bWindowOnly variable to 1 in your control's constructor to force the control to be non-Windowless. For example:

          CMyDlgCtrl()
          {
             m_bWindowOnly = 1;
          }
                            

(c) Microsoft Corporation 1997, All Rights Reserved. Contributions by Mark Davis, Microsoft Corporation.

Keywords: kbhowto kbctrlcreate kbdlg KB175503