How to Add Tooltips to Buttons and other UI Components (like - Checkbox, Radio button etc) in Win32 (C++) Desktop Application?

MERUN KUMAR MAITY 596 Reputation points
2024-08-27T14:38:58.41+00:00

Hi there, I have a Win32 (C++) program on which I want to Add some Tooltips to some of it's UI components like - Button, Checkbox, Radio button. I tried a lot but nothing works.

Here is my Demo program :

#include <windows.h>
struct
{
	int iStyle;
	/*TCHAR* szText;*/
	TCHAR const* szText;
}
button[] =
{
BS_PUSHBUTTON, TEXT("PUSHBUTTON"),
BS_DEFPUSHBUTTON, TEXT("DEFPUSHBUTTON"),
BS_CHECKBOX, TEXT("CHECKBOX"),
BS_AUTOCHECKBOX, TEXT("AUTOCHECKBOX"),
BS_RADIOBUTTON, TEXT("RADIOBUTTON"),
BS_3STATE, TEXT("3STATE"),
BS_AUTO3STATE, TEXT("AUTO3STATE"),
BS_GROUPBOX, TEXT("GROUPBOX"),
BS_AUTORADIOBUTTON, TEXT("AUTORADIO"),
BS_OWNERDRAW, TEXT("OWNERDRAW")
};
#define NUM (sizeof button / sizeof button[0])
LRESULT CALLBACK WndProc(HWND, UINT, WPARAM, LPARAM);
int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance,
	PSTR szCmdLine, int iCmdShow)
{
	static TCHAR szAppName[] = TEXT("BtnLook");
	HWND hwnd;
	MSG msg;
	WNDCLASS wndclass;
	wndclass.style = CS_HREDRAW | CS_VREDRAW;
	wndclass.lpfnWndProc = WndProc;
	wndclass.cbClsExtra = 0;
	wndclass.cbWndExtra = 0;
	wndclass.hInstance = hInstance;
	wndclass.hIcon = LoadIcon(NULL, IDI_APPLICATION);
	wndclass.hCursor = LoadCursor(NULL, IDC_ARROW);
	wndclass.hbrBackground = (HBRUSH)GetStockObject(WHITE_BRUSH);
	wndclass.lpszMenuName = NULL;
	wndclass.lpszClassName = szAppName;
	if (!RegisterClass(&wndclass))
	{
		MessageBox(NULL, TEXT("This program requires Windows NT!"),
			szAppName, MB_ICONERROR);
		return 0;
	}
	hwnd = CreateWindow(szAppName, TEXT("Button Look"),
		WS_OVERLAPPEDWINDOW,
		400, 400,
		1297, 693,
		NULL, NULL, hInstance, NULL);
	ShowWindow(hwnd, iCmdShow);
	UpdateWindow(hwnd);
	while (GetMessage(&msg, NULL, 0, 0))
	{
		TranslateMessage(&msg);
		DispatchMessage(&msg);
	}
	return msg.wParam;
}
LRESULT CALLBACK WndProc(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)
{
	static HWND hwndButton[NUM];
	static RECT rect;
	static TCHAR szTop[] = TEXT("message wParam lParam"),
		szUnd[] = TEXT("_______ ______ ______"),
		szFormat[] = TEXT("%-16s%04X-%04X %04X-%04X"),
		szBuffer[50];
	static int cxChar, cyChar;
	HDC hdc;
	PAINTSTRUCT ps;
	int i;
	switch (message)
	{
	case WM_CREATE:
		cxChar = LOWORD(GetDialogBaseUnits());
		cyChar = HIWORD(GetDialogBaseUnits());
		for (i = 0; i < NUM; i++)
			hwndButton[i] = CreateWindow(TEXT("button"),
				button[i].szText,
				WS_CHILD | WS_VISIBLE | button[i].iStyle,
				cxChar, cyChar * (1 + 2 * i),
				20 * cxChar, 7 * cyChar / 4,
				hwnd, (HMENU)i,
				((LPCREATESTRUCT)lParam)->hInstance, NULL);
		return 0;
	case WM_SIZE:
		rect.left = 24 * cxChar;
		rect.top = 2 * cyChar;
		rect.right = LOWORD(lParam);
		rect.bottom = HIWORD(lParam);
		return 0;
	case WM_PAINT:
		InvalidateRect(hwnd, &rect, TRUE);
		hdc = BeginPaint(hwnd, &ps);
		SelectObject(hdc, GetStockObject(SYSTEM_FIXED_FONT));
		SetBkMode(hdc, TRANSPARENT);
		TextOut(hdc, 24 * cxChar, cyChar, szTop, lstrlen(szTop));
		TextOut(hdc, 24 * cxChar, cyChar, szUnd, lstrlen(szUnd));
		EndPaint(hwnd, &ps);
		return 0;
	case WM_DRAWITEM:
	case WM_COMMAND:
		ScrollWindow(hwnd, 0, -cyChar, &rect, &rect);
		hdc = GetDC(hwnd);
		SelectObject(hdc, GetStockObject(SYSTEM_FIXED_FONT));
		TextOut(hdc, 24 * cxChar, cyChar * (rect.bottom / cyChar - 1),
			szBuffer,
			wsprintf(szBuffer, szFormat,
				message == WM_DRAWITEM ? TEXT("WM_DRAWITEM") :
				TEXT("WM_COMMAND"),
				HIWORD(wParam), LOWORD(wParam),
				HIWORD(lParam), LOWORD(lParam)));
		ReleaseDC(hwnd, hdc);
		ValidateRect(hwnd, &rect);
		break;
	case WM_DESTROY:
		PostQuitMessage(0);
		return 0;
	}
	return DefWindowProc(hwnd, message, wParam, lParam);
}


