<CDialogBar 를 CDialog 처럼 사용하기>

 

CCalBar class의 .h/.cpp 안에 주석을 달았습니다.

----------------------------------------------------------------------------------
// calbar.h
 
#if !defined(AFX_CALBAR_H__1E82E2C9_D3C4_11D1_A4FF_00AA00C0AFD5__INCLUDED_)
#define AFX_CALBAR_H__1E82E2C9_D3C4_11D1_A4FF_00AA00C0AFD5__INCLUDED_

#if _MSC_VER >= 1000
#pragma once
#endif // _MSC_VER >= 1000
// CalBar.h : header file
//

/* CDialogBar 를 CDialog 처럼 사용하기
 
1.    리소스 에디트로 다이얼로그 템플릿을 만든다.
    toolbar처럼 보이게 만든다(title을 없애고, thin으로, ...)
    style을 popup에서 child로 바꾼다.
 
2.    new class로 새로운 클래스를 만든다.
    wizard가 CDialogBar를 지원하지 않으므로 base class를 generic CWnd로 선택한다.
    생성후 .h/.cpp에 있는 CWnd를 CDialogBar로 모두 대체한다.
    (public CWnd -> public CDialogBar, BEGIN_MESSAGE_MAP(..., CWnd -> CDialogBar))
 
3.    class wizard가 사용하는 .clw 파일을 편집한다.
    .clw파일을 열어 생성한 클래스의 이름 항목에
    BaseClass=CWnd -> CDialogBar로 변경하고,
    Filter=W 를 Filter=D롤 바꾼다.
    그럼 class wizird로 CDialog 함수를 추가할수 있다.
 
4.    DDX/DDV를 사용하기 위해
     메세지 처리함수 afx_msg LRESULT HandleInitDialog(WPARAM, LPARAM)를 추가하고
    메세지 맵에서 WM_INITDIALOG와 매핑시킨다. - ON_MESSAGE(WM_INITDIALOG, HandleInitDialog)
    HandleInitDialog 안에서 OninitDialog()를 호출하고,
    base인 CDialogBar::HandleInitDialog(wParam, lParam)를 호출한다.
    DoDataExchange(...)를 오버로딩한다.
    OnInitDialog()를 오버로딩하고 안에서 컨트로 초기작업과 UpdateData(FALSE)를
    호출한다.
 
5.    //{{AFX_DATA(...)        //}}AFX_DATA        .h
    //{{AFX_DATA_INIT(...)    //}}AFX_DATA_INIT    .cpp - ctor
    //{{AFX_DATA_MAP(...)    //}}AFX_DATA_MAP    .cpp - DoDataExchage
    를 추가하면 class wizard 로 member vairlabe을 추가할수 있다.
 
6.  CMainFrame::OnCreate() 안에서 toolbar 처러 생성한다.
 
7.    끝으로 위의 기능은 CDialog의 많은 부분이 CWnd 안에 구현되어 있기 때문에
    가능한것이다. 이렇게 만들면 직접코딩하는 번거러움을 많이 줄일수 있다.
    툴바에 버튼외에 다른 컨트롤을 삽입하는것은 resourc edit의 도움을 받을수 없음므로
    직접 코딩해야 되고, 버튼이 하나라도 있어야 제대로 출력된다.(크기 문제)
*/
 
#include "yearedit.h"
 
/////////////////////////////////////////////////////////////////////////////
// CCalBar window
 
class CCalBar : public CDialogBar
{
// Construction
public:
    CCalBar();
 
// Attributes
public:
    //{{AFX_DATA(CCalBar)
    enum { IDD = IDD_CALBAR };
    int m_nMonth;
    int m_nYear;
    CYearEdit    m_edit;
    CComboBox    m_cbox;
    CSpinButtonCtrl    m_spin;
    //}}AFX_DATA
 
// Operations
public:
 
// Overrides
    // ClassWizard generated virtual function overrides
    //{{AFX_VIRTUAL(CCalBar)
    //}}AFX_VIRTUAL
 
// Implementation
protected:
    virtual void DoDataExchange( CDataExchange* pDX );

public:
    bool InitInstance(CWnd* pParentWnd);
    void GetDay(int& nYear, int& nMonth);
    void SetDay(int nYear, int nMonth);
    virtual ~CCalBar();
 
    // Generated message map functions
protected:
    //{{AFX_MSG(CCalBar)
    afx_msg LRESULT HandleInitDialog(WPARAM, LPARAM);
    virtual BOOL OnInitDialog();
    //}}AFX_MSG
    DECLARE_MESSAGE_MAP()

};

/////////////////////////////////////////////////////////////////////////////
 
//{{AFX_INSERT_LOCATION}}
// Microsoft Developer Studio will insert additional declarations immediately before the previous line.

