강몬드의 프로그래밍 이야기

단일 문서에 다중 뷰 추가 본문

카테고리 없음

단일 문서에 다중 뷰 추가

강몬드 2015. 6. 2. 17:15

MFC(Microsoft Foundation Class) 라이브러리로 만든 SDI(단일 문서 인터페이스) 응용 프로그램에서 각 문서 형식은 단일 뷰 형식과 연결되어 있습니다. 일부 경우 문서의 현재 뷰를 새 뷰로 전환하는 기능을 설정하는 것이 좋습니다.


이 기능은 CView에서 파생된 새 클래스와 뷰를 동적으로 전환하는 추가 코드를 기존 MFC 응용 프로그램에 추가하여 구현할 수 있습니다.

위 기능을 구현하는 단계는 다음과 같습니다.


이 항목의 나머지 부분에서는 다음과 같이 가정합니다.

  • public CWinAppEx에서 파생된 클래스 -> CMyWinApp
  • CFormView에서 파생된 클래스 -> CNewView


기존 프로그램 클래스 수정


뷰 간에 전환하는 응용 프로그램에서 뷰와 뷰 전환 메서드를 저장하려면 멤버 변수를 추가하여 응용 프로그램 클래스를 수정해야 합니다.

CMyWinApp 헤더에 다음 코드를 추가합니다.

CView* m_pOldView;
CView* m_pNewView;
CView* SwitchView( );


CMyWinApp cpp 소스를 열고 include 섹션에 다음 줄을 삽입합니다.

#include <AFXPRIV.H>


새 뷰 클래스 만들기 및 수정


클래스 위자드를 사용하면 편리하게 새 뷰 클래스를 만들 수 있습니다.

솔루션탐색기에서 우클릭하여 클래스위자드 실행 또는 명령어 : Ctrl + Shift + X

이 클래스에 대한 유일한 요구 사항은 CFormView에서 파생되어야 한다는 것입니다. 이 새로운 클래스를 추가합니다. 

프로젝트에 클래스를 추가한 후 일부 뷰 클래스 멤버의 액세스 가능성을 변경해야 합니다.

생성자와 소멸자에 대한 액세스 지정자를 protected에서 public로 변경하여 수정합니다. 이렇게 하면 클래스가 동적으로 생성 및 소멸되고 뷰가 표시되기 전에 뷰의 모양을 수정할 수 있습니다.

변경 내용을 저장하고 다음 단계를 계속합니다.


Add Class 버튼을 클릭.

위와 같이 BaseClass를 CFormView로 입력하여 상속받고 클래스명은 임의로 지정.


새 뷰 만들기 및 연결

새 뷰가 만들어졌고 연결하려면 프로젝트 CMyWinApp 클래스의  InitInstance 함수를 수정해야 합니다. 이 함수를 수정하면 새 뷰 개체를 만드는 새 코드를 추가한 다음 두 개의 기존 뷰 개체를 사용하여 m_pOldView  m_pNewView 모두를 초기화합니다.

새 뷰는 InitInstance 함수 내에 만들어지므로 새 뷰와 기존 뷰는 응용 프로그램이 소멸될 때까지 지속됩니다. 그러나, 응용 프로그램에서는 간단하게 새 뷰를 동적으로 만들 수 있습니다.

ProcessShellCommand를 호출한 후 다음 코드를 삽입합니다.

...
CView* pActiveView = ((CFrameWnd*) m_pMainWnd)->GetActiveView();
m_pOldView = pActiveView;
m_pNewView = (CView*) new CNewView;

CDocument* pCurrentDoc = ((CFrameWnd*)m_pMainWnd)->GetActiveDocument();

// Initialize a CCreateContext to point to the active document.
// With this context, the new view is added to the document
// when the view is created in CView::OnCreate().
CCreateContext newContext;
newContext.m_pNewViewClass = NULL;
newContext.m_pNewDocTemplate = NULL;
newContext.m_pLastView = NULL;
newContext.m_pCurrentFrame = NULL;
newContext.m_pCurrentDoc = pCurrentDoc;

// The ID of the initial active view is AFX_IDW_PANE_FIRST.
// Incrementing this value by one for additional views works
// in the standard document/view case but the technique cannot
// be extended for the CSplitterWnd case.
UINT viewID = AFX_IDW_PANE_FIRST + 1;
CRect rect(0, 0, 0, 0); // Gets resized later.

// Create the new view. In this example, the view persists for
// the life of the application. The application automatically
// deletes the view when the application is closed.
m_pNewView->Create(NULL, "AnyWindowName", WS_CHILD, rect, m_pMainWnd, viewID, &newContext);

// When a document template creates a view, the WM_INITIALUPDATE
// message is sent automatically. However, this code must
// explicitly send the message, as follows.
m_pNewView->SendMessage(WM_INITIALUPDATE, 0, 0);
...

변경 내용을 저장하고 다음 단계를 계속합니다.


전환 함수 구현

이전 단계에서 새 뷰 개체를 만들고 초기화하는 코드를 추가하였습니다. 마지막 작업은 전환 메서드 SwitchView를 구현하는 것입니다.

응용 프로그램 CMyWinApp 클래스의 구현 파일 끝에 다음 메서드 정의를 추가합니다.

CView* CMyWinApp::SwitchView( )
{
   CView* pActiveView =
      ((CFrameWnd*) m_pMainWnd)->GetActiveView();

   CView* pNewView= NULL;
   if(pActiveView == m_pOldView)
      pNewView= m_pNewView;
   else
      pNewView= m_pOldView;

   // Exchange view window IDs so RecalcLayout() works.
   #ifndef _WIN32
   UINT temp = ::GetWindowWord(pActiveView->m_hWnd, GWW_ID);
   ::SetWindowWord(pActiveView->m_hWnd, GWW_ID, ::GetWindowWord(pNewView->m_hWnd, GWW_ID));
   ::SetWindowWord(pNewView->m_hWnd, GWW_ID, temp);
   #else
   UINT temp = ::GetWindowLong(pActiveView->m_hWnd, GWL_ID);
   ::SetWindowLong(pActiveView->m_hWnd, GWL_ID, ::GetWindowLong(pNewView->m_hWnd, GWL_ID));
   ::SetWindowLong(pNewView->m_hWnd, GWL_ID, temp);
   #endif

   pActiveView->ShowWindow(SW_HIDE);
   pNewView->ShowWindow(SW_SHOW);
   ((CFrameWnd*) m_pMainWnd)->SetActiveView(pNewView);
   ((CFrameWnd*) m_pMainWnd)->RecalcLayout();
   pNewView->Invalidate();
   return pActiveView;
} 

변경 내용을 저장하고 다음 단계를 계속합니다.


뷰 전환 지원 코드 추가

마지막 단계에서는 응용 프로그램에서 뷰 간에 전환해야 할 경우 SwitchView 메서드를 호출하는 코드를 추가합니다. 뷰 간의 전환은 여러 가지 방법으로 수행할 수 있습니다. 사용자가 선택하는 새 메뉴 항목을 추가할 수도 있고 일정한 조건을 만족하는 경우 내부적으로 뷰를 전환할 수도 있습니다.


새로운 뷰에 버튼을 하나 추가하고 버튼 입력 메세지 함수를 아래와 같이 수정합니다.

버튼 입력시 뷰 전환 함수를 호출하여 뷰가 전환됩니다.

void NewView::OnBnClickedButton1()

{

// TODO: Add your control notification handler code here

Cformview_v1App *pApp = (Cformview_v1App*)AfxGetApp(); //App구함


pApp->SwitchView();

}





Comments