본문으로 바로가기

MDI(Multi Document Interface)로 개발하기(완료)

category 개발언어/c++ 2016. 7. 26. 16:37

AppFrame_demo.zip

AppFrame_source.zip


MDI 와 SDI의 차이를 설명하고
자주 사용하게 되는 Frame형태 또는 Architecture를 미리 만들어 놓는 과정을 설명 하였습니다.

이제 제가 사용하는 Frame의 최종 형태가 완성이 되었습니다.
이렇게 기본적인 형태를 준비 해놓고 프로젝트를 수행할 때 필요한 부분만 수정하여 사용할 목적으로 기본형태를 만들었습니다.

개발 방법이 궁금하시면 이전 단계 MDI(Multi Document Interface)로 개발하기1~5 까지를 참조 하시기 바랍니다.

이번에 다루는 주제는 사이즈 변경 가능한 유연한 도킹컨트롤 바 입니다.

Resizable Docking Window 또는 SizingControlBar 정도로 이름을 붙이면 될 것 같습니다.
첨부된 소스의 “CDynamicCtrlBar”클래스 입니다.

MFC로 작업하다 보면 CDialogBar를 사용하다 보면 상당히 불편한 부분이 많습니다.
CDynamicCtrlBar는 CDialog를 직접 도킹 시킬 수 있게 만들어 져 있습니다.

기능과 사용법을 살펴 보겠습니다.

MainFrame.h에 #include "DynaCtrlBar.h"를 선언합니다.그리고 도킹된 윈도우를 관리하는 클래스를 변수로 잡습니다 CSCBArray m_dynar; #include "DynaCtrlBar.h"
class CMainFrame : public baseFRAME
{
     DECLARE_DYNCREATE(CMainFrame)  
public:
    CMainFrame();  
    CSCBArray    m_dynar;//동적바를 쉽게 추가 하고 제어하기 위해 Array로 만듦


윈도우 도킹

    Window를 직접 도킹시키기 위해서 m_dynar에 CDynamicWNDBarCF를 할당합니다.
    아래 소스는 CMainFrame::OnCreate(LPCREATESTRUCT lpCreateStruct) 에 코딩된 내용 입니다
.

     ///////////////////////////////////////////////////
    ///  동적 컨트롤바 생성 Default   
    //동적 BAR를 만들어 Frame에 붙인다
    int pos=0;
    pos=m_dynar.GetSize();
    m_dynar.Add(new CDynamicWNDBarCF());//생성자에서 바로 추가
    if (!m_dynar[0]->Create(_T("Window Add"), this,CSize(460,100),FALSE, ID_VIEW_CTRLBAR+pos /* control*= Id*/))
    {
        TRACE0("Failed to create instant bar\n");
        return -1;        // fail to create
    }
    m_dynar[pos]->SetBarStyle(m_dynar[pos]->GetBarStyle() |
        CBRS_TOOLTIPS | CBRS_FLYBY | CBRS_SIZE_DYNAMIC|SCBS_SIZECHILD);
    m_dynar[pos]->EnableDocking(CBRS_ALIGN_ANY);
    DockControlBar(m_dynar[pos], AFX_IDW_DOCKBAR_TOP);//default로 최상단에 고정

    CDlgSetItem* p_dlg=new CDlgSetItem();
    p_dlg->Create(IDD_DIALOG_SET,m_dynar[pos]);
    ((CDynamicWNDBarCF*)m_dynar[pos])->AddWnd(p_dlg,"Test1");

CDialog클래스인 CDlgSetItem를 Modaless로 생성하여 도킹 시킨 것 입니다.

CDialog  도킹

CDialog클래스는
((CDynamicWNDBarCF*)m_dynar[pos])->AddDialog(new CDlgSetItem(),IDD_DIALOG_SET,"Test1"); 
와 같이 사용하여 직접 도킹이 가능합니다. 

 

CRichEditCtrl 도킹

   StatusControl 클래스는 CDynamicCtrlBarCF를 상속받아서 도킹 시킨 것입니다.
    유산한 방법으로 상속 받아서 도킹 시켜도 됩니다.  

