다중 View
다중 View란 한개의 도큐먼트에 여러개의 View를 설정하는 것입니다. 간단한 예로 메뉴에서 특정항목을 설정하면 CFormView 형태로 전환되었다가 또다시 CView 형태로 전환되는 형태를 말합니다. 다중 View는 의미적으로 고급에 속하는 기법입니다. 그러나 프로그래밍은 그렇게 어렵지 않습니다. 또한 CCreateContext 클래스를 설명한 이상 다중 View까지 설명하면 일거 양득이 되지 않을까 하는 생각입니다.
다중 View를 만드는 방법
현재 도큐먼트에 새로운 View를 만들어서 대입코자한다면 CCreateContext를 사용해야 합니다. CCreateContext는 현재의 View와 도큐먼트의 정보를 가지고 있다고 설명하였습니다. View를 윈도우를 만들때 즉 Create할때 현재 설정되어 있는 도큐먼트이 정보를 CCreateContext에 넣고 (View는 새로 교채 해야 하기 때문에 넣을 필요는 없습니다) 이것을 Create인자로 넣어주면 현재 도큐먼트와 연결되는 View를 만들수 있습니다.
전항목에서 CCreateContext는 현재 있는 View와 도큐먼트 정보를 얻은다음 여기에 View만 교체하는 방식으로 새로운 View를 도큐먼트와 연결시키고 이것을 분할윈도우에 설정하였습니다. 다중View는 여러개의 View를 만드는 것이고 이 여러개의 View는 같은 도큐먼트를 갖고 있는 것입니다. 만일 여러개의 View가 서로 다른 도큐먼트를 가진다면 이거은 다중 View가 아니라 다중 템플레이트 입니다. 본항목에서는 다중 템플레이트가 아닌 다중View를 설명하는 것입니다. 예를 들어서
현재 프로젝트명이 EMxMulView이고 이곳에 AnotherView를 다중뷰로 설정하고자 한다면 다음과 같이 할수 있습니다.
CCreateContext context;
CAnotherView *pView;
:
context.m_pCurrentDoc=GetActiveDocument();
pView->Create(NULL,NULL,0L,CFrameWnd::rectDefault,this,2,
&context);
CCreateContext형의 context변수를 설정하고 이변수에 현재 설정되어 있는 도큐먼트를 받습니다. 이 context를 Create의 마지막인자로 넘겨주는 것입니다.
위와 같이하면 현재의 도큐먼트를 받은 또하나의 View를 만들수가 있습니다. 이렇게 만든 뷰를 화면에 출력하기 위해서는 OnInitialUpdate 함수를 이용합니다.
pView->OnInitialUpdate();
특정 View를 활성화 한다.
전항목처럼 프로젝트안에 두개의 View를 선택하였다면 이둘중 한개만 화면에 출력되어야 합니다. (만일 두개가 다출력된다고 한다면 이것은 분할윈도우를 사용하는것이 되는것이고 현재는 한개의 View만 출력되는 형태이기 때문에)
예를 들어서 설명하겠습니다.
MExMulView라는 프로젝트에 CAnotherView라는 하나의 View를 설정하게 되면 CMExMulViewView와 CAnotherView 두개의 클래스가 나오게 됩니다. 프로그램이 처음 실행될때는 CMExMulViewView가 설정될것입니다. 여기에서 새로운 CAnotherView를 설정하고자 한다면 우선적으로 전항목처럼 새로운 View를 만든다음에 이View를 활성화 시킵니다.
CAnotherView *pView;
://pView를 Create시킨다.
SetActiveView(pView);//pView를 활성화 한다.
pView->ShowWindow(SW_SHOW);//pView 를 화면에 출력한다.
위와 같이하면 현재는 CAnotherView가 화면에 보여 집니다. 또한 기존에 설정되어 있는 CMExMulViewView또한 화면에 보여집니다. 기존에 있는 View 는 화면에서 감추어져야 함으로 ShowWindow함수를 이용하여 화면에서 사라지게 합니다.
CMExMulViewView *m_pView;
m_pView=GetActiveView();
m_pView->ShowWindow(SW_HIDE);
지금까지 한방법으로 현재의 View에서 새로운 View를 설정할수가 있는 것입니다.
다중View 예제 MExMulView
MExMulView라는 하나의 프로젝트를 만들어서 다중뷰를 만드는방법을 알아봅시다. 먼저 프로젝트를 만들때 SDI이며 현재의 View가 CFormView이 형태로 만듭니다. (꼭 CFormView로 만드셔야 합니다. Step6에서 꼭 CFormView를 선택하세요)
처음에 CFormView를 설정하고자 하는 의미는 CFormView에서 마우스 클릭에 의해서 CView로 전환을 하고자 하는 프로그램을 제작하기 위해서 입니다.
이와 같이 프로젝트를 만든후에 CAnotherView라는 클래스를 만듭니다. 이클래스는 CView를 base class로 만듭니다.
“새로운 윈도우를 만들거나 또는 도큐먼트와 함께 다중뷰를 한다!” 라고 했을경우 이프로그램은 어디에서 작성을 해야 할까요? 한번 생각해 보시기 바랍니다.
정답은 메인프레임입니다. 위에서 분할윈도우를 작성하는 방법은 메인프레임에서 설정한것과 같은 의미입니다.
CAnotherView를 화면에 보이거나 또는 CExMulViewView를 화면에 보이게 하기 위해서는 이 두개의 View를 핸들링하는 두개의 변수가 필요합니다. 하나는 CAnotherView형의 변수이며 또하나는 CExMulViewView형의 변수입니다. 이변수를 메인프레임에 다음과 같이 설정합니다.
CMExMulViewView *m_pMainView;
CAnotherView *m_pAnotherView;
이제 메인프레임에 ShowAnotherView 라는 함수를 하나 만듭니다. 이함수의 내용은 다음과 같습니다.
void CMainFrame::ShowAnotherView()
{
//현재 View를 받는다.
m_pMainView=(CMExMulViewView *)GetActiveView();
//m_pAnotherView가 NULL 이면 View를 Create하지 않았으므로 새로만든다.
if(!m_pAnotherView)
{
//context에 현재 도큐먼트를 받는다.
CCreateContext pContext;
pContext.m_pCurrentDoc=GetActiveDocument();
//CView형의 m_pAnotherView를 만든다.
m_pAnotherView = new CAnotherView;
m_pAnotherView->Create(NULL,NULL,0L,CFrameWnd::rectDefault,this,
2,&pContext);
//Update한다.
m_pAnotherView->OnInitialUpdate();
}
//m_pAnotherView를 활성화하고
SetActiveView(m_pAnotherView);
//이전 뷰를 감추고
m_pMainView->ShowWindow(SW_HIDE);
//현재 뷰를 보여준다.
m_pAnotherView->ShowWindow(SW_SHOW);
//현재 뷰의 컨트롤 아이디 설정
m_pAnotherView->SetDlgCtrlID(AFX_IDW_PANE_FIRST);
//프레임 갱신
RecalcLayout();
}
ShowAnotherView() 함수는 현재 View대신에 새로운 View를 설정하는 함수입니다. 최우선적으로 해야 할일이 바로 이전View를 받아두는 것입니다. 첫줄에서 m_pMainView 바로 현재의 View를 받아둡니다.
m_pAnotherView는 생성자에 우선 다음과 같이 초기화가 되어 있어야 합니다.
CMainFrame::CMainFrame()
{
// TODO: add member initialization code here
m_pAnotherView=NULL;//다른 View를 초기화
}
위와 같이 되어 있을때 처음에 ShowAnotherView 함수를 호출하면 m_pAnotherView가 NULL 이므로 if(!m_pAnotherView) 문 안의 내용을 수행하면서 새로운 View윈도우를 만듭니다.
이렇게 만든 윈도우를 활성화시키고 이전 View를 감추고 현재의 View를 활성화 시킵니다. 여기까지 하면 화면에 보일것 같으나 두가지 작업이 남아있습니다. 첫번째 작업은 바로 컨트롤 ID를 설정하는 것입니다. 새로운 View로 교채 되면 메뉴에서 선택한 메시지나 여러 컨트롤 메시지가 새로 설정된 View로 날아가야 합니다. 모든 컨트롤들의 포커스를 받기 위해서는 포커스를 받는 ID 를 설정해 주어야 합니다. 이 ID값은 AFX_IDW_PANE_FIRST MFC에 설정되어 있습니다. ShowAnotherView() 의 끝에서 두번째 줄의 SetDlgCtrlID 함수에 이 ID를 설정하고 실행하면 모든 컨트롤메시지가 View로 전송되게 됩니다. 분할윈도우에서도 분할윈도우를 만들때 이 ID를 사용했다는 것을 기억하시기 바랍니다. 이렇게 새로운 설정이 끝나면 RecalcLayout()함수를 실행시킵니다. 이함수의 기능은 프레임에 있는 모든 윈도우들의 정보를 갱신하고 새로이 출력하는 기능을 가지고 있습니다. 즉 프레임에 툴바가 수정되었다든지 또는 View자체에 어떤 변화가 있을경우에는 메인프레임은 그에 맞게끔 변화가 있어야 합니다. 이때 RecalcLayout()함수를 이용하는 것입니다. 지금의 경우는 View자체가 바뀌게 된것입니다. View자체가 바뀌게 되면 View에 설정되어 있는 컨트롤 긴능도 바뀌게 됩니다. 예를 들어 첫번째 View의 화면은 (100,100)이었는데 대치되는 두번째 View가 (200,200)이라면 메인프레임은 이 View를 감싸기 위해서 크기가 바뀌어야 합니다.이런것을 자동적으로 처리해주기 위해서 RecalcLayout()함수가 사용되는 것입니다.
위의 소스에서 다음과 같은 문장이 있을것입니다.
m_pAnotherView = new CAnotherView;
CView에 의한 새로운 클래스 를 만들고 위와 같이하고 컴파일 하면 에러가 발생됩니다. 그이유는 CAnotherView::CAnotherView() 함수, 즉 생성자 함수가 protected 에 설정되어 있기 때문입니다. 이함수를 public로 옴겨놓아야 위의 문장이 컴파일 될수 있습니다.
이번에는 원래의 View즉 처음에 출력된 FormView 로 돌아가는 함수를 만들어 보겠습니다. 이함수는 다음과 같습니다.
void CMainFrame::ShowMainView()
{
SetActiveView(m_pMainView);
m_pMainView->ShowWindow(SW_SHOW);
m_pAnotherView->ShowWindow(SW_HIDE);
m_pMainView->SetDlgCtrlID(AFX_IDW_PANE_FIRST);
RecalcLayout();
}
ShowMainView는 ShowAnotherView와 같은방법으로 m_pMainView 를 활성화 하고 보여주고 이전 View를 감추고 컨트롤 ID를 설정하고 RecalcLayout()함수를 실행했습니다. ShowMainView함수의 내용은 ShowAnotherView의 내용을 참조하시면 금방 이해가 될것입니다. ShowMainView에서는 윈도우를 만드는 어떤 내용도 들어가 있지 않습니다. 그이유는 ShowMainView가 실행될려고 한다면 먼저 ShowAnotherView가 실행되어야 하고 ShowAnotherView가 실행되면 이곳에서 m_pMainView는 현재의 View를 받게 되고 m_pAnotherView는 새로 만들어 지기 때문입니다.
이제 각뷰에서 메인프레임에 있는 두개의 함수를 호출하는 함수를 만들어 보겠습니다. 먼저 CExMulViewView에서 좌측 마우스 버튼을 클릭했을때의 OnRButtonDown 함수를 만듭니다. 이함수에서 메인프레임의 ShowAnotherView함수를 호출합니다.
void CMExMulViewView::OnRButtonDown(UINT nFlags, CPoint point)
{
((CMainFrame *)AfxGetMainWnd())->ShowAnotherView();
CFormView::OnRButtonDown(nFlags, point);
}
좌측 마우스 버튼을 클릭하면 메인프레임에 있는 ShowAnotherView를 호출하여서 현재의 View를 CAnotherView로 전환하는 것입니다. 이렇게 되면 CMExMulViewView는 비활성화 되고 CAnotherView가 활성화 되는 것입니다.
이제 CAnotherView에서 좌측 마우스 버튼을 클릭했을때의 함수 OnRButtonDown을 만듭니다.
void CAnotherView::OnRButtonDown(UINT nFlags, CPoint point)
{
// TODO: Add your message handler code here and/or call default
((CMainFrame *)AfxGetMainWnd())->ShowMainView();
CView::OnRButtonDown(nFlags, point);
}
현재 View가 CAnotherView이므로 이상태에서 전환을 하여서 CExMulViewView로 전환하는 메인프레임의 ShowMainView를 호출한것입니다.
위와 같이하고 컴파일할때 주의점이 있습니다. 지금까지 배워본 독자분들중에 이런 실수를 하지 않을수도 있지만 혹이 실수하여 컴파일하였는데 에러가 발생되는 경우가 있을것입니다. 바로 #include문제입니다.(이문제가 웹서버에 질문이 매우 많이 들어옵니다.)
각 클래스에서 #include해야 하는 것은 다음과 같습니다.
//MExMulView.cpp에 포함해야 할 헤더들
#include "stdafx.h"
#include "MExMulView.h"
#include "MExMulViewDoc.h"
#include "MExMulViewView.h"
#include "AnotherView.h"
#include "MainFrm.h"
//MainFrm.cpp에 포함해야 할 헤더들
#include "stdafx.h"
#include "MExMulView.h"
#include "MExMulViewDoc.h"
#include "MExMulViewView.h"
#include "AnotherView.h"
#include "MainFrm.h"
//MExMulViewView.cpp에서 포함해야 할 헤더들
#include "stdafx.h"
#include "MExMulView.h"
#include "MExMulViewDoc.h"
#include "MExMulViewView.h"
#include "AnotherView.h"
#include "MainFrm.h"
//AnotherView.cpp에서 포함해야 할 헤더들
#include "stdafx.h"
#include "MExMulView.h"
#include "MExMulViewDoc.h"
#include "MExMulViewView.h"
#include "AnotherView.h"
#include "MainFrm.h"
헤더들을 포함시킬때 보면 MainFrm.h위에는 꼭 AnotherView와 ExMulViewView가 그리고 이것과 쌍인 ExMulViewDoc가 설정되어 있습니다. 이이유는 메인프레임 클래스 맴버로 CMExMulViewView형인 m_pMainWnd와 CAnotherView의 형인 m_pAnotherView가 있기 때문입니다. MainFram.h를 인식할때 두개의 클래스형을 찾지못하면 컴파일 에러가 발생합니다. 그렇기 때문에 위와 같이 기록한것입니다.
혹이 MainFrm.h다음에 AnotherView.h나 또는 MExMulViewView.h를 설정하면 컴파일시 에러가 발생됩니다. 꼭 위와같이 순서적으로 #include하여야 합니다. 왜그런지는 여러분이 생각해 보세요. 아주 기본적인것이면서도 빼먹는 내용중에 하나입니다.!
이제 컴파일하고 실행하면 그림 17과 같은 화면이 출력됩니다. 이때 우측 마우스 버튼을 클릭하면 CFormView가 CView로 전환 되는 그림 18과 같은 화면이 나타납니다.
(그림 17)MExMulView의 실행화면
(그림 18)MExMulView에서 우측 마우스 버튼을 클릭했을때의 화면
MExMulView와 같이 다중뷰를 쓰는것은 주로 CD-ROM타이틀 윈도우에서 많이 사용됩니다. 또는 print기능을 이용항 OnDraw함수를 호출할경우에도 이 기능을 이용합니다. CFormView에서는 프린터를 할경우 내용이 프린터 되지 않습니다. 그러나 CView에서는 OnDraw함수를 이용하여 프린터가 가능합니다. 처음에 프로그램은 CFormView상태로 놓았다가 프린터할때만 임시적으로 CView로 전환하여 프린터하고 다시 CFromView로 전환하는 방식도 프린팅을 할때 그래도 유용한 방법이기 때문입니다.
# by | 2008/06/08 16:15 | MFC | 트랙백 | 덧글(1)





☞ 내 이글루에 이 글과 관련된 글 쓰기 (트랙백 보내기) [도움말]
안녕하세요.
다중뷰 글을 일고 이렇게 글을 씁니다.
다름이 아니오라. 위에서 언급된 방법으로 다중뷰를 생성해서 테스트해보니,
WM_SIZE메세지가 메인뷰로는 들어가질 않고 계속 AnotherView로만 넘어가네요.
처음에는 메인뷰에 메세지가 넘어가다가 AnotherView를 활성화했다가 다시 메인뷰를 활성화해서 하면, 메세지가 넘어가질 않네요.
왜 그런지 아시는지.......
지금 이문제 때문에 삽질중입니다.
알고 계시면 도움을 부탁드립니다.