#endif // !defined(AFX_CALBAR_H__1E82E2C9_D3C4_11D1_A4FF_00AA00C0AFD5__INCLUDED_)

 
------------------------------------------------------------------------------
 
// CalBar.cpp : implementation file
//
 
#include "stdafx.h"
#include "cal.h"
#include "CalBar.h"
 
#ifdef _DEBUG
#define new DEBUG_NEW
#undef THIS_FILE

static char THIS_FILE[] = __FILE__;
#endif
 
/////////////////////////////////////////////////////////////////////////////
// CCalBar
 
CCalBar::CCalBar()
{
    //{{AFX_DATA_INIT(CCalBar)
    m_nYear = 1;
    m_nMonth = 0;

    //}}AFX_DATA_INIT
}
 
CCalBar::~CCalBar()
{
}
 
BEGIN_MESSAGE_MAP(CCalBar, CDialogBar)

    //{{AFX_MSG_MAP(CCalBar)
    ON_MESSAGE(WM_INITDIALOG, HandleInitDialog)
    //}}AFX_MSG_MAP
END_MESSAGE_MAP()
 
/////////////////////////////////////////////////////////////////////////////
// CCalBar message handlers
 
// CDialogBar 를 CDialog 처럼 사용하기 위해 OnInitDialog를 호출한다.
// CDialog 역시 이런식으로 구성되어 있다.
LRESULT CCalBar::HandleInitDialog(WPARAM wParam, LPARAM lParam)
{
    OnInitDialog();
 
    return CDialogBar::HandleInitDialog(wParam, lParam);
}
 
void CCalBar::DoDataExchange( CDataExchange* pDX )
{
    //{{AFX_DATA_MAP(CCalBar)
    DDX_Control(pDX, IDW_MONTH, m_cbox);
    DDX_Control(pDX, IDW_YEAR,  m_edit);
    DDX_Control(pDX, IDW_YEAR_SELECT, m_spin);
    DDX_Text(pDX, IDW_YEAR, m_nYear);
    DDX_CBIndex(pDX, IDW_MONTH, m_nMonth);
    //}}AFX_DATA_MAP
}

void CCalBar::SetDay(int nYear, int nMonth)
{
    /*
    CString string;
    string.Format("%d", nYear);
    
    m_edit.SetWindowText(string);
    m_cbox.SetCurSel(nMonth - 1);
    */

    m_nYear = nYear;
    m_nMonth = nMonth - 1;
    UpdateData(FALSE);
}

BOOL CCalBar::OnInitDialog()
{
    // TODO: Add extra initialization here
    UpdateData(FALSE);
 
    m_spin.SetRange(1, 3000);
    m_cbox.SetCurSel(0);
    
    return TRUE;  
}

void CCalBar::GetDay(int & nYear, int & nMonth)
{
    UpdateData();
    nYear = m_nYear;
    nMonth = m_nMonth + 1;
}

 

<다이얼로그바의 초기화>

다이얼로그바는 툴바와 달리 콤보 박스 에디트 박스등 다이얼로그에서 사용할 수 있는 컨트롤들을 모두 사용할 수 있습니다. 그래서 좀더 유용하다고 볼수 있죠.

한데 다이얼로그는 OnInitDialog에서 초기화해줄 수 있지만 다이얼로그바는 그 부분이 없습니다. 그 부분을 직접 만들어 주시면 다이얼로그와 똑같이 사용할 수 있습니다. 코드는 다음과 같습니다.

 1. 헤더 파일에 다음과 같이 추가합니다. 

    afx_msg LRESULT InitDialog(WPARAM, LPARAM);
    afx_msg BOOL OnInitDialog(); 

2. cpp 파일 메세지 맵 부분에 다음을 추가합니다.

     ON_MESSAGE(WM_INITDIALOG, InitDialog) 

3. 다음과 같이 코딩합니다.

 LRESULT CMyBar::InitDialog(WPARAM, LPARAM)
{
    UpdateData(FALSE);
    OnInitDialog();
 
    return 0L;
}

BOOL CMyBar::OnInitDialog()
{
    // 여기에다가 초기화 코드를 적습니다.
 
    return TRUE;  // return TRUE unless you set the focus to a control
                        // EXCEPTION: OCX Property Pages should return FALSE
}

 

실제로 다이얼로그바는 OnInitDialog가 없지만 WM_INITDIALOG 메세지는 받습니다.

그걸 이용한 것입니다.

 

==========

다음과 같이 바꾸면 코드가 완전하겠습니다.

 

1. 헤더 파일에 다음과 같이 추가합니다.

    afx_msg LRESULT InitDialog(WPARAM wParam, LPARAM lParam);
    afx_msg BOOL OnInitDialog();

2. cpp 파일 메세지 맵 부분에 다음을 추가합니다.

    ON_MESSAGE(WM_INITDIALOG, InitDialog)

