본문 바로가기

old drawer/C, C++, MFC

[MFC] Tab Control 사용법

MFC프로그램을 하다보면,

Tabcontrol을 만들어야 하는 경우가 생길 것이다.

그 탭컨트롤을 만드는 과정을 알아보자.

모든 프로그램은 Dialog based로 하는 것을 기준으로 한다.

 

우선 dialog based 프로젝트를 만든다.

다이어로그 베이스 프로젝트는 앞의 포스트에서 미리 설명을 하였다.

test라는 프로젝트 이름으로 만들었다.

만든 뒤에, TODO:…. 된 부분을 지우고,

그림에서 보듯 Toolbox에서 Tab Control을 드래그 해서 아래와 같이 화면에 붙여 넣어준다.

만약 Toolbox가 보이지 않는다면,

메뉴바의 view에서 toolbox를 찾을 수 있다.

 

 

이렇게 만들어진 Tab에 이름을 정해주자.

그림에서 보듯이 Add Variable을 클릭한다. (변수 추가)

새로 창이 뜨는데,

Access는 public, Variable type은 CTabCtrl인지를 확인하고,

Variable name에는 m_Tab이라고 적자. (대/소문자 구분을 하기 때문에 확실하게 적어야 된다.)

이제 Finish를 눌러주면 지금 만든 탭의 이름은 m_Tab가 된다.

 

이제는 프로그램 내용을 수정할 차례이다.

위의 그림처럼 CtestDlg.cpp 파일에서 OnInitDialog를 찾는다.

이 안의 내용을 수정할 것인데,

 

 

 

Code Snippet
  1. BOOL CtestDlg::OnInitDialog()
  2. {
  3.     CDialog::OnInitDialog();
  4.  
  5.     // Add "About..." menu item to system menu.
  6.  
  7.     // IDM_ABOUTBOX must be in the system command range.
  8.     ASSERT((IDM_ABOUTBOX & 0xFFF0) == IDM_ABOUTBOX);
  9.     ASSERT(IDM_ABOUTBOX < 0xF000);
  10.  
  11.     CMenu* pSysMenu = GetSystemMenu(FALSE);
  12.     if (pSysMenu != NULL)
  13.     {
  14.         BOOL bNameValid;
  15.         CString strAboutMenu;
  16.         bNameValid = strAboutMenu.LoadString(IDS_ABOUTBOX);
  17.         ASSERT(bNameValid);
  18.         if (!strAboutMenu.IsEmpty())
  19.         {
  20.             pSysMenu->AppendMenu(MF_SEPARATOR);
  21.             pSysMenu->AppendMenu(MF_STRING, IDM_ABOUTBOX, strAboutMenu);
  22.         }
  23.     }
  24.  
  25.     // Set the icon for this dialog.  The framework does this automatically
  26.     //  when the application's main window is not a dialog
  27.     SetIcon(m_hIcon, TRUE);            // Set big icon
  28.     SetIcon(m_hIcon, FALSE);        // Set small icon
  29.  
  30.     // TODO: Add extra initialization here
  31.  
  32.     CString    strOne = _T("First");
  33.     CString    strTwo = _T("Second");
  34.     CString    strThree = _T("Third");
  35.     m_Tab.InsertItem(1, strOne);
  36.     m_Tab.InsertItem(2, strTwo);
  37.     m_Tab.InsertItem(3, strThree);
  38.  
  39.     return TRUE;  // return TRUE  unless you set the focus to a control
  40. }

그 안의 내용에서 위와 같이 추가를 한다.

추가 된 내용은 32번째 줄에서 37번째 줄인데,

32 - 34번째 줄은 탭컨트롤에서 탭의 제목으로 들어갈 내용 들이다.

35 - 37번재 줄은 몇 번째 탭에 어떤 이름이 들어갈 지에 대한 내용으로,

35번째 줄을 보면 (1, strOne)으로 되어있는데, 1번째 탭에 strOne에 들어있는 내용을 넣는 다는 말이다.

여기서 strOne에는 First라는 내용이 들어 있기 때문에

1번째 탭은 First라는 이름으로 들어간다.

여기까지 수정을 하고, 빌드를 해보자.

예상한 대로 1번째 탭의 이름은 First이고 나머지도 예상 대로 됬다.

이제 각각의 탭에 내용을 넣어줄 차례이다.

 

 

 

지금부터 하는 과정은 뒤에서 그만이라는 말이 나올때까지의 부분을 3번 반복해준다.

(탭이 3개일 경우에 3번으로 탭의 수에 맞춰서 해주면 된다.)

위의 그림처럼 Resource View를 클릭을 해서 Resource view를 열어주자.

