MFC

Multi_View 사용하는 방법 - 소스 참조

디버그정 2008. 9. 4. 17:49

첨부소스 :


Multi_View 사용하는 방법


  1. 왜 멀티뷰를 사용하는가?

 App Wizard는 Splitter된 윈도우는 제공해 주지만(사실 Splitter View도 멀티뷰의 일종이다. 여기서 말하는 멀티뷰는 한 윈도우에 여러 View를 사용하는 것을 말한다.) MultiView는 제공해 주지 않는다. 그렇다면 왜 Muti View를 사용하는가? 예를 들라고 하면, 지금 우리가 만들고 있는 프로그램과 같이 사용자에게 하나의 윈도우를 통해 여러화면을 제공해줘야 할 때이다. 하지만 그것보다 더 중요한 사항이 있다. 간단히 말하면, 하나의 데이터에 대한 멀티 인터페이스를 제공하는 것이다. 쉽게 이해를 하기 위해 예를 들면, 여러분들은 인터넷 게시판에 글을 적어 본적이 있을 것이다. 그 글들은 DB화 되어 저장된다. 시간이 지나, 게시판의 모습이 지루하게 느껴지면, 스킨을 바꾸곤 하는 경우도 있다. 하지만 DB를 카피할 필요는 없다. 만약 DB를 다시 옮겨야 한다면, 그것은 정말로 불편한 일이 아닐수 없다. 모든 프로그램도 마찬가지이다. Java 역시 이런 개념이 있다.

 여러분은 오버로딩의 개념을 알 것이다. 한가지 예만 든다면, 다른 타입에 대해 같은 작업을 하는 메쏘드는 다른 이름으로 한다면, 그것 또한 Real World와의 개념에 어긋난다. 이것은 왜 그렇게 했을까? 좀 더 real world와 적합한 Object를 만들어 보자는 것이다.

 MFC는 Doc / View 구조를 제공한다. MFC를 만들어지진 않았겠지만 엑셀이 그런 경우이다. 엑셀은 한 데이터에 대해 표, 차트 등 여러 가지 정보를 제공해 준다. 표에서 Data가 바뀌면 차트에도 바뀐다. 표의 Data가 바뀌면 차트의 Data를 바꾸는 것일까? 그러진 않았을 것이다. 나도 그렇게 할 생각이 없기 때문이다.  Doc는 Data를 두는 Object이다. View는 주로 이벤트와 Interface를 제공한다. 이제 왜 Data와 Interface를 구분하는지 이해가 가는가?


  2. MFC에서는 어떻게 멀티뷰를 지원하는가?

앞에서 말했듯이. 첫 번째는 Splitter 윈도우다. App Wizard에서도 제공해주며, MainFrame::Oncreate()를 조금만 본다면 이해할 수 있을 것이다. 사실 완전히 이해할 필요는 없다. 언제나 우리에겐 Bible과 Google이 있기 때문이다. 다른 경우는 Tab Dialog or Properties Sheet를 이용할 수도 있다. 하지만, 여기서 말할려고 하는 경우는 Doc와 결합된 Multi Views를 말한다.

  MFC는 MainFrame과 Doc, 그리고 View를 크게 하나로 묶어서 관리한다. Doc Template가 그것이다. 여기서 잠시 App Wizard에 의해 생성된 Code를 잠시만 보면 (SDI),

            CProjectNameApp::InitInstance(){
               ......
                CSingleDocTemplate* pDocTemplate;
                pDocTemplate = new CSingleDocTemplate(
                        IDR_MAINFRAME,

                        RUNTIME_CLASS(CProjectNameDoc),

                        RUNTIME_CLASS(CMainFrame),       // main SDI frame window
                       RUNTIME_CLASS(CProjectNameView));
               AddDocTemplate(pDocTemplate);
               ......
            }

 위와 같은 코드를 발견할 수 있을 것이다.  바로 다음줄부터 다음과 같이 코드를 적어보자.

                CSingleDocTemplate* pDocTemplate;
                pDocTemplate = new CSingleDocTemplate(
                        IDR_MAINFRAME,

                        RUNTIME_CLASS(CProjectNameDoc),

                        RUNTIME_CLASS(CMainFrame),       // main SDI frame window
                       RUNTIME_CLASS(COtherView));
               AddDocTemplate(pDocTemplate);

 View를 하나더 추가 시켰다. (물론 COtherView는 만들어야 한다). 이제 프로그램을 실행시키면 처음부터 무엇을 묻는 다이얼로그가 뜰 것이다. 즉, 어떤 뷰를 사용하겠냐는 것이다. 마지막 라인인 AddDocTemplate()를 삭제하면 다이얼로그가 뜨지 않는다. Doc 역시 임의로 선택할 수 있다는 것을 알수 있을 것이고, Doc가 같고 View가 다르다면 같은 Doc를 사용할 수 있다.


  3. Multi-View 만들기