I hope you all know about Tooltips, sometimes it's very difficult to implement this in Win32 (C++). I hope any Windows Developer able to solve this problem.

Windows App SDK
Windows App SDK
A set of Microsoft open-source libraries, frameworks, components, and tools to be used in apps to access Windows platform functionality on many versions of Windows. Previously known as Project Reunion.
783 questions
Windows API - Win32
Windows API - Win32
A core set of Windows application programming interfaces (APIs) for desktop and server applications. Previously known as Win32 API.
2,603 questions
C++
C++
A high-level, general-purpose programming language, created as an extension of the C programming language, that has object-oriented, generic, and functional features in addition to facilities for low-level memory manipulation.
3,709 questions
0 comments No comments
{count} votes

Accepted answer
  1. Castorix31 84,871 Reputation points
    2024-08-27T21:50:55.77+00:00

    You can do :

    At beginning (#pragma for Visual Styles) :

    #include <commctrl.h>
    #pragma comment(lib, "comctl32")
    
    #pragma comment(linker,"\"/manifestdependency:type='win32' \
    name='Microsoft.Windows.Common-Controls' version='6.0.0.0' \
    processorArchitecture='*' publicKeyToken='6595b64144ccf1df' language='*'\"")
    
    BOOL AddTool(int nMessage, HWND hWndTooltip, HWND hWnd, TCHAR* pszTooltip, RECT* rectTool, int nID);
    HWND CreateTooltip(HWND hWndParent, HWND hWnd, TCHAR* pszTooltip);
    HWND g_hWndTooltip = NULL;
    void ScreenToClientRect(HWND hWndClient, RECT& rcInOut);
    
    

    At end of WM_CREATE :

    		g_hWndTooltip = CreateTooltip(hwnd, hwnd, TEXT(""));
    		TCHAR wsBuffer[4096];		
    		for (i = 0; i < NUM; i++)
    		{
    			wsprintf(wsBuffer, TEXT("Tooltip : %d"), i);
    			if ((button[i].iStyle == BS_GROUPBOX))
    			{
    				RECT rect;
    				GetWindowRect(hwndButton[i], &rect);
    				ScreenToClientRect(hwnd, rect);
    				AddTool(TTM_ADDTOOL, g_hWndTooltip, hwnd, wsBuffer, &rect, -1);
    			}
    			else
    				AddTool(TTM_ADDTOOL, g_hWndTooltip, hwndButton[i], wsBuffer, NULL, -1);
    		}
    
    

    Utility functions :

    
    HWND CreateTooltip(HWND hWndParent, HWND hWnd, TCHAR* pszTooltip)
    {
    	HWND hWndTooltip = NULL;
    	TOOLINFO ti = { 0 };
    	RECT rect;
    	hWndTooltip = CreateWindowEx(WS_EX_TOPMOST,
    		TOOLTIPS_CLASS,
    		NULL,
    		WS_POPUP | TTS_NOPREFIX | TTS_ALWAYSTIP | WS_VISIBLE,
    		CW_USEDEFAULT,
    		CW_USEDEFAULT,
    		CW_USEDEFAULT,
    		CW_USEDEFAULT,
    		hWndParent,
    		NULL,
    		GetModuleHandle(0),
    		NULL
    	);
    	if (NULL != hWndTooltip)
    	{
    		SetWindowPos(hWndTooltip, HWND_TOPMOST, 0, 0, 0,	0,	SWP_NOMOVE | SWP_NOSIZE | SWP_NOACTIVATE);
    		GetClientRect(hWnd, &rect);
    		//ti.cbSize = sizeof(TOOLINFO);
    		ti.cbSize = (6 > SendMessage(hWndTooltip, CCM_GETVERSION, 0, 0)) ? TTTOOLINFOW_V2_SIZE : sizeof(TOOLINFO);
    		ti.uFlags = TTF_SUBCLASS;
    		ti.hwnd = hWnd;
    		ti.hinst = GetModuleHandle(0);
    		ti.uId = 0;
    		ti.lpszText = pszTooltip;
    		ti.rect = rect;		
    		BOOL bReturn = SendMessage(hWndTooltip, TTM_ADDTOOL, 0, (LPARAM)(LPTOOLINFO)&ti);
    	}
    	SetFocus(hWnd);
    	return hWndTooltip;
    }
    
    BOOL AddTool(int nMessage, HWND hWndTooltip, HWND hWnd, TCHAR* pszTooltip, RECT* rectTool, int nID)
    {
    	TOOLINFO ti = { 0 };
    	//ti.cbSize = sizeof(TOOLINFO);
    	ti.cbSize = (6 > SendMessage(hWndTooltip, CCM_GETVERSION, 0, 0)) ? TTTOOLINFOW_V2_SIZE : sizeof(TOOLINFO);
    	ti.uFlags = TTF_IDISHWND | TTF_SUBCLASS;
    	ti.hwnd = hWnd;
    	ti.hinst = GetModuleHandle(0);
    	ti.lpszText = pszTooltip;
    	if (rectTool == NULL)
    	{
    		ti.uId = (UINT)hWnd;
    		ti.uFlags = TTF_IDISHWND | TTF_SUBCLASS;
    		ti.rect.left = ti.rect.top = ti.rect.bottom = ti.rect.right = 0;
    	}
    	else
    	{
    		ti.uId = nID;
    		ti.uFlags = TTF_SUBCLASS;
    		SetRect(&ti.rect, rectTool->left, rectTool->top, rectTool->right, rectTool->bottom);
    	}
    	if (!SendMessage(hWndTooltip, nMessage, 0, (LPARAM)&ti))
    		return FALSE;	
    	return TRUE;
    }
    
    void ScreenToClientRect(HWND hWndClient, RECT& rcInOut)
    {
    	POINT ptTopLeft;
    	ptTopLeft.x = rcInOut.left;
    	ptTopLeft.y = rcInOut.top;
    	POINT ptBottomRight;
    	ptBottomRight.x = rcInOut.right;
    	ptBottomRight.y = rcInOut.bottom;
    	ScreenToClient(hWndClient, &ptTopLeft);
    	ScreenToClient(hWndClient, &ptBottomRight);
    	rcInOut.left = ptTopLeft.x;
    	rcInOut.top = ptTopLeft.y;
    	rcInOut.right = ptBottomRight.x;
    	rcInOut.bottom = ptBottomRight.y;
    }
    
    
    2 people found this answer helpful.

0 additional answers

Sort by: Most helpful

Your answer

Answers can be marked as Accepted Answers by the question author, which helps users to know the answer solved the author's problem.