순간이 영원해 지는 곳

MDI에서 사용자 정의 뷰를 가지는 차일드 윈도우 만들기 본문

MFC 프로그래밍

MDI에서 사용자 정의 뷰를 가지는 차일드 윈도우 만들기

nenunena 2009. 8. 15. 13:41


MDI 프로젝트에서 새 문서를 만들면 하얀 바탕에 아무것도 없는 기본 뷰를 가지는 차일드 윈도우가 생성된다.

내가 원하는 뷰를 가지는 차일드 윈도우를 생성하는 방법에 대해 정리하려고 한다.

Visual Studio 6.0 / Visual Studio 2008 에서 모두 사용되는 코드이다.



프로젝트명.cpp 파일을 보면
BOOL C프로젝트명App::Initinstance() 멤버 함수가 있다.

* 중간에 CMultiDocTemplate* pDocTemplate; 이라는 지역변수가 있는데 이 변수는 삭제하고 아래와 같은 멤버 변수를 만든다.

CMultiDocTemplate* m_pDocTemplate;


이 변수는 기본 MDI프로젝트에서 새 문서를 누르면 만들어지는 기본틀을 정의한다.

* 그리고 나서 다시 함수로 돌아와서 new로 생성된 객체를 가리키는 포인터를 멤버변수로 바꾼다.

    m_pDocTemplate = new CMultiDocTemplate(IDR_ManagerTYPE,
        RUNTIME_CLASS(CManagerDoc),
        RUNTIME_CLASS(CChildFrame),                // 사용자 지정 MDI 자식 프레임입니다.
        RUNTIME_CLASS(CManagerView));
    if (!m_pDocTemplate)
        return FALSE;
    AddDocTemplate(m_pDocTemplate);

여기서 Manager는 내가 만든 프로젝트 명이다.

CMultiDocTemplate 생성자의 인자에 대해 설명하면

첫번째 IDR_ManagerTYPE은 리소스로써 메뉴나 아이콘등 해당 차일드 윈도우에서 사용할 리소스들이다.
템플릿이 여러개인 경우 이 리소스는 서로 다른 것을 사용하여야 한다.
다른 템플릿이 같은 리소스를 사용하는 경우 새 문서를 만들때 어떤 템플릿을 사용할 것인지 고르는 창이 뜬다.

두번째인자부터 사용된 RUNTIME_CLASS() 매크로는 괄호 안에들어간 클래스를 new 가 아닌 다른 방식으로 동적 생성하기 위한 것이다.
이 부분에 관한 자세한 설명은 다른 곳을 참조하기 바란다.

두번째 인자는 차일드 윈도우생성시에 같이 생성되는 도큐먼트 클래스를 넣어주면 된다.

세번째는 프레임 클래스, 네번째는 뷰 클래스이다.

두 번째부터 세번째 클래스를 사용자 정의 클래스로 바꿔주면 된다. (물론 이게 끝은 아니다.)
이때 사용자 정의 클래스는 CDocument, CMDIFrameWnd, CView 클래스를 상속받은 클래스여야 한다.

AddDocTemplate() 함수는 생성한 템플릿을 템플릿 목록에 추가하는 함수이다.

따라서 사용자가 생성할 차일드 윈도우의 뷰가 여러개인 경우 여러개의 CMultiDocTemplate 객체를 생성하여
AddDocTemplate() 함수로 추가해 주면 된다.

* 나는 기본 템플릿 외에 추가로 리스트뷰를 가지는 차일드 윈도우를 생성하는 템플릿을 추가하였다.

m_pHostListViewTemplate = new CMultiDocTemplate( IDR_HostListTYPE,
        RUNTIME_CLASS( CManagerDoc ),
        RUNTIME_CLASS( CChildFrame ),
        RUNTIME_CLASS( LTestView )
        );
    if( ! m_pHostListViewTemplate )
        return FALSE;
    AddDocTemplate( m_pHostListViewTemplate );

우리가 원하는 것은 뷰를 바꾸고 싶은 것이므로 리소스와 뷰만 다른 클래스로 대체하였다.

* 사용자 정의 뷰 클래스 헤더파일 ; 여기선 LTestView.h 파일을 열었다.

class LTestView :    public CListView
{
protected:
    LTestView(void);
    DECLARE_DYNCREATE(LTestView)
    ~LTestView(void);
}

부모 클래스로 CListView를 사용하였다. CListView는 CView를 상속받은 CCtrlView클래스를 부모로 한다.
CListView도 CView의 자식 클래스라는 말이다.

MSDN에서는 차일드 윈도우의 뷰 클래스는 new가 아닌 다른 방식으로 동적생성하기 때문에 뷰클래스를 직접생성할 수 없도록 생성자는 protected로 하는 것을 추천하고 있다.