3. 다음과 같이 코딩합니다.

LRESULT CMyBar::InitDialog(WPARAM wParam, LPARAM lParam)
{
    if ( !HandleInitDialog(wParam, lParam) || !UpdateData(FALSE))
    {
        TRACE0("Warning: UpdateData failed during dialog init.\n");
        return FALSE;
    }
    return OnInitDialog();
}

BOOL CMyBar::OnInitDialog()
{
         // 여기다가 초기화 코드를 적습니다.
 
    return TRUE;  // return TRUE unless you set the focus to a control
                        // EXCEPTION: OCX Property Pages should return FALSE

=========

그렇게 짜시면 OnInitDialog에서의 리턴값이 의미가 없게 되는데요.

리턴 값에 따라서 처음에 포커스가 가는 차일드 컨트롤을 직접 지정한것에 따르느냐, 아니면 리소스에서 선언한 첫번째 컨트롤로 가느냐가 달라지게 됩니다. 

적어도 InitDialog에서 OnInitDialog의 리턴값을 리턴해야 합니다.

 그리고 아래의 영어(솜씨는 안되지만 홈주인이 마음대로 번역해 봤습니다...)는 MS에서 제시하는 다이얼로그바 사용하는 법입니다. 좀 복잡하긴 해도 확실히 돌아가지요.

 HandleInitDialog는 OLE 컨트롤을 초기화해주는 일을 하는 CDialogBar의 함수입니다. 안불러줘도 되는 상황이 많겠지만, 안전하게 하자면 불러주는게 좋겠죠.

 

MFC의 "미리보기"처럼 단순히 CButtons만을 사용한 단순한 CDialogBar를 생성할 때, CDialogBar에서 파생시킬 필요는 없습니다. CControlBar의 parent는 차일드 컨트롤로부터 통지 메시지를 받기 때문이죠.

When creating a simple CDialogBar, such as one with only CButtons similar to MFC's print preview, it is not necessary to derive from CDialogBar because the parent of CControlBar receives the notification messages from any child controls.

 그러나, drop-down 콤보박스, 트리뷰, ActiveX 컨트롤 같은 것을 포함한 좀더 복잡한 CDialogBar의 경우에는, 차일드 컨트롤의 초기화를 하기위해서 CDialogBar에서 파생시키는 것이 유용할 겁니다.

However, in the case of a more complex CDialogBar, which might have a drop-down combo box, a treeview, or ActiveX control, it might be useful to derive from CDialogBar to provide initialization for the child controls.

  클래스 위저드에서 CDialogBar는 베이스 클래스로 나타나지 않기 때문에, 다음 글은 CDialog에서 파생된 클래스를 CDialogBar에서 파생된 클래스로 바꾸는 방법을 보여줍니다.

Because ClassWizard does not support deriving a class from CDialogBar, this article shows the steps necessary to create a class from CDialog and then "convert" the class to CDialogBar.

 

MORE INFORMATION

================

 우선, 각종 차일드 컨트롤들을 포함한 CDialog 클래스를 만듭니다. 다음 아홉가지 순서를 거쳐 CDialog 클래스를 CDialogBar 클래스로 변경시킬 수 있습니다.

To start out, create a CDialog class with the child controls you want to use. You can transform the CDialog class into a CDialogBar class using the following nine steps:

 

1. Change the base class from CDialog to CDialogBar in the class declaration. Don't forget to also change the base class in BEGIN_MESSAGE_MAP in the .cpp file.

2. Change the constructor in both the .h and the .cpp files. Also make the change to the DoDataExchange(). Below are three items to change.

  Change the following from

 

      CMyDlgBar (CWnd* pParent = NULL);   // standard constructor

      CMyDlgBar:: CMyDlgBar (CWnd* pParent /*=NULL*/)
         : CDialog(CMyDlgBar::IDD, pParent)
      {
         ...

      void CMyDlgBar::DoDataExchange(CDataExchange* pDX)
      {
         CDialog::DoDataExchange(pDX);
         ...

 

   to the following:

 

      CMyDlgBar ();   // standard constructor

      CMyDlgBar:: CMyDlgBar ()
      {
         ...

      void CMyDlgBar::DoDataExchange(CDataExchange* pDX)
      {
         CDialogBar::DoDataExchange(pDX);
         ...

 

   The key to the transformation is the conversion of the virtual OnInitDialog() member function to the WM_INITDIALOG message mapped method by changing the OnInitDialog method and by adding the ON_MESSAGE() handler. You may not have an override of OnInitDialog(). If not, add one before proceeding.

 

3. Remove "virtual BOOL OnInitDialog();" from the class header and add "afx_msg LONG OnInitDialog ( UINT, LONG );" in its place. For example:

 

      class CMyDlgBar : public CMyDlgBar
      {
         ...
      // Implementation
      protected:

         // Generated message map functions
         //{{AFX_MSG(CMyDlgBar)
         virtual BOOL OnInitDialog();                // <-Remove this line.
         //}}AFX_MSG

         afx_msg LONG OnInitDialog ( UINT, LONG );   // <-Add this line.
         DECLARE_MESSAGE_MAP()
      };

 

   Now, in the class implementation section, make the corresponding changes.

 

4. Add "ON_MESSAGE(WM_INITDIALOG, OnInitDialog );" to the message map in the .CPP implementation file. For example:

 

      BEGIN_MESSAGE_MAP(CMyDlgBar, CDialogBar)
         //{{AFX_MSG_MAP(CMyDlgBar)
         ...
         //}}AFX_MSG_MAP
         ON_MESSAGE(WM_INITDIALOG, OnInitDialog )    // <-- Add this line.
      END_MESSAGE_MAP()

 

   Now, convert the virtual OnInitDialog() to the message-mapped OnInitDialog().

 

5. Make the OnInitDialog() conversion as follows:

    Change the following:

 

      BOOL CMyDlgBar::OnInitDialog()
      {
         CDialog::OnInitDialog();   // <-- Replace this line:
            ...
 

   to the following:

 

      LONG CMyDlgBar::OnInitDialog ( UINT wParam, LONG lParam)
      {
                       // <-- with these lines. -->

         if ( !HandleInitDialog(wParam, lParam) || !UpdateData(FALSE))
         {
            TRACE0("Warning: UpdateData failed during dialog init.\n");
            return FALSE;
         }
         ...

 

   The CDialogBar class doesn't have a virtual OnInitDialog(), and therefore calling one does not work. UpdateData is called to subclass or initialize any child controls.

 

6. Make sure the dialog box resource styles to the following:

      Style: Child
      Boarder: None
      Visible: Unchecked 

   At this point, everything has been reconnected to make the transformation from a CDialog class to a CDialogBar class work correctly. Now, create and use it.

 

7. Add an instance of the derived CDialogBar to the CframeWnd-derived class (normally called CMainFrame). For example:

 

      class CMainFrame : public CFrameWnd
      {
          ...
          CMyDlgBar m_myDlgBar;
          ...
      };

 

8. Call the create method for the m_myDlgBar variable in the CFrameWnd::OnCreate() method similar to the following:

       int CMainFrame::OnCreate(LPCREATESTRUCT lpCreateStruct)
      {
         ...
         if (!m_myDlgBar.Create(this, IDD_DLGBAR1, CBRS_LEFT, IDD_DLGBAR1))
         {
            TRACE0("Failed to create dialog bar\n");
            return -1;      // fail to create
         }
         ...
      }

 

9. Finally, if you want to support dynamic docking and resizing of the CDialogBar, add the following lines to the end of CMainFrame::OnCreate():

 

      int CMainFrame::OnCreate(LPCREATESTRUCT lpCreateStruct)
      {
         ...
         m_myDlgBar.SetBarStyle(m_wndToolBar.GetBarStyle() |
            CBRS_TOOLTIPS | CBRS_FLYBY | CBRS_SIZE_DYNAMIC);
         m_myDlgBar.EnableDocking(CBRS_ALIGN_ANY);
         DockControlBar(&m_myDlgBar);

         return 0;
      }

 

REFERENCES

==========

 

For additional information, please see the following article in the Microsoft Knowledge Base:

   ARTICLE-ID: Q99161
   TITLE     : HOWTO: Derive From Classes not Listed in ClassWizard

 

Additional query words: kbDSupport kbdsd

======================================================================

Keywords          : MfcUI
Version           : WINNT:5.0
Platform          : winnt
Issue type        : kbhowto

=============================================================================

 

THE INFORMATION PROVIDED IN THE MICROSOFT KNOWLEDGE BASE IS PROVIDED "AS IS" WITHOUT WARRANTY OF ANY KIND.  MICROSOFT DISCLAIMS ALL WARRANTIES, EITHER EXPRESS OR IMPLIED, INCLUDING THE WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE.  IN NO EVENT SHALL MICROSOFT CORPORATION OR ITS SUPPLIERS BE LIABLE FOR ANY DAMAGES WHATSOEVER INCLUDING DIRECT, INDIRECT, INCIDENTAL,

CONSEQUENTIAL, LOSS OF BUSINESS PROFITS OR SPECIAL DAMAGES, EVEN IF MICROSOFT CORPORATION OR ITS SUPPLIERS HAVE BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGES.  SOME STATES DO NOT ALLOW THE EXCLUSION OR LIMITATION OF LIABILITY FOR CONSEQUENTIAL OR INCIDENTAL DAMAGES SO THE FOREGOING LIMITATION MAY NOT APPLY.