본문으로 바로가기

MDI(Multi Document Interface)로 개발하기5

category 개발언어/c++ 2016. 7. 25. 11:35

앞에서 SDI로 프로젝트를 개발하고 MDI로 변환 하는 방법을 알아 보았습니다.
이장에서는 MDI 에서 CDialogBar를 사용하는 방법을 알아 보겠습니다.

CMainFrame과 CChildFrame의 차이와 CDialogBar에 개념이해 정도를 살펴 볼 것 입니다.

먼저 CDialogBar는 CDialog를 Frame에 ToolBar처럼 Docking되는 컨트롤이라고 보시면 됩니다.
실제로 CDialogBar는 CControlBar를 상속 받았으며 내부 코드로 구현 할 거의 없습니다.
이런 이유 때문에 코드가 Frame과 섞이게 되는 개인적으로는 만족스럽지 못하다고 느낍니다.

구현된 그림을 보면
CDialog를 ToolBar 처럼 프레임에 도킹시켜서, UI를 좀더 보기 좋게 만들 수 있습니다.
툴바를 대신할 수 있는 기능이라고 이해 하시면 됩니다.

콤보박스는 물론 리치에디터도 올릴 수가 있습니다.

 

자 그럼 구현 방법을 살펴 보겠습니다.

먼저 CDialog Class를 하나 생성하고 리소스에서 폼 속성 Style을 Child
Border속성을 None으로 바꾸어 줍니다.

생성된 CDialog클래스 에서 부모 클래스를 CDialogBar 로 바꾸어 줍니다.
저는 CDlgCtrl이라고 Dialog클래스를 만들었으므로 “class CDlgCtrl : public CDialogBar”와 같이 바꾸었으며
생성자를 변경했습니다.

“DlgCtrl.h”