   //동적 BAR를 만들어 Frame에 붙인다 좌측에 자동 고정    
    pos=m_dynar.GetSize();
    m_dynar.Add(p_App->p_statuslog);
    if (!m_dynar[pos]->Create(_T("Status"), this,CSize(160,100),FALSE, ID_VIEW_CTRLBAR+pos /* control*= Id*/))
    {
        TRACE0("Failed to create instant bar\n");
        return -1;        // fail to create
    }
    m_dynar[pos]->SetBarStyle(m_dynar[pos]->GetBarStyle() |
        CBRS_TOOLTIPS | CBRS_FLYBY | CBRS_SIZE_DYNAMIC|SCBS_SIZECHILD);
    m_dynar[pos]->EnableDocking(CBRS_ALIGN_ANY);
    DockControlBar(m_dynar[pos], AFX_IDW_DOCKBAR_LEFT);//default로 좌측  고정

도킹 컨트롤 메뉴 제어하기(동적 메뉴 만들기)

도킹된 메뉴를 숨겼을 때 이를 다시 보여주기 위한 메뉴를 자동적으로 만듧니다.
CMainFrame::OnInitMenu(CMenu* pMenu) 에서 메뉴가 만들어 질 때  메뉴를 추가 합니다.

void CMainFrame::OnInitMenu(CMenu* pMenu)
{
    baseFRAME::OnInitMenu(pMenu);   
    CreateBarShowMenu();//MAin Frame이 활성화 될때 마다 동적 Bar View 메뉴 최적하 하도록    
}

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

void CMainFrame::CreateBarShowMenu()
{
    CMenu* p_menu=AfxGetMainWnd()->GetMenu();
    UINT sz=p_menu->GetMenuItemCount();
    if(sz<0) return;
    if(m_dynar.GetSize()<=0)return;//컨트롤 할 것이 없으면 return
    BOOL b_Find=FALSE;
    CString m_text,m_comp;
    UINT i=0,j=0,sz_fnc=0,mID=0;        
    MENUITEMINFO m_menuinf;      
    CMenu* menu =NULL;
    CMenu* p_smenu=NULL;
    CMenu* p_third=NULL;
    memset(&m_menuinf,0,sizeof(MENUITEMINFO));
    m_menuinf.cbSize=sizeof(MENUITEMINFO);
    m_menuinf.fMask=MIIM_TYPE|MIIM_SUBMENU|MIIM_ID|MIIM_DATA;
    m_menuinf.fType=MFT_STRING;
    for(i=0; (p_third==NULL) && (i<sz) ;i++)//메뉴에서 찾고자 하는 항목을 찾아서 ...
    {
       p_smenu=p_menu->GetSubMenu(i); //Sub Menu를 불러 와럿
       sz_fnc=p_smenu->GetMenuItemCount();//Sub Menu의 갯수를 확인
       for(j=0;j<sz_fnc;j++)
       {
           mID=p_smenu->GetMenuItemID(j);
           if(mID==ID_VIEW_STATUS_BAR)
           {               
               p_third=p_smenu;//상태바가 있는 위치 일 경우
               break;
           }
       }      
    }
    if(p_third==NULL)//View Menu가 없으므로 메뉴 추가
    {
        p_menu->AppendMenu(MF_STRING,MF_STRING,"View");
    }
    //찾은 메뉴의 하단에 메뉴를 추가, 동적 메뉴 추가
    if(p_third)
    {     
      m_dynar[0]->GetWindowText(m_comp);//찾을 메뉴의 이름 가져 오기 즉 첫번째 메뉴
      sz=p_third->GetMenuItemCount();
      for(j=0;j<sz;j++)//추가 할 메뉴가 있는지 확인
      {
         p_third->GetMenuString(j,m_text,MF_BYPOSITION);
         if(m_text==m_comp)b_Find=TRUE;
      }
      if(!b_Find) //등록해야할 메뉴를 잧지 못한 경우
      {
      p_smenu->InsertMenu (sz,MF_BYPOSITION,MF_SEPARATOR);    //메뉴 구분선 추가 하기
      sz_fnc=m_dynar.GetSize();
      for(i=0;i<sz_fnc;i++)//메뉴에 자동 추가
      {
        m_dynar[i]->GetWindowText(m_text);
        p_smenu->InsertMenu (sz+i+1,MF_BYPOSITION,ID_VIEW_CTRLBAR+i,m_text);
        //p_menu->ModifyMenu(m_menuinf.wID, MF_BYCOMMAND, m_menuinf.wID,m_text);
      }
      }
    }
    sz_mnu=sz_mnu+m_dynar.GetSize();//Show 메뉴으 Click Command를 수행할 갯수 설정    
}

도킹 컨트롤의 최종상태 가져오기

도킹컨트롤의 사이즈 변경 또는 숨기기 도킹위치 변경 정보를 레지스트리에서 불러와  마지마 상태와 똑같이  복원합니다.

#ifdef _SCB_REPLACE_MINIFRAME
      m_pFloatingFrameClass = RUNTIME_CLASS(CSCBMiniDockFrameWnd);
    #endif //_SCB_REPLACE_MINIFAME
    CRuntimeClass* prt = GetRuntimeClass();
    CString sProfile = _T(prt->m_lpszClassName);
    TRY
    {
     if (VerifyBarState(sProfile))
     {
        CDynamicCtrlBar::GlobalLoadState(this, sProfile);
        LoadBarState(sProfile);
     }
    }
    CATCH(CException, e)
    {
      puts("in except");
    }
    END_CATCH   

도킹 컨트롤의 최종상태 저장하기

도킹컨트롤의 사이즈 변경 또는 숨기기 도킹위치 변경 정보를 레지스트리에 저장합니다.

void CMainFrame::OnDestroy()
{
    //Save Bar Status before MainFrame Closed
    CRuntimeClass* prt = GetRuntimeClass();
    CString sProfile = _T(prt->m_lpszClassName);
    CDynamicCtrlBar::GlobalSaveState(this, sProfile);
    SaveBarState(sProfile);       

    baseFRAME::OnDestroy();   
    // TODO: Add your message handler code here   
}

 

CRichEditCtrl을 파일로 저장하기

  CRichEditCtrl요 있는 내용을 파일로 저장합니다.

void StatusControl::SaveRtf()
{
       CString m_FullFile;
    CString s_file=AfxGetApp()->m_pszHelpFilePath ;
    int    p=s_file.ReverseFind('\\');
    s_file=s_file.Left(p);
   
    m_FullFile.Format("%s\\%s.rtf",s_file,CTime::GetCurrentTime().Format("%Y%m%d%H"));   
   
    CFile cFile(m_FullFile, CFile::modeCreate|CFile::modeWrite);
   
    EDITSTREAM es;
    es.dwCookie = (DWORD) &cFile;
    es.pfnCallback = MyStreamOutCallback;
    m_RichEdit.StreamOut(SF_RTF, es);
   
}


위의 과정까지 모두 구현 하고 나서 파일을 보관 하고 나면
다음에 작업할 땐 휠씬 쉽게 작업이 가능 할 것입니다.