.ToolTip 예제

툴팁도 리스트 뷰나 프로그래스 같은 공통 컨트롤인데 사실이 선뜻 이해가 가지 않는 사람도 있을 것이다. 항상 보이는 것이 아니고 불쑥 불쑥 아무 곳에나 나타나기 때문에 뭔가 특수해 보이지만 알고 보면 스스로 위치를 옮길 있는 팝업 윈도우일뿐이다. 그래서 다른 공통 컨트롤을 프로그래밍하는 방법과 비슷한 방법으로 다루면 된다. 속성이 있고 메시지로 프로그래밍하며 통지 메시지를 부모 윈도우로 보내기도 한다.

일종의 윈도우이므로 생성할 때는 당연히 CreateWindow(Ex) 함수를 사용하는데 클래스 이름으로 TOOLTIPS_CLASS 준다. , 다른 컨트롤과는 달리 생성할 몇가지 다른 점이 있는데 점만 주의해서 생성하면 된다.

 

항상 탑모스트 확장 스타일을 주는 것이 좋다. 그렇지 않으면 부모나 형제 윈도우들에 의해 가려져 안보이는 경우가 있으므로 특별한 이유가 없는 탑모스트 확장 스타일을 주도록 하자. 특성상 다른 윈도우보다 아래에 있으면 존재 가치가 없어진다.

WS_POPUP 스타일과 WS_EX_TOOLWINDOW 확장 스타일을 항상 가져야 한다. 툴팁이 타이틀 바나 시스템 메뉴를 가지고 있으면 얼마나 꼴불견이겠는가? 하지만 스타일은 생성할 주지 않아도 툴팁 컨트롤이 알아서 적용하므로 생략 가능하다. 항상 스타일이 적용된다는 것만 알아 두자.

생성할 좌표는 아무 의미가 없다. 툴을 등록하면 알아서 위치로 옮겨가므로 생성할 좌표는 CW_USEDEFAULT 주면 문제가 없다.

 

다음은 툴팁의 스타일이다. CreateWindow 툴팁을 만들 스타일 원하는 스타일을 지정해 준다. 실용적으로 쓸만한 스타일이 별로 없으므로 일단은 0으로 줘도 상관이 없다.

 

스타일

설명

TTS_ALWAYSTIP

소유 윈도우가 액티브 상태가 아닐 때도 툴팁을 보여 준다. 스타일이 지정되지 않으면 액티브 상태일 때만 툴팁이 나타난다. 일반적으로 스타일은 주지 않는 것이 바람직하다.

TTS_BALLOON

5.80이상.풍선형 도움말을 보여준다.

TTS_NOANIMATE

5.80이상.슬라이딩 애니메이션을 하지 않도록 한다.

TTS_NOFADE

5.80이상.페이드 애니메이션을 하지 않도록 한다.

TTS_NOPREFIX

툴팁 컨트롤은 텍스트의 & 제거해 주는데 스타일을 주면 & 제거하지 않도록 한다. 텍스트를 메뉴와 동일하게 작성하고자 경우는 메뉴의 단축키를 지정하는 & 문자를 제거하는 것이 좋다. 예를 들어 메뉴 항목 &Open 대한 텍스트는 &Open보다는 Open 보기 좋다.

 

하나의 툴팁 컨트롤을 만들면 컨트롤로 복수개의 툴에 대해 툴팁 기능을 제공할 있다. 10개의 버튼에 대해 툴팁을 제공할 10개의 툴팁 컨트롤을 만들 필요가 없으며 하나의 툴팁을 만든 컨트롤에 10개의 툴을 등록하면 된다. 어차피 한번에 하나의 툴팁만 보이므로 툴팁 컨트롤은 하나만 있으면 된다. 물론 아주 특수한 경우, 예를 들어 스타일이 다른 툴팁을 동시에 사용하고 싶다면 이상 생성해도 상관없다.

툴팁 컨트롤을 생성한 툴팁 대상이 되는 툴을 등록해야 하는데 이때는 TTM_ADDTOOL 메시지를 사용한다. 메시지의 lParam으로 등록될 툴에 대한 정보를 가지는 TOOLINFO 구조체를 전달해 준다. 이쯤되면 짐작이 가겠지만 구조체가 바로 툴팁 컨트롤의 핵심이라 있으며 툴팁을 자유 자재로 다루고 싶다면 일단 구조체를 이해해야 하고 암기할 있으면 좋다. 어떻게 선언되어 있는지 보자.

 

typedef struct tagTOOLINFO{

    UINT      cbSize;

    UINT      uFlags;

    HWND      hwnd;

    UINT_PTR  uId;

    RECT      rect;

    HINSTANCE hinst;

    LPTSTR    lpszText;

#if (_WIN32_IE >= 0x0300)

    LPARAM lParam;

#endif

} TOOLINFO, NEAR *PTOOLINFO, FAR *LPTOOLINFO;

 