Resource View의 화면이다.

여기서 오른쪽 버튼을 눌러서, 다이어로그를 추가해주자.

그러면 Dialog1이라는 다이어로그가 추가가 된 것을 볼 수있다.

이 다이어로그는 아까의 탭안에 들어갈 내용들을 넣어줄 다이어로그이다.

따라서 우리가 한 것처럼 탭이 3개가 있으면 3개의 다이어로그를 만들어 주어야 한다.

위의 그림처럼 IDD_Dialog1을 클릭한 뒤

메뉴의 view에서 Properties Window를 클릭해서 프로퍼티 창을 열어준다.

이러한 창을 볼 수 있는데, 여기서 ID를 자신이 원하는 ID로 바꾸어준다.

이 ID는 지금 새로 만드는 다이어로그의 이름으로서 나중에 탭컨트롤에서 쓸 것이다.

기본으로는 IDD_Dialog1으로 되어있을 것이다.

이 화면은 ID를 이미 바꾸어 놓은 것이다.

이 ID는 자신이 원하는 것도 괜찮지만, 우선은 똑같이 IDD_First로 해준다.

그런 뒤에 화면의 다이어로그를 클릭해준다.

그러면 프로퍼티 창이 바뀌는 것을 볼 수 있는데,

여기서 Border를 None으로,

Style을 Child로 바꾸어준다.

그러면 이 다이어로그가 바뀌는 것을 볼 수 있다.

border를 none으로 바꿈으로서 경계가 다 없어졌고,

style을 child로 바꿈으로서 다이어로그 안에 들어갈 수 있게 되었다.

 

이제 이 다이어로그의 구분을 해주기 위해,

static text를 추가해주자.

역시 tabcontrol처럼 드래그하면 된다.

막 드래그 한 상태이면 위의 그림처럼 선택이 되어있을 것이다.

이 상태에서 프로퍼티 윈도우를 보면 내용을 수정할 수 있다.

여기서 Caption을 수정해주면된다.

First라고 수정을 해주면,

바뀐 것을 알 수 있다.

이제 이 다이어로그에 이름을 붙여줄 차례이다.

다이어로그에서 오른쪽 버튼을 눌러서 Add Class를 눌러주고,

뜨는 창에서

그림과 같이 Class name을 정해준다.

여기선 CFirst로 정해주었다.

그리고 Base class 가 CDialog인지를 확인하고 finish를 눌러준다.

위에서부터 지금까지의 과정(새로운 다이어로그를 만들고 이름을 붙여주는 과정)을

3번 반복한다.

물론 각각의 이름을 다르게 해주어야 한다.

여기선 두번째, 세번째 다이어로그의 이름을 CSecond, CThird라고 해주었다.

여기까지의 과정이 완료가 되었다면 이제부터가 시작이다.

 

testDlg.h파일을 열어서 다음과 같이 수정한다.

Code Snippet
  1.  
  2. // testDlg.h : header file
  3. //
  4.  
  5. #pragma once
  6. #include "afxcmn.h"
  7. #include "First.h"
  8. #include "Second.h"
  9. #include "Third.h"
  10.  
  11.  
  12. // CtestDlg dialog
  13. class CtestDlg : public CDialog
  14. {
  15. // Construction
  16. public:
  17.     CtestDlg(CWnd* pParent = NULL);    // standard constructor
  18.  
  19. // Dialog Data
  20.     enum { IDD = IDD_TEST_DIALOG };
  21.     CFirst        m_First;
  22.     CSecond        m_Second;
  23.     CThird        m_Third;
  24.     CWnd*        m_pwndShow;
  25.  
  26.     protected:
  27.     virtual void DoDataExchange(CDataExchange* pDX);    // DDX/DDV support
  28.  
  29.  
  30. // Implementation
  31. protected:
  32.     HICON m_hIcon;
  33.  
  34.     // Generated message map functions
  35.     virtual BOOL OnInitDialog();
  36.     afx_msg void OnSysCommand(UINT nID, LPARAM lParam);
  37.     afx_msg void OnPaint();
  38.     afx_msg HCURSOR OnQueryDragIcon();
  39.     DECLARE_MESSAGE_MAP()
  40. public:
  41.     CTabCtrl m_Tab;
  42. };

위의 내용을 보면

7, 8, 9번째 줄에 방금 만든 헤더파일을 추가 시켜줬음을 알 수 있다.

또한, 21, 22, 23, 24번째 줄에도 내용이 추가 되었다.

각각의 다이어로그의 이름에 맞춰서 그 다이어로그를 제어할 수 있는 부분을 추가해주었다.

이제 다시 testDlg.cpp파일을 열어서

