Tech & IT/프로그래밍

MFC MDI SDI 설명 및 팁

해피콧 2013. 7. 9. 11:16
'); }
'); }

  출처: openwiki - http://wiki.rabidus.net/ow.asp?p=MFC%5FMDI%5FSDI&a=print


  MFC MDI SDI

  MFC MDI and SDI

Reseach

DocTemplate를 추가시 리소스를 반드시 할당해야 한다.

등록된 DocTemplate가 여러개일 경우 NEW_FILE을 하게 되면 다이얼로그가 떠서 등록된 템플릿중에 하나를 골라 새 윈도우를 생성하게 된다. 그런데 이때 리소스쪽에 몇가지 작업(스트링 테이블에 CNewTypeDlg를 위한 문자열)을 하지 않으면 인식되지않아 첫번째로 등록된 문서템플릿에 해당하는 문서만 새로 생성되는 것을 볼 수 있다. 반드시 해야 되는것은

  • 리소스 심볼
  • 스트링 테이블에 리소스 심볼 ID로 만든 CNewTypeDlg를 위한 문자열
  • 메뉴

이고 부가적으로 별도의 메뉴를 유지 하려면 엑셀레이터, 아이콘을 해주어야 한다.

왜 스트링 테이블에 CNewTypeDlg를 위한 문자열을 삽입하지 않으면 안되는 이유는 ID_FILE_NEW의 COMMAND가 CWinApp에 전달되어 CWinApp::OnFileNew()가 호출된다고 가정하면 아래와 같은 코드가 실행이 된다.

CDocManager::OnFileNew()
{
        CDocTemplate* pTemplate = (CDocTemplate*)m_templateList.GetHead();
        if (m_templateList.GetCount() > 1)
        {
                // more than one document template to choose from
                // bring up dialog prompting user
                CNewTypeDlg dlg(&m_templateList);
                INT_PTR nID = dlg.DoModal();
                if (nID == IDOK)
                        pTemplate = dlg.m_pSelectedTemplate;
                else
                        return;     // none - cancel operation
        }
}

가 실행되는데 이때 등록된 문서템플릿이 1개 이상이면 CNewTypeDlg의 다이얼로그가 보이게 된다. 근데 이때 CNewTypeDlg코드에서는 스트링 테이블의 문자열에서 템플릿에 대한 정보를 가져오게 되는데 못가져오면 다이얼로그의 리스트박스에 삽입하지 못하게 되고 2개인데 하나가 실패되면 등록되어 있는 템플릿만 새로 생성되게 되는 것이다. 그것을 판단하는 CNewTypeDlg::InitDialog()이다.

POSITION pos = m_pList->GetHeadPosition();
        // add all the CDocTemplates in the list by name
        while (pos != NULL)
        {
                CDocTemplate* pTemplate = (CDocTemplate*)m_pList->GetNext(pos);
                ASSERT_KINDOF(CDocTemplate, pTemplate);

                CString strTypeName;
                if (pTemplate->GetDocString(strTypeName, CDocTemplate::fileNewName) &&
                   !strTypeName.IsEmpty()) // 문자열로 판단한다.
                {
                        // add it to the listbox
                        int nIndex = pListBox->AddString(strTypeName);
                        if (nIndex == -1)
                        {
                                EndDialog(-1);
                                return FALSE;
                        }
                        pListBox->SetItemDataPtr(nIndex, pTemplate);
                }
        }

리소스에 등록된 문자열은 '\n'으로 구분되는 것으로 순서대로 아래 enum값의 의미를 가진다.

enum DocStringIndex
{
        windowTitle,        // default window title
        docName,            // user visible name for default document
        fileNewName,        // user visible name for FileNew
        // for file based documents:
        filterName,         // user visible name for FileOpen
        filterExt,          // user visible extension for FileOpen
        // for file based documents with Shell open support:
        regFileTypeId,      // REGEDIT visible registered file type identifier
        regFileTypeName,    // Shell visible registered file type name
};

따라서 이에 맞게 스트링테이블의 문서템플릿정보를 추가해야 한다.

MFC Apllication에서 Frame, Doc / View상에서 다른 개체를 참고하는 방법

DocumentGetFirstViewPosition 및 GetNextView를 사용하여 문서의 뷰 목록에 액세스합니다. GetDocTemplate를 호출하여 문서 템플릿에 액세스합니다.
ViewGetDocument를 호출하여 해당 문서에 액세스합니다,GetParentFrame을 호출하여 해당 프레임 창에 액세스합니다.
Signle Frame WindowGetActiveView를 호출하여 현재 뷰에 액세스합니다,GetActiveDocument를 호출하여 현재 뷰에 연결된 문서에 액세스합니다.
MDI Frame WindowMDIGetActive를 호출하여 현재 활성화된 CMDIChildWnd에 액세스합니다.

SDI에서 각 개체 얻는 방법

ApplicationCWinApp * pApp = (CWinApp*)AfxGetApp();
MainFrameCMainFrame * pFrame = (CMainFrame *)AfxGetMainWnd();
DocumentCMainFrame * pFrame = (CMainFrame *)AfxGetMainWnd();, CDocument *pDoc = CDocument *)pFrame->GetActiveDocument();
ViewCMainFrame * pFrame = (CMainFrame *)AfxGetMainWnd();, CView *pView = (CView *)pFrame->GetActiveView();

MDI에서 각 개체 얻는 방법