cbSize

버전 확인을 위한 구조체의 크기이며 sizeof(TOOLINFO) 대입해 주면 된다. 멤버의 값은 반드시 대입해 주어야 한다.

uFlags

툴팁의 표시 방식을 지정하는 플래그들이며 다음 플래그들의 조합을 지정할 있다. 플래그의 효과에 대해서는 다시 개별적으로 실습을 예정이므로 대충 의미만 두도록 하자.

 

플래그

설명

TTF_ABSOLUTE

TTF_TRACK 플래그와 함께 사용하며 정확한 위치에 툴팁이 나타나도록 한다.

TTF_CENTERTIP

툴팁이 툴의 아래쪽 중앙에 나타나도록 한다. 플래그가 없으면 커서 위치에 나타난다.

TTF_IDISHWND

등록되는 툴이 윈도우라는 뜻이며 uId 멤버는 윈도우 핸들값을 가진다. 플래그가 없으면 툴은 사각 영역이며 uId 툴의 ID이다.

TTF_PARSELINKS

텍스트의 링크를 해석하도록 한다.

TTF_RTLREADING

오른쪽에서 왼쪽으로 텍스트를 출력한다. 한글 윈도우즈에서는 해당되지 않는다.

TTF_SUBCLASS

툴팁 컨트롤이 툴을 서브 클래싱하도록 한다.

TTF_TRACK

툴팁의 위치를 TTM_TRACKPOSITION 메시지에서 지정한 대로 이동시킨다.

TTF_TRANSPARENT

마우스 메시지를 부모 윈도우에게 전달한다. 투명하게 보이는 것은 아니다.

hwnd

툴의 부모 윈도우 핸들이며 윈도우로 툴팁 컨트롤의 통지 메시지가 전달된다. 툴을 소유하고 있는 윈도우의 핸들이지 툴팁 컨트롤의 부모 윈도우나 툴의 윈도우 핸들이 아님을 주의하자.

uId

툴의 고유 번호에 해당하는 ID이다. 툴이 윈도우이면, uFlags TTF_IDISHWND 플래그가 있으면 값은 툴의 윈도우 핸들이 되며 사각 영역이면, uFlags TTF_IDISHWND 플래그가 없으면 값은 0부터 시작되는 툴의 일련 번호이다. 고유한 값을 가져야 하므로 툴끼리 중복되어서는 안된다.

rect

툴의 사각 영역을 지정한다. 윈도우 툴인 경우 핸들로부터 영역을 구할 있으므로 멤버는 무시된다. 좌표값은 부모 윈도우의 작업 영역 기준이다.

hinst

문자열 리소스를 가진 인스턴스 핸들이다. 문자열 리소스를 쓰지 않을 경우 멤버는 무시된다.

lpszText

텍스트 문자열이다. 세가지 형태로 지정할 있는데 간단하게 문자열 상수를 있으며 문자열 리소스의 ID 수도 있다. LPSTR_TEXTCALLBACK 값을 주면 부모 윈도우에게 TTN_GETDISPINFO 통지 메시지를 보내 실행중에 문자열을 구한다. 텍스트를 구할 멤버는 충분한 길이의 버퍼를 가리키고 있어야 한다.

lParam

사용자 정의 데이터이다.

 

cbSize, hwnd, lpszText 멤버는 툴을 등록할 항상 전달해 주어야 한다. 나머지 멤버는 툴의 종류에 따라 선택적으로 전달할 있으며 의미도 약간 달라진다.

 

멤버

윈도우

사각영역

uFlags

TTF_IDISHWND 포함

TTF_IDISHWND 미포함

hId

툴의 윈도우 핸들

0부터 시작하는 툴의 고유 번호

rect

무시

사각 영역 좌표

 

툴팁의 생성과 등록에 필요한 구조체까지 알아 봤으므로 복잡해지기 전에 간단한 예제를 하나 만들어 보도록 하자.

 

#include <commctrl.h>

HWND hTip, hBtn;

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

