. 탭 관리

탭 컨트롤을 프로그래밍할 때는 TCM_로 시작되는 메시지를 보내야 한다. 그러나 공통 컨트롤 라이브러리가 메시지를 대신 보내 주는 매크로 함수들을 정의하고 있으므로 TabCtrl_로 시작되는 매크로 함수를 대신 사용하는 것이 훨씬 더 편리하다. 즉 TCM_INSERTITEM 메시지를 보내는 대신 TabCtrl_InsertItem 함수를 사용하여 탭을 추가할 수 있다.

 

int TabCtrl_InsertItem( HWND hwnd, int iItem, const LPTCITEM pitem);

 

CreateWindow로 탭 컨트롤을 생성하면 탭을 가지지 않는 상태로 빈 컨트롤이 생성되는데 이 함수로 원하는만큼 탭을 추가한다. hwnd는 물론 탭 컨트롤의 핸들이며 이후 모든 함수들의 첫번째 인수는 탭 컨트롤의 핸들아더, iItem은 탭이 삽입될 위치이되 0부터 시작하는 인덱스 번호를 지정한다. pitem은 삽입될 탭의 정보를 지정하는 다음 구조체이다.

 

typedef struct tagTCITEM { 

    UINT mask;

#if (_WIN32_IE >= 0x0300)

    DWORD dwState;

    DWORD dwStateMask;

#else

    UINT lpReserved1;

    UINT lpReserved2;

#endif

    LPTSTR pszText;

    int cchTextMax;

    int iImage;

    LPARAM lParam;

} TCITEM, FAR *LPTCITEM;

 

mask 멤버는 이 구조체의 어떤 멤버가 유효한지를 지정하며 다음 플래그들의 조합을 사용할 수 있다. 삽입할 탭에 지정할 속성을 이 플래그로 명시해야 한다.

 

플래그

설명

TCIF_IMAGE

iImage로 이미지 번호를 지정한다. 이미지 리스트 상의 인덱스 번호로 지정하며 -1일 경우 이미지를 사용하지 않는다.

TCIF_PARAM

lParam으로 사용자 정의 데이터를 지정한다.

TCIF_RTLREADING

문자열을 오른쪽에서 왼쪽으로 출력한다.

TCIF_STATE

dwState로 탭의 상태를 지정한다. 단, 이 값은 탭을 삽입할 때는 사용되지 않으며 별도의 함수로 상태를 조사 변경해야 한다.

TCIF_TEXT

pszText 멤버로 탭의 텍스트를 지정한다. 텍스트를 읽을 때는 cchTextMax 멤버에 pszText 버퍼의 크기를 전달해 주어야 한다.

 

TabCtrl 예제에서는 mask에 TCIF_TEXT 플래그를 주고 pszText에 탭에 나타날 문자열을 지정하여 세 개의 탭을 추가하였다. iImage나 lParam을 사용하면 탭에 이미지를 출력하거나 사용자 정의 데이터를 저장할 수도 있다. 이미 삽입된 탭의 정보를 조사하거나 변경할 때는 다음 두 함수를 사용한다.

 

BOOL TabCtrl_GetItem( HWND hwnd, int iItem , LPTCITEM pitem);

BOOL TabCtrl_SetItem( HWND hwnd, int iItem, LPTCITEM pitem);

 

pitem의 mask에 조사 또는 변경하고 싶은 정보를 지정한 후 이 함수들을 호출하면 원하는 정보가 pitem에 대입되어 돌아올 것이다. 예를 들어 2번 탭의 텍스트를 조사하고 싶다면 mask에 TCIF_TEXT를 주고 pszText에 버퍼의 포인터를 cchTextMax에 버퍼의 길이를 대입한 후 ㅆTabCtrl_GetItem(hTab,2,&pitem)을 호출한 후 pszText를 읽으면 된다. 다음 두 함수는 이미 추가되어 있는 탭을 삭제한다.

 

BOOL TabCtrl_DeleteItem( HWND hwnd, int iItem);

BOOL TabCtrl_DeleteAllItems( HWND hwnd);

 

탭의 인덱스를 지정하여 하나만 삭제할 수도 있고 또는 탭 전체를 한꺼번에 삭제할 수도 있다. 함수 원형이 워낙 직관적이라 더 이상의 설명은 필요가 없을 것 같다. 다음 함수들은 탭 컨트롤의 여러 가지 상태를 조사한다.

 

int TabCtrl_GetCurSel( HWND hwnd);

int TabCtrl_SetCurSel( HWND hwnd, int iItem);

int TabCtrl_GetItemCount( HWND hwnd);

 

각각 현재 선택된 탭의 인덱스 조사, 변경, 총 탭 개수 조사 함수이다. 이런 류의 함수들은 콤보 박스나 리스트 박스 등의 표준 컨트롤과 사용 방법이 동일하므로 따로 예를 들 필요도 없이 쉽게 이해가 갈 것이다. 다음 TabManage 예제는 지금까지 설명한 탭 관리 함수들의 종합 실습편이다.

 

#include <commctrl.h>