class CDlgCtrl : public CDialogBar
{
// Construction
public:
    CDlgCtrl(CWnd* parent=NULL);

“DlgCtrl.cpp”

CDlgCtrl::CDlgCtrl(CWnd* pParent /*=NULL*/)//CDialog(CDlgCtrl::IDD, pParent)
{
    //{{AFX_DATA_INIT(CDlgCtrl)
        // NOTE: the ClassWizard will add member initialization here
    //}}AFX_DATA_INIT
}

CDialogBar의 부모클래스가 CDialog가 아니고 CControlBar이므로 생성자의 모양이 다릅니다.
CDialog상속에서 CDialogBar으로 바뀌었으니 부모클래스를 호출하는 부분도 모두 바꾸어 주어야 합니다.

void CDlgCtrl::OnSize(UINT nType, int cx, int cy)
{
    CDialogBar::OnSize(nType, cx, cy);
   

 

Child Frame에서 CDlgCtrl을 사용하기 위해 헤더에 CDlgCtrl를 하나 선언합니다.

#include "DlgCtrl.h"

class CChildFrame : public CMDIChildWnd
{
    DECLARE_DYNCREATE(CChildFrame)
public:
    CChildFrame();
    CDlgCtrl m_DlgCtrl;

그리고 “ChildFrame.cpp”에 도킹을 구현 합니다.

int CChildFrame::OnCreate(LPCREATESTRUCT lpCreateStruct)
{
    if (CMDIChildWnd::OnCreate(lpCreateStruct) == -1)
        return -1;
   
    // TODO: Add your specialized creation code here
    if (!m_DlgCtrl.Create(this, IDD_DIALOG_CTRL,
        CBRS_FLOAT_MULTI|CBRS_ALIGN_LEFT|CBRS_TOOLTIPS|CBRS_TOP|CBRS_BOTTOM|WS_CHILD|WS_VISIBLE|CBRS_SIZE_DYNAMIC,AFX_IDW_DIALOGBAR))
    {
        TRACE0("Failed to create dialogbar\n");
        return -1;        // fail to create
    }

  EnableDocking(CBRS_ALIGN_ANY); //프레임에 도킹허용
    m_DlgCtrl.EnableDocking(CBRS_ALIGN_LEFT|CBRS_ALIGN_TOP|CBRS_ALIGN_BOTTOM); //Dialog bar가 도킹 될 수 있는 위치 설정
    DockControlBar(&m_DlgCtrl,AFX_IDW_DOCKBAR_LEFT);   //프레임 좌측에 Dialgbar 도킹
    RecalcLayout(); //Layout 재 계산
    return 0;
}

도킹된 컨트롤러 사이즈 변경

       ChildFrame이 Layout을 변경할 때 도킹된 Dialogbar도 프레임 사이즈에 맞게 변경되도록 합니다.

void CChildFrame::RecalcLayout(BOOL bNotify)
{
    // TODO: Add your specialized code here and/or call the base class          
    if(m_DlgCtrl.GetSafeHwnd())
    {
        CRect rect;
        CMDIChildWnd::GetWindowRect(&rect) ;   
        m_DlgCtrl.MoveWindow(&rect);
    }
    CMDIChildWnd::RecalcLayout(bNotify);
}

도킹컨트롤 숨기기, 보이기 메뉴 구현
      
도킹된 DialgBar를 메뉴에서 보이거나 숨기기 위해 Child 프레임에서 메뉴이벤트를 받아서
      도킹컨트롤을 보여주거나 숨기도록 합니다.

void CChildFrame::OnViewDlgbar()
{
   BOOL b_show=!m_DlgCtrl.IsWindowVisible();
   ShowControlBar(&m_DlgCtrl,b_show,TRUE);   
}

메뉴에 체크 버튼 넣기
    
도킹 컨트롤바의 상태를 알아내어 상태를 보여줍니다.
     ON_UPDATE_COMMAND_UI에서 컨트롤바의 상태에 따라 메뉴를 Check 상태로 만들어 줍니다.

void CChildFrame::OnUpdateViewDlgbar(CCmdUI* pCmdUI)
{
      if(m_DlgCtrl.IsWindowVisible()) pCmdUI->SetCheck(1);   
    else pCmdUI->SetCheck(0);       
}

도킹컨트롤의 도킹상태 알아내기 
    
CDailoBar는 컨트롤바 이므로 컨트롤바의 도킹 상태를 알아 낼 수 있습니다.
     이렇게 도킹상태를 파악하여 부착위치가 Top,left 등이 상태 인지에 따라서 버튼의 크기를 조절 할 수 있습니다.  

BOOL CDlgCtrl::OnChildNotify(UINT message, WPARAM wParam, LPARAM lParam, LRESULT* pLResult)
{
    DWORD bs=    GetBarStyle();  
    if(bs & (CBRS_ALIGN_TOP|CBRS_ALIGN_BOTTOM) )
        i_align=1;   
    else   i_align=0;
   
    switch(message)
    {
    case 310:   
       Resize();   
        break;
    }
    TRACE("%d\r\n",message);
    return CDialogBar::OnChildNotify(message, wParam, lParam, pLResult);
}
void CDlgCtrl::Resize()
{
      CFrameWnd* p_Frame= GetDockingFrame();
    CRect m_Rect,mrec;
    if(!m_Btn1.GetSafeHwnd() && (this!=NULL))
    {
      m_Btn1.SubclassDlgItem(ID_CHILD_BTN,this);   
    }
    if(!m_Btn1.GetSafeHwnd()) return; 
   
    p_Frame->GetWindowRect(&mrec);
    CDialogBar::MoveWindow(0,0,mrec.right,mrec.Height());
    if(i_align==0)    m_Rect.SetRect(4,4,110,40);   
    else//Herizen   
        m_Rect.SetRect(4,4,mrec.Width()-18,30);   

    m_Btn1.MoveWindow(m_Rect);
}

윈도우배경 색상 및 컨트롤의 색상 바꾸기
     OnCtlColor에서 Dialog또는 버튼의 색상을 바 꿀 수 있습니다.

HBRUSH CDlgCtrl::OnCtlColor(CDC* pDC, CWnd* pWnd, UINT nCtlColor)
{
    HBRUSH hbr = CDialogBar::OnCtlColor(pDC, pWnd, nCtlColor);
    hbr=hbrush;
       CString m_txt;
    COLORREF RefBg=RGB(25,200,240);
    COLORREF cReffnt=RGB(25,200,24);
    CObject* pobj=pWnd;
    if(pobj->IsKindOf(RUNTIME_CLASS(CComboBox)))//콤보박스 인 경우의 색상
    {
        pDC->SetTextColor(RGB(0xFF,0xDC,0x07));
        pDC->SetBkColor( RefBg );   
   
    }
    switch (pWnd->GetDlgCtrlID())
    {
        case ID_CHILD_BTN:
              pDC->SetTextColor(RGB(230,230,250) );
              pDC->SetBkColor(RGB(130,130,150) );
            break;
           case IDC_CHECK1:
              pDC->SetTextColor(RGB(130,130,150));
              pDC->SetBkColor(RefBg );
            break;
        default:
            break;

       }
   
    return hbr;
}

여태 까지 구현된 결과화면은 바로 아래의 그림과 같은 모양이 됩니다.
왼쪽은 ChildFrame에 DialogBar를 도킹한 것이고 오른쪽은 아무런 코딩을 하지 않은
또 하나의 ChildFrame이 활성화 된 상태입니다.
물론 두개의 다른 Document Type이 하나의 MDI Project에 올려진 상태입니다.

MDI에서 MainFrame과 ChildFrame의 역할을 좀더 이해하기가 용이 할 것입니다.

세세하게 더 많은 것을 설명하려고 했는데 막상 그렇지 못해서 아쉽습니다.

하나하나 풀어서 설명하려니 한도 끝도 없는 것 같아
MDI(Multi Document Interface)로 개발하기는 여기까지 설명하고 마칩니다.

기본적인 개념 이해화 향후 재사용 가능한 뼈대는 만들어 진 상태 입니다.
현재까지 코딩한 내용 소스첨부 하여

AppFrame.zip

올려 놓았으니 참조 하시고 이해 하는데 도움이 되었으면 합니다.

지금 까지 의 내용은 Multi Document Interface 와 Single Document Interface를  이해하기 위한
코드를 소개 했습니다.

다음에는 컨트롤 바를 동적으로 사이즈 조절이 가능하게 하고
Log기능을 강화 해서 최종 아키텍쳐를 완성할 예정입니다.

 

 

이전