2011년 5월 5일 목요일

MFC 임의의 클래스의 포인터 얻기

Visual C++로 프로그램을 작성하고자 할 때, 가장 먼저 막히는 부분 중의 하나가 어떤 클래스 인스턴스의 포인터를 얻는 것이죠. 일반적으로 뷰 클래스를 가장 많이 다루게 되고, 그 다음이 도큐먼트 클래스, 그 다음이 메인 프레임 클래스 순일 것입니다. 그리고 거의 변경할 필요가 없는 애플리케이션 클래스도 있구요.

물론 사용자가 만든 클래스나 버튼 같은 컨트롤에 관련된 클래스에 접근해야 할 경우도 있는데, 그럴 때 초보자는 막막해 질 수밖에 없습니다. 경험담이기도 하죠.

여기서는 그러한 경우의 각 클래스의 포인터를 얻는 경우에 관해 알아봅니다.여기서는 프로젝트 이름이 Test라고 가정하죠. 사용자에 따라 프로젝트의 이름이 다르겠죠?

그리고 중간에 참조되는 모든 클래스의 헤더파일이 인클루드 되어야 하구요. 뷰의 헤더가 인클루드 된다면 반드시 그 앞에 도큐먼트의 헤더가 인클루드 되어야 합니다.

"일반적인 SDI 프로그램에서."

1.애플리케이션 클래스의 포인터를 얻을 때

    CWinApp* AfxGetApp()

함수를 사용합니다.

2.메인 프레임 클래스의 포인터를 얻을 때

    CWnd* AfxGetMainWnd()

함수를 사용합니다.

이들 두 함수는 MFC의 전역함수로써 프로그램을 작성하는 도중 어디에서나 사용할 수 있습니다. MFC에서는 Afx~로 시작하는 함수들은 모두 전역함수를 의미합니다.

물론 타입 캐스팅을 해야 하구요. 사용법은 다음과 같습니다.

    CTestApp *pApp = (CTestApp *)AfxGetApp();
    CMainFrame *pFr = (CMainFrame *)AfxGetMainWnd();

이렇게 써준 다음에는 pApp와 pFr은 각각 애플리케이션 클래스와 메인 프레임 클래스의 인스턴스 포인터를 가리키게 됩니다.

이 외에 뷰 클래스에서 그 뷰를 둘러싸고 있는 프레임 윈도우를 참조할 때는

    CFrameWnd* GetParentFrame() const

함수를 사용할 수 있습니다. 물론 뷰 클래스뿐만이 아니라 일반적인 윈도우를 둘러싸는 틀로써 프레임 윈도우가 사용될 수 있기 때문에 GetParentFrame() 함수는 CWnd() 클래스의 멤버함수로 되어있습니다.

이 함수와 AfxGetMainWnd() 함수는 SDI에서는 같은 기능을 하지만, MDI에서는 메인 프레임 윈도우와 뷰를 둘러싸고 있는 프레임 윈도우가 다르기 때문에 그 각각을 구하는 역할을 합니다.

3.도큐먼트 클래스의 포인터를 얻을 때는 몇 가지 경우가 있습니다.

    (a) 뷰 클래스에서 도큐먼트 클래스에 접근할 때.

    이때는 말할 필요도 없이 GetDocument() 함수를 쓰면 됩니다. 뷰에 이미 정의되어 있는 함수죠. 하지만 사용자가 임의로 뷰를 추가한 경우에는 이 GetDocument() 함수가 포함되어 있지 않습니다. 이럴 경우 기존에 있는 뷰에서 GetDocument() 함수 부분을 복사해다가 넣으면 됩니다. 이 부분은 Debug 모드와 Release 모드 두 가지의 함수가 있으므로 모두 복사해 넣아야 합니다.

    (b) 임의의 클래스에서 도큐먼트 클래스에 접근할 때.

      CMainFrame *pFr = (CMainFrame *)AfxGetMainWnd();
      CTestDoc *pDoc = (CTestDoc *)pFr->GetActiveDocument();

    위와 같이 두 줄에 걸쳐 써도 되고 다음처럼 한 줄로 줄여써도 됩니다.

    CTestDoc *pDoc = (CTestDoc *) ((CMainFrame *)AfxGetMainWnd())->GetActiveDocument();