HWND hTab, hEdit;

#define EDIT_TEXT 0

#define BTN_ADD 1

#define BTN_GET 2

#define BTN_EDIT 3

#define BTN_DEL 4

#define BTN_DELALL 5

LRESULT CALLBACK WndProc(HWND hWnd,UINT iMessage,WPARAM wParam,LPARAM lParam)

{

   TCITEM tie;

   TCHAR Text[250];

   int count, idx;

 

   switch(iMessage) {

   case WM_CREATE:

      InitCommonControls();

      hTab=CreateWindow(WC_TABCONTROL,"",WS_CHILD | WS_VISIBLE

          | WS_CLIPSIBLINGS,

          0,0,400,250,hWnd,(HMENU)0,g_hInst,NULL);

      hEdit=CreateWindow("edit",NULL,WS_CHILD | WS_VISIBLE | WS_BORDER,

          420,20,100,25,hWnd,(HMENU)EDIT_TEXT,g_hInst,NULL);

      CreateWindow("button","추가",WS_CHILD | WS_VISIBLE | BS_PUSHBUTTON,

          420,50,100,25,hWnd,(HMENU)BTN_ADD,g_hInst,NULL);

      CreateWindow("button","조사",WS_CHILD | WS_VISIBLE | BS_PUSHBUTTON,

          420,80,100,25,hWnd,(HMENU)BTN_GET,g_hInst,NULL);

      CreateWindow("button","변경",WS_CHILD | WS_VISIBLE | BS_PUSHBUTTON,

          420,110,100,25,hWnd,(HMENU)BTN_EDIT,g_hInst,NULL);

      CreateWindow("button","삭제",WS_CHILD | WS_VISIBLE | BS_PUSHBUTTON,

          420,140,100,25,hWnd,(HMENU)BTN_DEL,g_hInst,NULL);

      CreateWindow("button","모두 삭제",WS_CHILD | WS_VISIBLE | BS_PUSHBUTTON,

          420,170,100,25,hWnd,(HMENU)BTN_DELALL,g_hInst,NULL);

      return 0;

   case WM_COMMAND:

      switch(LOWORD(wParam)) {

      case BTN_ADD:

          GetWindowText(hEdit,Text,250);

          if (lstrlen(Text) !=0 ) {

             tie.mask=TCIF_TEXT;

             tie.pszText=Text;

             count=TabCtrl_GetItemCount(hTab);

             TabCtrl_InsertItem(hTab,count,&tie);

          }

          break;

      case BTN_GET:

          tie.mask=TCIF_TEXT;

          tie.pszText=Text;

          tie.cchTextMax=250;

          idx=TabCtrl_GetCurSel(hTab);

          if (idx != -1) {

             TabCtrl_GetItem(hTab,idx,&tie);

             SetWindowText(hEdit,Text);

          }

          break;

      case BTN_EDIT:

          idx=TabCtrl_GetCurSel(hTab);

          GetWindowText(hEdit,Text,250);

          if (idx != -1 && lstrlen(Text) != 0) {

             tie.mask=TCIF_TEXT;

             tie.pszText=Text;

             TabCtrl_SetItem(hTab,idx,&tie);

          }

          break;

      case BTN_DEL:

          idx=TabCtrl_GetCurSel(hTab);

          if (idx != -1) {

             TabCtrl_DeleteItem(hTab,idx);

          }

          break;

      case BTN_DELALL:

          TabCtrl_DeleteAllItems(hTab);

          break;

      }

      return 0;

   case WM_DESTROY:

      PostQuitMessage(0);

      return 0;

   }

   return(DefWindowProc(hWnd,iMessage,wParam,lParam));

}

 

탭 컨트롤 하나와 탭의 텍스트를 지정하기 위한 에디트 컨트롤, 그리고 명령 버튼 다섯개를 생성한 후 버튼을 사용하여 탭 컨트롤을 삽입, 삭제, 변경한다. 이미지나 사용자 정의 데이터는 일단 제외하고 텍스트로만 탭을 등록하도록 하였다.

추가 버튼을 누르면 에디트 컨트롤에 입력된 텍스트로 새 탭을 추가했다. mask에 TCIF_TEXT를 주고 pszText에 에디트에 입력된 텍스트 번지를 대입한 후 총 항목 개수번째에 탭을 추가하였다. 물론 인덱스 번호만 제대로 지정해 주면 기존의 탭 중간에 새로운 탭을 삽입해 넣을 수도 있다.

조사 버튼을 누르면 현재 선택된 탭의 텍스트를 TabCtrl_GetItem 함수로 조사하여 에디트로 다시 출력해 주며 변경 버튼은 TabCtrl_SetItem 함수로 선택탭의 텍스트를 변경하였다. 나머지 두 개의 버튼은 지정한 탭, 또는 전체 탭을 삭제한다. 탭 컨트롤의 탭을 관리하는 방법은 리스트 박스나 콤보 박스의 항목을 관리하는 방법과 거의 유사해서 이 예제의 코드를 한번 읽어 보기만 해도 쉽게 이해할 수 있는 정도다.