ApplicationCWinApp *pApp = (CWinApp*)AfxGetApp();
MainFrameCMainFrame *pFrame = (CMainFrame *)AfxGetMainWnd();
ChildFrameCMainFrame *pFrame = (CMainFrame *)AfxGetMainWnd();, CChildFrame *pChild = (CChildFrame *)pFrame->GetActiveFrame();
DocumentCMainFrame *pFrame = (CMainFrame *)AfxGetMainWnd();, CChildFrame *pChild = (CChildFrame *)pFrame->GetActiveFrame();, CDocument *pDoc = CDocument *)pChild->GetActiveDocument();
ViewCMainFrame *pFrame = (CMainFrame *)AfxGetMainWnd();, CChildFrame *pChild = (CChildFrame *)pFrame->GetActiveFrame();, CView *pView = (CView *)pChild->GetActiveView();

Tip and Tech

How To Get Current CDocument or CView from Anywhere

// Document header file
class CMyDoc : public CDocument
{
        public:
        static CMyDoc * GetDoc();
};
                                
CMyDoc * CMyDoc::GetDoc()
{
        CFrameWnd * pFrame = (CFrameWnd *)(AfxGetApp()->m_pMainWnd);
        return (CMyDoc *) pFrame->GetActiveDocument();
}
                                
{
        CMDIChildWnd * pChild =
                ((CMDIFrameWnd*)(AfxGetApp()->m_pMainWnd))->MDIGetActive();

        if ( !pChild )
                return NULL;

        CDocument * pDoc = pChild->GetActiveDocument();

        if ( !pDoc )
                return NULL;

        // Fail if doc is of wrong kind
        if ( ! pDoc->IsKindOf( RUNTIME_CLASS(CMyDoc) ) )
                return NULL;

        return (CMyDoc *) pDoc;
}

class CMyView
{
        ...
public:
        static CMyView * GetView();
        ...
};

CMyView * CMyView::GetView()
{
        CFrameWnd * pFrame = (CFrameWnd *)(AfxGetApp()->m_pMainWnd);

        CView * pView = pFrame->GetActiveView();

        if ( !pView )
                return NULL;

        // Fail if view is of wrong kind
        // (this could occur with splitter windows, or additional
        // views on a single document
        if ( ! pView->IsKindOf( RUNTIME_CLASS(CMyView) ) )
                return NULL;

        return (CMyView *) pView;
}

CMyView * CMyView::GetView()
{
        CMDIChildWnd * pChild =
                ((CMDIFrameWnd*)(AfxGetApp()->m_pMainWnd))->MDIGetActive();

        if ( !pChild )
                return NULL;

        CView * pView = pChild->GetActiveView();

        if ( !pView )
                return NULL;

        // Fail if view is of wrong kind
        if ( ! pView->IsKindOf( RUNTIME_CLASS(CMyView) ) )
                return NULL;

        return (CMyView *) pView;
}
                                
CMyView::GetView();

여러개의 DockTemplate를 등록한경우(MDI, SDI)에서 Document, View 순회하기

AddDocTemplate를 통하여 여러개의 DocTemplate를 등록한 경우 아래와 같은 코드를 통하여 순회가능하다.

POSITION doctempPos = GetFirstDocTemplatePosition();
while (doctempPos)
{
        CDocTemplate* doctemp = GetNextDocTemplate(doctempPos);
        POSITION docPos = doctemp->GetFirstDocPosition();
        while (doctemp && docPos)
        {
                CDocument *doc = doctemp->GetNextDoc(docPos);        
                POSITION viewPos = doc->GetFirstViewPosition();
                while (doc && viewPos)
                {
                        CView* view = doc->GetNextView(viewPos);
                        view->GetParentFrame()->ActivateFrame(SW_SHOW); // 순차적으로 활성화
                }
        }
        CString doctempStr;
        doctemp->GetDocString(doctempStr, CDocTemplate::docName);
        TRACE("CDocTemplate name: %s\n", doctempStr);
}

MultiDocumentTemplate에서 Document Template추가법

AddDocTemplate( new CMultiDocTemplate( IDR_SCRIBTYPE,
          RUNTIME_CLASS( CScribDoc ),
          RUNTIME_CLASS( CMDIChildWnd ),
          RUNTIME_CLASS( CScribView ) ) );

SingleDocumentTemplate에서 Document Template추가법

CSingleDocTemplate* pDocTemplate;
pDocTemplate = new CSingleDocTemplate(
IDR_MAINFRAME,
RUNTIME_CLASS(CTestDoc),
RUNTIME_CLASS(CMainFrame),
RUNTIME_CLASS(CTestView)
AddDocTemplate(pDocTemplate);

MDI에서 자동으로 빈문서 생성을 막는 법

CWinApp::InitInstance()에서 ProcessShellCommand()함수에서 FILE_NEW를 함으로 CmdInfo.m_nShellCommand에 CCommandLineInfo::FileNothing를 넣어준다.

if(cmdInfo.m_nShellCommand == CCommandLineInfo::FileNew )
      cmdInfo.m_nShellCommand = CCommandLineInfo::FileNothing;

ChildFrame생성시 MainFrame의 최대 크기 맞추기

  • ChildFrame의 PreCreateWindow함수에 WS_MAXIMIZE를 추가한다.
  • ActivateFrame() 함수를 오버라이딩 하여 nCmdShow에 SW_MAXMIZE를 추가한다. 
    void CChildFrameEx::ActivateFrame(int nCmdShow) 
    {
        // TODO: Add your specialized code here and/or call the base class
        nCmdShow = SW_MAXIMIZE;
        CMDIChildWnd::ActivateFrame(nCmdShow);
    }

Reference

Documents

Website

MFC에서 Document, View, Frame간의 관계, MSDN

Document, View, Windows만들기, MSDN

Document Template 만들기, MSDN

How To Get Current CDocument or CView from Anywhere, MSDN

Books


CategoryCategory