4. 뷰 클래스의 포인터를 얻을 경우.

    (a) 임의의 클래스에서 뷰 클래스에 접근할 때

    뷰 클래스의 포인터를 얻어야 하는 경우는 대부분 다이얼로그에서 뷰에 접근하거나, 스플릿을 사용한 경우 다른 뷰 클래스에서 접근하는 경우가 대부분일 겁니다. 뭐 어떻든 상관없이 다음과 같이 하면 어디서든 뷰에 접근할 수 있습니다.

    CTestView *pView = (CTestView *) ((CMainWnd *)AfxGetMainWnd())->GetActiveView();

    물론 위의 3번의 경우처럼 두 줄로 나누어 써도 상관이 없습니다.

    (b) 도큐먼트 클래스에서 뷰 클래스에 접근을 할 때

    도큐먼트 클래스에서 뷰 클래스의 인스턴스 포인터를 얻으려면 GetFirstViewPosition() 함수와 GetNextView() 함수를 조합하여 사용해야 합니다. 이렇게 복잡해지는 이유는 도큐먼트 하나에 여러개의 뷰가 연결될 수 있기 때문입니다.

    도큐먼트에는 이에 연결된 뷰가 연결 리스트 형태로 관리되고 있기 때문에 몇 번째 뷰를 얻을 것인지 선택하고 나서 위의 함수를 조합하여 사용하면 됩니다.

    다음은 도큐먼트와 연결된 모든 뷰 클래스를 차례로 얻어 뷰 클래스의 멤버함수인 UpdateWindow() 함수를 호출하는 예제입니다.

      POSITION pos = GetFirstViewPosition();

      while(pos != NULL) {
      CView *pView = GetNextView(pos);
      pView->UpdateWindow();
      }

    물론 이와 같은 효과를 내기 위해서 도큐먼트 클래스의 멤버함수인 UpdateAllViews(NULL) 함수를 호출해도 됩니다. 여기서 쓰이는 인자인 NULL 은 모든 뷰를 업데이트하는 것이고, NULL 대신, 신호를 보내는 뷰의 포인터를 넣어주면 신호를 보내는 뷰는 빼고 나머지 뷰만 업데이트를 합니다.

    도큐먼트에 뷰가 오직 하나만 연결되어 있는 경우에는 다음과 같이 간단하게 뷰 클래스의 인슽턴스 포인터를 얻어낼 수도 있습니다. m_viewList 는 CDocument 클래스의 멤버변수로서, 뷰를 관리하는 연결 리스트입니다. 이것을 이용하여 GetHead() 함수를 호출하면 리스트에 들어있는 첫 번째 뷰가 얻어집니다.

      void CTestDoc::OnRepaintViews()
      {
      CView *pView = m_viewList.GetHead();
      pView->UpdateWindows();
      }

    (c) 스플리트 윈도우에서의 각 뷰 클래스에 접근할 때

    동적 스플리트 윈도우라면 모든 페인에서 같은 뷰를 사용하므로 별 문제가 되지 않는데, 정적 스플리트 윈도우라면 각 페인마다 다른 뷰를 사용할 수 있으므로 각 페인별로 뷰의 인스턴스 포인터를 얻는 것이 문제가 되는 경우가 생길 수 있습니다.

    이렇때는 메인 프레임 클래스에서 정의한 CSplitterWnd 클래스의 변수인 m_wndSplitter 의 멤버함수 GetPane()을 사용하면 각 페인의 뷰에 접근할 수 있습니다.

    우선 메인 프레임에서 정의된 m_wndSplitter 변수를 public: 속성으로 바꾸고(외부에서 접근해야 하므로) 메인 클래스의 인스턴스 포인터를 얻은 다음, 다시 여기서 m_wndSplitter 변수에 접근하여 이 멤버변수의 멤버함수 GetPane()을 이용하면 됩니다. 다음은 GetPane()의 함수 원형입니다. 리턴값은 대부분의 경우 CView에서 파생된 클래스의 인스턴스 포인터가 됩니다.

    CWnd* GetPane( int row, int col );

    임의의 클래스에서 다음과 같이 사용하면 페인에 연결된 뷰의 포인터를 얻을 수 있습니다.

CTestView *pView = (CTestView *)((CMainFrame *)AfxGetMainWnd())->m_wndSplitter.GetPane(0,1);

댓글 없음:

댓글 쓰기