MainFrame Class, View Class 참조
CMainFrame* pFrame = (CMainFrame*)AfxGetMainWnd();
CCeSerialView* pView = (CCeSerialView*)pFrame->GetActiveView();
위와같이 참조하기 위해선 메인프레임, 도큐먼트, 뷰 헤더파일 인클루드
#include "CeSerial.h"
#include "MainFrm.h"
#include "CeSerialDoc.h"
#include "CeSerialView.h"
App 헤더파일을 인클루드 하지않으면 아래와 같은 에러 발생
error C2065: 'IDD_CESERIAL_FORM' : undeclared identifier
error C2057: expected constant expression
CTestSendDlg *p_Dlg = (CTestSendDlg*)AfxGetMainWnd(); 임의클래스에서 다이얼로그 참조
------------------------------------------------------------------------------------------------
MFC로 프로그래밍을 하다 보면, 각 클래스에 어떻게 접근해서 포인터를 얻어와야 하는지 매우 어려울 때가 많다... 특히 초중급자 시절엔 말이다.. 사용한 지 오래 되어도 여전히 헷갈리기는 매 한가지다.자 그럼.. 하나씩.. 이해해 보자.
우선 MFC로 프로그램을 만들면, 다음과 같이 클래스가 생성된다. 프로젝트 명을 Test라고 가정해 보자.
CTestApp - CWinApp 클래스를 상속, 프로그램 초기화 클래스 (InitInstance)
CMainFrame - CFrameWnd 클래스를 상속, 전체 윈도우 관련 클래스(툴바, 메뉴, 상태바 등등)
CTestDoc - CDocument 클래스를 상속, 문서 관련 클래스(Open, Save, Serialize)
CTestView - CView 클래스를 상속, 사용자 화면 클래스(OnPaint, OnDraw)
CAboutDlg - CDialog 클래스를 상속, 도움말 대화 상자 클래스(DoModal)
------------------------------------------------------------------------------------------------
우선 가장 쉬운 것부터 설명하자면,
어느 곳에서나 CTestApp의 포인터를 얻고자 한다면, 다음과 같이 코딩한다.
AfxGetApp()는 전역 함수이므로 어느 곳에서나 호출할 수 있다.
CTestApp* pApp = AfxGetApp();
하지만 위와 같이 사용하면, 다음과 같은 에러가 발생한다.
error C2440: 'initializing' : cannot convert from 'class CWinApp *' to 'class CTestApp *'
에러를 보면, CWinApp*를 CTestApp*로 변환할 수 없다는 것인데, AfxGetApp()의 함수 원형이
CWinApp* AfxGetApp();
이기 때문이다. 그래서 이 문제를 해결하기 위해서는 아래와 같이 형변환(casting)을 해 주어야 한다.
CTestApp* pApp = (CTestApp*)AfxGetApp();
이제는 컴파일 에러 없이 잘 될 것이다. 그리고 다음과 같은 에러가 발생될 수도 있느데,
error C2065: 'CTestApp' : undeclared identifier
이는 CTestApp 클래스의 선언 부분이 포함(include)되지 않았기 때문이다. 이럴 때는
CTestApp 클래스의 선언을 다음처럼 포함시켜야 한다.
#include "Test.h"
pApp는 이미 생성되어 있는 CTestApp의 객체 포인터이기 때문에,
이젠 pApp 포인터를 통해 CTestApp 객체에 쉽게 접근할 수 있다. 만약에 CTestApp에
int i;
라는 멤버 변수가 있었다면,
pApp->i = 5;
와 같이 사용할 수 있다. 또한 멤버 함수 func()가 있다면,
pApp->func();
처럼 접근할 수 있다. 주의해야 할 것은 객체 접근이기 때문에 public 속성의 멤버들만 접근할 수 있고, private와 protected는 접근할 수 없음에 유의해야 한다.
------------------------------------------------------------------------------------------------
두 번째는 CMainFrame의 포인터을 얻어 오는 방법이다. 다음과 같이 사용하려 할 때,
CMainFrame* pFrame = AfxGetMainWnd();
두 가지 에러와 만나게 된다.
첫 번째, CMainFrame에 대해서 컴파일러가 모르겠다는 에러이다.
error C2065: 'CMainFrame' : undeclared identifier
이 에러를 수정하기 위해서는 CMainFrame 클래스가 선언되어 있는 헤더 파일을
다음처럼 포함시키면 된다. 그리고 포함시키는 위치도 Test.h와 TestDoc.h 사이가 좋다.
그것은 클래스들간에 서로 관계가 있기 때문에 포함 순서는 매우 중요하다.
#include "stdafx.h"
#include "Test.h"
#include "MainFrm.h"
#include "TestDoc.h"
#include "TestView.h"
이 에러를 해결하고 나면, 다음과 같이 CTestApp에서 발생했던 에러가 발생한다.
error C2440: 'initializing' : cannot convert from 'class CWnd *' to 'class CMainFrame *'
에러를 보면, CWnd*를 CMainFrame*로 변환할 수 없다는 것인데, AfxGetMainWnd()의 함수 원형이
CWnd* AfxGetMainWnd();
이기 때문이다. 그래서 이 문제를 해결하기 위해서는 아래와 같이 형변환(casting)을 해 주어야 한다.
CMainFrame* pFrame = (CMainFrame*)AfxGetMainWnd();
이제는 컴파일 에러 없이 잘 될 것이다. CMainFrame은 CFrameWnd를 상속 받고, CFrameWnd는
CWnd를 상속 받기 때문에, pFrame 포인터를 통해서 CFrameWnd와 CWnd가 갖고 있는 모든 함수를
호출하여 사용할 수 있다. 만약 윈도우의 타이틀에 나오는 제목을 바꾸고 싶다면,
pFrame->SetWindowText( "야.. 바뀌어라" );
처럼 사용하면 된다.
------------------------------------------------------------------------------------------------
CMainFrame*는 또 다른 이유로 자주 사용하게 되는데, 그 이유는 도큐먼트(CTestDoc)와
뷰(CTestView) 클래스에 접근하기 위해서이다. CMainFrame 클래스는 뷰에 접근하는 GetActiveView(); 함수와
문서에 접근하는 GetActiveDocument() 함수를 제공한다.
우리가 어느 특정 클래스를 만들어서 사용하고, 이 클래스가 뷰에 접근해야 한다면 다음과 같은 문장을 사용해야 한다.
CMainFrame* pFrame = (CMainFrame*)AfxGetMainWnd();
CTestView* pView = (CTestView*)pFrame->GetActiveView();
만약 도큐먼트 클래스에 접근해야 한다면,
CMainFrame* pFrame = (CMainFrame*)AfxGetMainWnd();
CTestDoc* pDoc = (CTestDoc*)pFrame->GetActiveDocument();
물론 이 때는 각각의 클래스에 대한 헤더를 반드시 포함시켜야 함을 잊지 말아야 된다.
가끔 아래와 같은 순서로 헤더를 포함시키지 않는 경우가 있는데, 반드시 아래와 같이 포함해야 한다.
#include "MainFrm.h"
#include "TestDoc.h"
#include "TestView.h"
만약 대화 상자에서 도큐먼트 클래스에 접근하려면 어떻게 할 것인가? 방법은 위와 동일하다.
------------------------------------------------------------------------------------------------
다음은 뷰에서 도큐먼트에 접근하는 것과 도큐먼트에서 뷰에 접근하는 것에 대해 알아보자.
뷰와 도큐먼트는 서로를 직접 접근할 수 있는 함수가 제공된다. 뷰에서 도큐먼트를 직접 접근하려면,
CTestDoc* pDocument = (CTestDoc*)GetDocument();
를 호출하면 된다. 물론 View 클래스에는 GetDocument() 함수가 존재해야 한다. 일반적으로 이 함수는
MFC로 프로젝트 생성 시 기본적으로 있다. 하지만 만약 직접 만든 새로운 클래스라면 GetDocument() 함수를
직접 만들어줘야 한다. 직접 만들기가 귀찮다면, 다음과 같이 사용해도 된다.
CTestDoc* pDocument = (CTestDoc*)m_pDocument;
m_pDocument는 CTestView 클래스의 멤버이며, 다음과 같이 선언되어 있다.
CDocument* m_pDocument;
그리고, 도큐먼트에 뷰 클래스를 참조하려면 아래와 같이 해야 한다.
POSITION pos = GetFirstViewPosition();
CTestView* pView = (CTestView*)GetNextView( pos );
도큐먼트는 뷰 클래스를 내부적으로 링크드리스트를 사용해서 관리하고 있다. 그러므로 GetFirstViewPosition()
함수를 통해 POSITION을 얻어 온 다음, GetNextView() 함수를 통해 뷰의 포인터를 얻어오면 된다.
만약 창 분할에 의해 뷰가 여러 개 존재한다면, 다음과 같이 얻어 올 수 있다. 만약 컴파일 에러가 발생하면,
#include "TestView.h" 처럼 해서 헤더 파일을 포함하는 것도 잊지 말자.
POSITION pos = GetFirstViewPosition();
CTestView1* pView1 = (CTestView1*)GetNextView( pos );
CTestView2* pView2 = (CTestView2*)GetNextView( pos );
CTestView3* pView3 = (CTestView3*)GetNextView( pos );
CTestView4* pView4 = (CTestView4*)GetNextView( pos );
...
------------------------------------------------------------------------------------------------
그렇다면,,, CTestApp 클래스에서 뷰(CTestView) 클래스는 어떻게 얻어 올 수 있을까? 쉽게 응용할 수 있지만
머리가 꽉 막힐 수도 있다. 이 곳에서도 혹시 에러가 나면 헤더 파일을 자~알 추가해야 할 것이다.
CMainFrame* pFrame = (CMainFrame*)AfxGetMainWnd();
CTestView* pView = (CTestView*)pFrame->GetActiveView();
------------------------------------------------------------------------------------------------
마지막으로, 뷰(CTestView) 클래스에서 CMainFrame에 접근할 수 있는데 AfxGetMainWnd() 함수외에도
CMainFrame* pFrame = (CMainFrame*)GetParentFrame();
------------------------------------------------------------------------------------------------
MDI 환경에서 포인터를 가져와 보자.
CTestApp* pApp = (CTestApp*)AfxGetApp();
POSITION pos = pApp->GetFirstDocTemplatePosition();
CDocTemplate* pDocTemplate;
pDocTemplate = pApp->GetNextDocTemplate( pos ); // 첫 번째 등록한 템플릿
pDocTemplate->OpenDocumentFile( NULL ); // 첫 번째 문서 생성
POSITION posDoc = pDocTemplate->GetFirstDocPosition();
CTestDoc* pDoc = (CTestDoc*)pDocTemplate->GetNextDoc( posDoc ); // 첫 번째 문서 포인터
ASSERT( pDoc->IsKindof( RUNTIME_CLASS(CMongDoc) ) ); // 포인터 유효성 검사
//CXXXDoc* pDoc2 = (CXXXDoc*)pDocTemplate->GetNextDoc( posDoc ); // 두 번째 문서 포인터
POSITION posView = pDoc->GetFirstViewPosition();
CTestView* pView = (CTestView*)pDoc->GetNextView( posView ); // 첫 번째 뷰 포인터
ASSERT( pView->IsKindof( RUNTIME_CLASS(CTestView) ) ); // 포인터 유효성 검사
//CXXXView* pView2 = (CXXXView*)pDoc->GetNextView( posView ); // 두 번째 뷰 포인터
//ASSERT( pView2->IsKindof( RUNTIME_CLASS(CXXXView) ) ); // 포인터 유효성 검사
// 다음 템플릿이 존재할 경우만 가능합니다.
pDocTemplate = pApp->GetNextDocTemplate( pos ); // 두 번째 등록한 템플릿
if( pDocTemplate ) // 두 번째 템플릿이 존재하는지 검사
{
pDocTemplate->OpenDocumentFile( NULL ); // 두 번째 문서 생성
}
------------------------------------------------------------------------------------------------
분할 뷰에서는 위의 방법과 같이 포인터를 얻어 올 수 있다. 또는 다음과 같은 방법으로 얻어 올 수 있다.
만약 창이 두 개로 분할되어 있다면,
CMainFrame* pFrame = (CMainFrame*)AfxGetMainWnd();
CTestView* pView1 = (CTestView*)pFrame->m_wndSplitter.GetPane(0,0); // 첫 번째 뷰 포인터 얻기
CXXXView* pView2 = (CTestView*)pFrame->m_wndSplitter.GetPane(0,1); // 두 번째 뷰 포인터 얻기
뷰 포인터는 다음과 같이 좌표를 사용하여 구할 수 있다.
------------------------------------------------------------------
| | |
| GetPane( 0, 0 ) | GetPane( 0, 1 ) |
| | |
------------------------------------------------------------------
| | |
| GetPane( 1, 0 ) | GetPane( 1, 1 ) |
| | |
---------------------------------------------------------------------------------------------
| | | |
| GetPane( 2, 0 ) | GetPane( 2, 1 ) | GetPane( 2, 2 ) |
| | | |
---------------------------------------------------------------------------------------------