이제부터 Multi-View를 만들어 보자. 하지만 위와의 다른 방법으로 진행 할 것이다. 위 코드는 아래에서 Download할 수 있다. 아래는 지금 현재 글을 쓰고 있는 사람이 택한 것이므로 절대적인 것은 아님을 밝혀 둔다.

  1. App Wizard를 눌러 1단계에서 SDI를 선택한다, Language는 English를 선택한다.

  2. 2, 3단계를 통과한후 4단계에서 툴바 타입을 Internet Explorer Rebar로 선택,  Context-Senstive Help 체크, Print option 체크 해제한다.

  3. 5단계는 통과한 후, 6단계에서 CFormView 선택하고, View 이름을 CDynCaptureView로 한다. 이제 App Wizard를 Finish한다.

  4. 이제 하나의 뷰가 만들어 졌으므로 나머지 뷰를 만들어 보자.

  • Insert Dialog를 하여 적당한 ID를 주고, Class Wizard로 Class를 만든다. 이름은 CDynFilterView로 하고, CFormView에서 상속받는다.
  • 만들어진 CDynFiltersView의 Create()를 재정의 한다. 하지만 코드를 적을 필요는 없다. CFormView의 Create는 protected로 선언되어 있어 외부에서 접근이 안된다. 하지만 코딩 도중 Create() 호출하는 부분이 있다.
  • 만들어진 CDynFilterView의 Constructor의 접근자가 protected로 되어 있을 것이다. public으로 수정한다.
  • 위와 같은 과정을 2번더 반복하되, Class Name은 CStaCaptureView, CStaFilterView로 한다.

  5. MainFrame.h에 App Wizard에 의해 만들어진 View와 직접 만든 뷰의 헤더파일을 Include한다.

  6. MainFrame.h에 각 View의 멤버 변수를 정의한다.

    CDynCaptureView *m_pViewDynCapture;
    CDynFilterView *m_pViewDynFilter;
    CStaCaptureView *m_pViewStaCapture;
    CStaFilterView *m_pViewStaFilter;

  7.  CMainFrame::OnCreate()의 마지막 부분에 아래와 같이 추가한다.

    m_pViewDynCapture = NULL;
    m_pViewDynFilter = NULL;

    m_pViewStaCapture = NULL;
    m_pViewStaFilter = NULL;

  8. 메뉴에 Evnet를 추가한다. Class Wizard를 통해 각 Event 처리 Method를 MainFrame내에 정의한다.

  9. App Wizard가 만들어진 뷰가 선택되는 Event에는 다음 코드를 추가한다.

    GetActiveView()->ShowWindow(SW_HIDE);                  // Hide Active View (NOT MainView)
    SetActiveView(m_pViewDynCapture);                           // 메인뷰를 활성화 한다.
    m_pViewDynCapture->ShowWindow(SW_SHOW);      // 메인 View를 보여준다.


    // 메인 View를 첫번째 윈도우로 설정한다.
    m_pViewDynCapture->SetDlgCtrlID(AFX_IDW_PANE_FIRST);

    RecalcLayout();

10. 직접 만든 뷰가 보이도록 하는 Event에는 다음과 같이 추가한다.

    // TODO: Add your command handler code here
    CView *m_pView = GetActiveView();

    if(m_pViewDynCapture == NULL) m_pViewDynCapture = (CDynCaptureView*) m_pView;

   
    if (m_pViewDynFilter == NULL){

        m_pViewDynFilter = new CDynFilterView;

      CCreateContext context;                  

      //현재View의 토큐먼트를 받는다.

      context.m_pCurrentDoc = m_pView->GetDocument();

      //View를 만든다.

      m_pViewDynFilter->Create(NULL, NULL, 0L, CFrameWnd::rectDefault, this, 2, &context);      

      //화면에 업데이트한다.

      m_pViewDynFilter->OnInitialUpdate();

    }

    SetActiveView(m_pViewDynFilter);
    m_pViewDynFilter->ShowWindow(SW_SHOW);

    m_pView->ShowWindow(SW_HIDE);

    m_pViewDynFilter->SetDlgCtrlID(AFX_IDW_PANE_FIRST);

    RecalcLayout();

11. 컴파일 후 실행한다.