CtestDlg를 찾아서 아래와 같이 수정을 해준다.

5번째 줄만 수정을 해주었다.

Code Snippet
  1. CtestDlg::CtestDlg(CWnd* pParent /*=NULL*/)
  2.     : CDialog(CtestDlg::IDD, pParent)
  3. {
  4.     m_hIcon = AfxGetApp()->LoadIcon(IDR_MAINFRAME);
  5.     m_pwndShow = NULL;
  6. }

같은 파일(testDlg.cpp)에서

OnInitDialog에

Code Snippet
  1. // TODO: Add extra initialization here
  2.  
  3. CString    strOne = _T("First");
  4. CString    strTwo = _T("Second");
  5. CString    strThree = _T("Third");
  6. m_Tab.InsertItem(1, strOne);
  7. m_Tab.InsertItem(2, strTwo);
  8. m_Tab.InsertItem(3, strThree);
  9.  
  10. CRect Rect;
  11. m_Tab.GetClientRect(&Rect);
  12.  
  13. m_First.Create(IDD_First, &m_Tab);
  14. m_First.SetWindowPos(NULL, 5, 25,
  15.     Rect.Width() - 12, Rect.Height() - 33,
  16.     SWP_SHOWWINDOW | SWP_NOZORDER);
  17. m_pwndShow = &m_First;
  18.  
  19. m_Second.Create(IDD_Second, &m_Tab);
  20. m_Second.SetWindowPos(NULL, 5, 25,
  21.     Rect.Width() -12, Rect.Height() - 33,
  22.     SWP_NOZORDER);
  23.  
  24. m_Third.Create(IDD_Third, &m_Tab);
  25. m_Third.SetWindowPos(NULL, 5, 25,
  26.     Rect.Width() - 12, Rect.Height() - 33,
  27.     SWP_NOZORDER);
  28.  
  29. return TRUE;  // return TRUE  unless you set the focus to a control

아까 탭을 만들때 추가시켜준 부분 아래에 다음과 같이 추가를 해준다.

중요한 것은 16번째 줄의 내용은 한번만 추가해주는 것이다.

탭컨트롤을 만드는 것은 책에서 보고 따라한 것인데

아무생각없이 3번 복사했더니, 탭컨트롤이 똑바로 초기화가 안되어서

실행을 했을 때, 탭안의 내용이 바로 실행이 되어야 하는데,

다른 탭을 일일이 눌러주지 않으면 처음탭의 내용이 보이기만하고 실행이 안되는 경우가 생긴다.

 

다시 Resource view로 돌아와서,

그림에서 보이는 화면을 클릭해주고, (탭안을 클릭해야됨)

프로퍼티 창을 보면(위에 설명 해놨음)

이 화면에서 오른쪽의 번개마크를 누르면 아래와 같이 화면이 바뀌게 된다.

이 화면에서, 밑에서 두번째에 있는

TCN_SELCHANGE를 누르면 창이 뜨는데,

OnTcnSelchangeTab을 추가할 수 있게 된다.

추가를 하면 testDlg.cpp파일로 돌아오게 되는데,

Code Snippet
  1. void CtestDlg::OnTcnSelchangeTab1(NMHDR *pNMHDR, LRESULT *pResult)
  2. {
  3.     // TODO: Add your control notification handler code here
  4.     *pResult = 0;
  5. }

이런 것이 생긴 것을 볼 수 있다.

 

Code Snippet
  1. void CtestDlg::OnTcnSelchangeTab1(NMHDR *pNMHDR, LRESULT *pResult)
  2. {
  3.     // TODO: Add your control notification handler code here
  4.     if(m_pwndShow != NULL)
  5.     {
  6.         m_pwndShow->ShowWindow(SW_HIDE);
  7.         m_pwndShow = NULL;
  8.     }
  9.  
  10.     int nIndex = m_Tab.GetCurSel();
  11.     switch(nIndex)
  12.     {
  13.     case 0:
  14.         m_First.ShowWindow(SW_SHOW);
  15.         m_pwndShow = &m_First;
  16.         break;
  17.     case 1:
  18.         m_Second.ShowWindow(SW_SHOW);
  19.         m_pwndShow = &m_Second;
  20.         break;
  21.     case 2:
  22.         m_Third.ShowWindow(SW_SHOW);
  23.         m_pwndShow = &m_Third;
  24.         break;
  25.     }
  26.     *pResult = 0;
  27. }

위와 같이 수정을 해주면 탭컨트롤 추가가 끝이다.

이로서 완성이 되었다.

실행을 해보면,

위와 같이 잘 된 것을 볼 수 있다.^^

 

 

출처

http://jaiyun.tistory.com/5