* DECLARE_DYNCREATE() 함수는 CreateObject() 함수를 정의하는 매크로이다. 이것도 동적생성과 관련이 있다. 괄호안에 사용자 정의 뷰 클래스 이름을 넣는다. 이 부분을 추가해야 한다.

* 그리고 LTestView.cpp 파일을 열고 #include 아래쯤에 아래와 같은 부분을 추가한다.


#include "StdAfx.h"
#include "LTestView.h"

IMPLEMENT_DYNCREATE(LTestView, CListView)

위에서 말한 CreateObject() 함수 구현 부분을 추가하는 매크로이다.

첫번째 인자로는 사용자 정의 뷰 클래스를, 오른쪽에는 상속받은 부모 뷰 클래스를 적는다.
위에서 말했듯이 부모 클래스는 CView이거나 CView를 상속받은 클래스여야 한다.

나는 리스트뷰를 초기화 하기 위해 LTestView클래스의 OnCreate() 메시지 함수를 정의하여 리스트 컨트롤에 열을 집어넣는 등에 작업을 추가하였다.

int LTestView::OnCreate(LPCREATESTRUCT lpCreateStruct)
{
   ...
   CListCtrl &listCtrl = GetListCtrl();
   ...
   listCtrl.InsertColumn(0, "IP Address", LVCFMT_CENTER, 110, 0 );
   ...
}


자, 이제 사용자 정의 뷰를 가지는 차일드 윈도우를 생성하기 위한 준비는 끝났다.
이제 생성하는 일만 남았다.

* 새로운 도큐먼트를 생성하여 차일드 윈도우를 만드는 경우 아래와 같은 코드를 이용하면 된다.

CDocTemplate *pTemplate = theApp.m_pHostListViewTemplate;
pTemplate->OpenDocumentFile( NULL );

theApp는 전역 변수로 프로그램 자체를 나타낸다. 우리는 theApp의 멤버변수로 템플릿을 만들었으므로 위에서 처럼 템플릿을 가져와서 OpenDocumentFile()함수를 호출하면 된다. 첫번째 인자는 open할 파일명인데 NULL을 집어넣으면 새로운 파일을 만들겠다는 뜻이다.

OpenDocumentFile() 함수 안에서는 템플릿 생성시 지정한 리소스, 도큐먼트, 프레임, 뷰클래스를 가지고CreateNewDocument() 함수와 CreateNewFrame()함수를 호출하여 새로운 도큐먼트와 프레임, 뷰를 만든 뒤 InitialUpdateFrame() 함수를 호출하여 차일드 윈도우를 초기화하고 활성화 시킨다.

CreateNewFrame() 함수호출시 WM_CREATE 메시지가 발생 되는데 이때 CFrameWnd::OnCreate() 함수가 호출되어 뷰를 생성한다.


* 기존에 생성된 도큐먼트 객체를 그대로 사용하는 뷰를 생성하는 경우에는 생성된 도큐먼트 객체의 포인터를 이용한다. (도큐먼트 하나에 대해 여러개의 뷰 생성)

BOOL CManagerDoc::OnNewDocument()
{
    ...

    CDocTemplate *pTemplate = theApp.m_pHostListViewTemplate;
    CDocument *pDocument = this; // GetActiveDocument();
    CFrameWnd* pFrame = pTemplate->CreateNewFrame( pDocument, NULL );
    if( pFrame == NULL )
    {
        TRACE0( "Warning: failed to create new frame.\n" );
        return -1;
    }
    ASSERT_KINDOF( CFrameWnd, pFrame );

    pTemplate->InitialUpdateFrame( pFrame, pDocument );

    ...
}

템플릿을 가져와서 OpenDocumentFile()함수가 아닌 CreateNewFrame() 함수를 직접 호출하여 기존에 있던 도큐먼트 객체와 연결된 차일드 윈도우(프레임,뷰)를 만든다.

여기서는 새로운 도큐먼트 객체가 열릴 때 그 객체와 연결된 다른 뷰를 생성하였다.

실행 결과, 프로그램 시작시 기존 하얀 뷰와 함께 새롭게 정의한 리스트뷰 차일드 윈도우가 생성된다.

사용자 삽입 이미지

차일드 윈도우의 제목표시줄을 보면 프로젝트명1:1, 프로젝트명1:2 이런식으로 되어 있는데

1:1은 첫번째 도큐먼트의 첫번째 뷰, 1:2는 첫번째 도큐먼트의 두번째 뷰를 말한다.

새문서를 눌러 도큐먼트를 새로 생성하는 경우, 프로젝트명2 라고 표시된다.


Comments