{

HDC hdc;

PAINTSTRUCT ps;

INITCOMMONCONTROLSEX iccex;

RECT rt={300,50,500,150};

TOOLINFO ti;

 

switch(iMessage) {

case WM_CREATE:

    iccex.dwICC=ICC_WIN95_CLASSES;

    iccex.dwSize=sizeof(INITCOMMONCONTROLSEX);

    InitCommonControlsEx(&iccex);

 

    hTip=CreateWindowEx(WS_EX_TOPMOST,TOOLTIPS_CLASS,NULL,0,

       CW_USEDEFAULT,CW_USEDEFAULT,CW_USEDEFAULT,CW_USEDEFAULT,

       hWnd,NULL,g_hInst,NULL);

 

    hBtn=CreateWindow("button","버튼",WS_CHILD | WS_VISIBLE | BS_PUSHBUTTON,

       50,50,200,100,hWnd,(HMENU)0,g_hInst,NULL);

 

    ti.cbSize=sizeof(TOOLINFO);

    ti.uFlags=TTF_SUBCLASS | TTF_IDISHWND;

    ti.hwnd=hWnd;

    ti.uId=(WPARAM)hBtn;

    ti.lpszText="버튼입니다";

    SendMessage(hTip,TTM_ADDTOOL,0,(LPARAM)(LPTOOLINFO)&ti);

 

    ti.uFlags=TTF_SUBCLASS;

    ti.hwnd=hWnd;

    ti.uId=0;

    ti.lpszText="사각영역입니다";

    ti.rect=rt;

    SendMessage(hTip,TTM_ADDTOOL,0,(LPARAM)(LPTOOLINFO)&ti);

    return 0;

case WM_PAINT:

    hdc=BeginPaint(hWnd, &ps);

    Rectangle(hdc,rt.left,rt.top,rt.right,rt.bottom);

    EndPaint(hWnd, &ps);

    return 0;

case WM_LBUTTONDOWN:

    SendMessage(hTip,TTM_ACTIVATE,(WPARAM)TRUE,0);

    return 0;

case WM_RBUTTONDOWN:

    SendMessage(hTip,TTM_ACTIVATE,(WPARAM)FALSE,0);

    return 0;

case WM_DESTROY:

    PostQuitMessage(0);

    return 0;

}

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

}

 

버튼 컨트롤 하나와 사각 영역 하나를 만들고 대상에 대해 툴팁을 달아 보았다. 사각 영역은 굳이 경계선이 있어야 필요가 없지만 분명한 확인을 위해 WM_PAINT에서 사각형을 그려 주었다. 실행한 마우스 커서를 버튼이나 사각 영역에 가져 가면 툴팁이 나타난다.

모든 처리는 WM_CREATE에서 하고 있는데 코드를 분석해 보자. 공통 컨트롤을 사용하려면 commctrl.h 포함하고 InitCommonControls(Ex) 함수로 공통 컨트롤 라이브러리를 초기화해야 하며 comctl32.lib 링크시켜 주어야 한다. 라이브러리 초기화 툴팁 컨트롤을 생성하였다. WS_EX_TOPMOST 확장 스타일을 주었으며 윈도우 스타일은 특별히 것이 없으므로 0으로 지정했다. 스타일을 주지 않아도 WS_POPUP, WS_EX_TOOLWINDOW 스타일은 디폴트로 적용된다.

툴팁 컨트롤 생성 툴팁 대상인 버튼 컨트롤을 적당한 위치에 생성했으며 버튼과 사각 영역에 대해 개의 툴을 등록한다. 버튼은 윈도우이므로 uFlags TTF_IDISHWND 플래그를 주었으며 uId 버튼의 윈도우 핸들인 hBtn 대입해 준다. TTF_SUBCLASS라는 플래그도 지정했는데 플래그의 의미는 다소 복잡하므로 다음 항에서 따로 자세히 알아볼 것이다.

버튼 툴을 등록한 곧이어 사각 영역을 등록한다. 윈도우가 아니므로 TTF_IDISHWND 플래그는 필요가 없고 대신 rect 멤버에 사각 영역의 좌표를 전달해 주어야 한다. uId에는 사각 영역의 고유 번호를 주는데 만약 사각 영역을 등록한다면 계속해서 1,2,3 으로 증가하는 고유 번호를 주면 된다.

이렇게 개의 툴을 등록해 놓으면 나머지 처리는 툴팁 컨트롤이 알아서 한다. 툴팁 컨트롤은 툴의 좌표와 윈도우 핸들에 대한 정보를 가지고 있으므로 어느 위치에서 툴팁을 출력해야 할지를 계산할 있다. 항상 마우스 위치를 감시하고 있다가 등록된 위에서 일정시간 머무르면 즉시 텍스트를 보여줄 것이다.

아주 간단한 툴팁 예제를 만들어 봤는데 예제를 통해 툴팁의 생성 방법과 등록 방법, 그리고 무엇보다 TOOLINFO 구조체에 익숙해지도록 하자. 툴을 삭제할 때는 TTM_DELTOOL 메시지를 사용하며 툴의 활성화 여부를 실행중에 변경하고 싶을 때는 TTM_ACTIVATE 메시지를 사용한다. 예제에서 마우스 왼쪽, 오른쪽 버튼에 메시지를 호출하는 코드를 넣어 툴팁 기능을 실행중에 활성/비활성화시킬 있도록 놓았다.