I am currently working on a project, where i need to create transparent bubble as main body. To make this transparence i used WS_EX_LAYERED on window creation. But now, i can't get children to render, but they do exist and even function like they should, they are just not visible. Without Layered Window mode everything renders just fine. What is wrong?
Here is my window creation:
winClass = {};
winClass.lpfnWndProc = WindowProc;
winClass.hInstance = hInstance;
winClass.hIcon = hIcon;
winClass.hCursor = LoadCursor(NULL, IDC_ARROW);
winClass.lpszClassName = CLASS_NAME;
winClass.hbrBackground = (HBRUSH)0;
winClass.lpszMenuName = NULL;
winClass.cbClsExtra = 0;
winClass.cbWndExtra = 0;
RegisterClass(&winClass);
Gdiplus::GdiplusStartupInput gdiinput = {};
gdiinput.GdiplusVersion = 1;
Gdiplus::GdiplusStartupOutput gdioutput = {};
Gdiplus::GdiplusStartup(&WinGDIToken, &gdiinput, &gdioutput);
hWnd = CreateWindowEx(
WS_EX_TOOLWINDOW | WS_EX_TOPMOST | WS_EX_LAYERED,
CLASS_NAME, // Window class
L"Color Picker", // Window text
WS_VISIBLE | WS_POPUP | WS_CLIPCHILDREN, // Window style
// Size and position
CW_USEDEFAULT, CW_USEDEFAULT, 700 * Scale, 516 * Scale,
NULL, // Parent window
NULL, // Menu
hInstance, // Instance handle
NULL // Additional application data
);
Window::PopulateClientWithWindows(hWnd);
And here is PopulateClientWithWindows(hwnd) func:
void Window::PopulateClientWithWindows(HWND hwnd)
{
HWND test = CreateWindow(L"edit", L"Test ", WS_VISIBLE | WS_CHILD , 50, 50,
100, 100, hwnd, NULL, hI, NULL);
}
Related
I'm trying to create a window with c++ winapi (CreateWindowExW, RegisterClassExW).
The window is dark, has the mica theme, and is borderless.
The window itself works fine, but when I'm trying to create any kind of child window (in this example, a button) it just won't show on the parent.
Does anyone why?
The window and button creation is as such:
WNDCLASSEXW wcx{};
wcx.cbSize = sizeof(wcx);
wcx.style = CS_HREDRAW | CS_VREDRAW;
wcx.hInstance = nullptr;
wcx.lpfnWndProc = wndproc;
wcx.lpszClassName = L"RunUIClass";
wcx.hbrBackground = reinterpret_cast<HBRUSH>(COLOR_WINDOW + 1);
wcx.hCursor = ::LoadCursorW(nullptr, IDC_ARROW);
ATOM result = ::RegisterClassExW(&wcx);
auto handle = CreateWindowExW(
WS_EX_NOREDIRECTIONBITMAP | WS_EX_TOPMOST, L"RunUIClass", L"RunUI Window",
WS_POPUP | WS_THICKFRAME | WS_CAPTION | WS_SYSMENU | WS_MAXIMIZEBOX | WS_MINIMIZEBOX, GetSystemMetrics(SM_CXSCREEN) / 2 - (APP_WIDTH / 2), GetSystemMetrics(SM_CYSCREEN) / 2 - (APP_HEIGHT / 2),
APP_WIDTH, APP_HEIGHT, nullptr, nullptr, nullptr, userdata
);
auto button = CreateWindow(WC_BUTTON, L"Button",
WS_VISIBLE | WS_CHILD | BS_PUSHBUTTON,
50, 50,
BUTTON_SIZE, BUTTON_SIZE,
handle, NULL, NULL, NULL);
Im making a game with a custom game engine and when you have selected the window that it creates it doesn't allow you to use media keys e.g. changing volume or playing/pausing music or anything that has to do with windows like getting the windows start menu and alt+tab behaves weird
It feels like my window is "blocking" all system specific keys and commands
The code is written in c++
Heres the code i'm using for creating the window:
bool FrameWork::CreateDXWnd(int x, int y, int width, int height)
{
HWND hwnd;
WNDCLASSEX wc;
m_hInstance = GetModuleHandle(nullptr);
//setup window class with default setings:
wc.style = CS_HREDRAW | CS_VREDRAW | CS_OWNDC;
wc.lpfnWndProc = WndProc;
wc.cbClsExtra = 0;
wc.cbWndExtra = 0;
wc.hInstance = m_hInstance;
//wc.hIcon = LoadIcon(nullptr, IDI_WINLOGO);
wc.hIcon = (HICON)LoadImage(m_hInstance, ".\\Assets\\Icons\\NgineIcon512.ico", IMAGE_ICON, 32, 32, LR_LOADFROMFILE);
wc.hIconSm = wc.hIcon;
wc.hCursor = LoadCursor(nullptr, IDC_HAND);
wc.hbrBackground = (HBRUSH)GetStockObject(BLACK_BRUSH);
wc.lpszMenuName = nullptr;
wc.lpszClassName = applicationName.c_str();
wc.cbSize = sizeof(WNDCLASSEX);
if (!RegisterClassEx(&wc))
{
Error(1);
return false;
}
//Style of window
//int nStyle = WS_OVERLAPPED | WS_SYSMENU | WS_VISIBLE | WS_CAPTION | WS_MINIMIZEBOX;
int nStyle = WS_OVERLAPPED | WS_SYSMENU | WS_VISIBLE | WS_CAPTION | WS_MINIMIZEBOX;
SettingsManager::GetInstance()->SetNativeResolution(GetSystemMetrics(SM_CXSCREEN), GetSystemMetrics(SM_CYSCREEN));
if (SettingsManager::GetInstance()->GetDisplayMode() == FULLSCREEN)
{
DEVMODE dmScreenSettings;
memset(&dmScreenSettings, 0, sizeof(dmScreenSettings));
dmScreenSettings.dmSize = sizeof(dmScreenSettings);
dmScreenSettings.dmPelsWidth = (unsigned long)SettingsManager::GetInstance()->GetScreenWidth();
dmScreenSettings.dmPelsHeight = (unsigned long)SettingsManager::GetInstance()->GetScreenHeight();
dmScreenSettings.dmBitsPerPel = 32;
dmScreenSettings.dmFields = DM_BITSPERPEL | DM_PELSWIDTH | DM_PELSHEIGHT;
ChangeDisplaySettings(&dmScreenSettings, CDS_FULLSCREEN);
}
else
{
}
if ((SettingsManager::GetInstance()->GetDisplayMode() == BORDERLESS))
{
hwnd = CreateWindowEx(WS_EX_APPWINDOW, applicationName.c_str(), applicationName.c_str(), WS_POPUP, x, y, SettingsManager::GetInstance()->GetScreenWidth(), SettingsManager::GetInstance()->GetScreenHeight(), nullptr, nullptr, m_hInstance, nullptr);
}
else
{
hwnd = CreateWindowEx(WS_EX_APPWINDOW, applicationName.c_str(), applicationName.c_str(), nStyle, x, y, SettingsManager::GetInstance()->GetScreenWidth(), SettingsManager::GetInstance()->GetScreenHeight(), nullptr, nullptr, m_hInstance, nullptr);
}
if (hwnd == nullptr)
{
Error(2);
Ngine::GetInstance()->Release();
PostQuitMessage(0);
return false;
}
if (!Ngine::GetInstance()->InitGraphics(hwnd))
{
Error(hwnd, 30);
Ngine::GetInstance()->Release();
PostQuitMessage(0);
UnregisterClass(applicationName.c_str(), m_hInstance);
m_hInstance = nullptr;
DestroyWindow(hwnd);
return false;
}
Ngine::GetInstance()->GetGraphics()->SetHwnd(hwnd);
ShowWindow(hwnd, SW_SHOW);
SetForegroundWindow(hwnd);
SetFocus(hwnd);
return true;
}
Tim. The code you are showing deals with creating a window, not with how to handle the input to the window. To handle input, you need to set up a Message handling loop in your code.
Typically, in a game engine, you will have a main loop or "Game Loop", with each pass through the loop typically resulting in a single frame drawn. The first thing the Game Loop does is handle window messages. This allows you to handle the typical windows functions. Then, once you have no more messages to deal with, you will move on to handling your game's logic and rendering.
I recommend you take a look at Braynzarsoft's tutorials. The tutorial I linked deals with setting up your window and how to make a bare-bones Windows Message system.
Once you understand the basics of that, you can refine your post if needed to get more information.
Consider the code below where 2 different kinds of combo boxes are created(WC_COMBOBOX and WC_COMBOBOXEX), and then each is attached a tool tip.
Tool tip for WC_COMBOBOX works as expected, but WC_COMBOBOXEX fails to display the tool tip.
What is the problem?
BOOL TooltipDlg_OnInitDialog(HWND hWndDialog, HWND hWndFocus, LPARAM lParam)
{
// Load and register Tooltip, ComboBox, ComboBoxEx control classes
INITCOMMONCONTROLSEX iccx;
iccx.dwSize = sizeof(INITCOMMONCONTROLSEX);
iccx.dwICC = ICC_WIN95_CLASSES | ICC_USEREX_CLASSES;
if (!InitCommonControlsEx(&iccx))
return FALSE;
// Create combo boxes
const int idc_ComboBox = 1000;
const int idc_ComboBoxEx = 1001;
{
// create WC_COMBOBOX
CreateWindow(WC_COMBOBOX, NULL,
WS_CHILD | WS_VISIBLE | CBS_DROPDOWNLIST,
40, 80,
100, 20,
hWndDialog, (HMENU)idc_ComboBox, g_hInst,
NULL);
// create WC_COMBOBOXEX
CreateWindowEx(0, WC_COMBOBOXEX, NULL,
WS_CHILD | WS_VISIBLE | CBS_DROPDOWNLIST,
40, 110,
100, 20,
hWndDialog, (HMENU)(idc_ComboBoxEx), g_hInst,
NULL);
}
// Create tooltip
g_hwndTooltip = CreateWindowEx(0, TOOLTIPS_CLASS, L"",
TTS_ALWAYSTIP,
0, 0, 0, 0,
hWndDialog, 0, g_hInst, 0);
// attach the tooltip to controls
{
TOOLINFO ti;
ti.cbSize = sizeof(ti);
ti.uFlags = TTF_IDISHWND | TTF_SUBCLASS;
// attach to idc_ComboBox -- works fine
ti.uId = (UINT_PTR)GetDlgItem(hWndDialog, idc_ComboBox);
ti.lpszText = L"This is tooltip for WC_COMBOBOX.";
SendMessage(g_hwndTooltip, TTM_ADDTOOL, 0, (LPARAM)&ti);
// attach to idc_ComboBoxEx -- does NOT work: no tooltip displayed
ti.uId = (UINT_PTR)GetDlgItem(hWndDialog, idc_ComboBoxEx);
ti.lpszText = L"This is tooltip for WC_COMBOBOXEX.";
SendMessage(g_hwndTooltip, TTM_ADDTOOL, 0, (LPARAM)&ti);
}
return TRUE;
}
WC_COMBOBOXEX create 2 windows - parent and child combo box control, which have the same size as parent and all mouse messages go to this child, not for parent. so need subclass child combobox control. we can get it via CBEM_GETCOMBOCONTROL message. so code must look like:
HWND hwndCBex = CreateWindowEx(0, WC_COMBOBOXEX, ...);
ti.uId = (UINT_PTR)SendMessage(hwndCBex, CBEM_GETCOMBOCONTROL, 0, 0);
ti.lpszText = L"This is tooltip for WC_COMBOBOXEX.";
SendMessage(g_hwndTooltip, TTM_ADDTOOL, 0, (LPARAM)&ti);
I can't find a way to disable checkboxes in my TreeView control on specific items (actually I only need to enable checkboxes on specific items).
I have read this, this and this answer to no avail.
When creating the treeview items (that don't need checkboxes) I tried to set flags to :
tvinsert.item.mask = TVIF_TEXT | TVIF_STATE | TVIF_PARAM; // attributes
tvinsert.item.stateMask = TVIS_STATEIMAGEMASK;
tvinsert.item.state = INDEXTOSTATEIMAGEMASK(0);
which are supposed to hide an item's checkbox but MSDN documentation says
Version 5.80. Displays a check box even if no image is associated with
the item.
I am creating the treeview window control with
g_WindowHandleTreeView = CreateWindow(
WC_TREEVIEW,
"", //caption not required
TVS_TRACKSELECT | WM_NOTIFY | WS_CHILD | TVS_HASLINES | TVS_LINESATROOT | WS_VISIBLE/* | TVS_CHECKBOXES*/,
CW_USEDEFAULT,
CW_USEDEFAULT,
300,
550,
g_WindowHandlePannelStructure,
NULL,
(HINSTANCE)GetWindowLong(g_WindowHandlePannelStructure, GWL_HINSTANCE),
NULL);
DWORD dwStyle = GetWindowLong(g_WindowHandleTreeView, GWL_STYLE);
dwStyle |= TVS_CHECKBOXES;
SetWindowLongPtr(g_WindowHandleTreeView, GWL_STYLE, dwStyle);
and then creating treeview items with
// Clear the treeview
TreeView_DeleteAllItems(hwnd);
// Tree items
std::vector<HTREEITEM> root_sub;
std::vector<HTREEITEM> mesh_items;
std::vector<HTREEITEM> mesh_items_sub;
TV_INSERTSTRUCT tvinsert = { 0 }; // struct to config the tree control
tvinsert.hParent = TVI_ROOT; // top most level Item
tvinsert.hInsertAfter = TVI_LAST; // root level item attribute.
tvinsert.item.mask = TVIF_TEXT | TVIF_PARAM; // attributes
tvinsert.item.stateMask = TVIS_STATEIMAGEMASK;
tvinsert.item.state = INDEXTOSTATEIMAGEMASK(0);
// ^^^ here trying to disable the checkbox but only prior to Version 5.80. ?
// Create root item
std::string rootTxt = "Model";
tvinsert.item.pszText = (LPSTR)rootTxt.c_str();
tvinsert.item.lParam = ID_MESH_ALL;
HTREEITEM Root = (HTREEITEM)SendMessage(hwnd, TVM_INSERTITEM, 0, (LPARAM)&tvinsert);
// Create path item
std::string pathTxt = std::string("Path : ") + pModel->objPath;
tvinsert.hParent = Root;
tvinsert.item.pszText = (LPSTR)pathTxt.c_str();
tvinsert.item.lParam = 0;
root_sub.push_back((HTREEITEM)SendMessage(hwnd, TVM_INSERTITEM, 0, (LPARAM)&tvinsert));
// More items....................
// Now attempting to change flags to ENABLE+CHECK the checkbox (which are always enabled anyways...)
tvinsert.item.state = INDEXTOSTATEIMAGEMASK(2);
// Create mesh header
std::string meshTxt = std::string("Mesh #") + std::to_string(mesh_items.size() + 1) + std::string(" - ") + std::to_string(mesh.v.size()) + std::string(" vertices");
tvinsert.hInsertAfter = mesh_root;
tvinsert.hParent = mesh_root;
tvinsert.item.pszText = (LPSTR)meshTxt.c_str();
tvinsert.item.lParam = ID_MESH_0 + mesh_items.size();
mesh_items.push_back((HTREEITEM)SendMessage(hwnd, TVM_INSERTITEM, 0, (LPARAM)&tvinsert));
// Disable flags
tvinsert.item.state = INDEXTOSTATEIMAGEMASK(0);
// ...
So what's the other way around ? I don't understand what subclassing my TreeView control is supposed to mean apart from giving it a different windows proc.
Expected behavior is having a checkbox displayed only next to select treeview items. I currently have a checkbox for all items.
Thanks for your insight.
Here is how to create a treeview control with checkboxes and removing checkboxes on select nodes.
First create a window control without the TVS_CHECKBOXES checkbox style. For example :
g_WindowHandleTreeView = CreateWindow(
WC_TREEVIEW,
"",
TVS_TRACKSELECT | WS_CHILD | TVS_HASLINES | TVS_LINESATROOT | WS_VISIBLE | TVS_HASBUTTONS,
CW_USEDEFAULT,
CW_USEDEFAULT,
300,
550,
g_WindowHandlePannelStructure, // is the parent window control
NULL,
(HINSTANCE)GetWindowLong(g_WindowHandlePannelStructure, GWL_HINSTANCE),
NULL);
Then add the checkbox style :
DWORD dwStyle = GetWindowLong(g_WindowHandleTreeView, GWL_STYLE);
dwStyle |= TVS_CHECKBOXES;
SetWindowLongPtr(g_WindowHandleTreeView, GWL_STYLE, dwStyle);
Now preparing an item for the treeview with an insert struct such as :
TV_INSERTSTRUCT tvinsert = { 0 }; // struct to config the tree control
tvinsert.hParent = TVI_ROOT; // root item
tvinsert.hInsertAfter = TVI_LAST; // last current position
tvinsert.item.mask = TVIF_TEXT | TVIF_PARAM | TVIF_STATE; // attributes
tvinsert.item.stateMask = TVIS_STATEIMAGEMASK;
tvinsert.item.state = 0;
tvinsert.item.pszText = (LPSTR)"Root node";
tvinsert.item.lParam = SOME_ID; // ID for the node
And inserting the node with a SendMessage(...) call :
HTREEITEM Root = (HTREEITEM)SendMessage(hwnd, TVM_INSERTITEM, 0, (LPARAM)&tvinsert);
The node will display a checkbox at this point (even with item.state set to 0) so all that's left to do is removing it :
TVITEM tvi;
tvi.hItem = Root; // The item to be "set"/modified
tvi.mask = TVIF_STATE;
tvi.stateMask = TVIS_STATEIMAGEMASK;
tvi.state = 0; // setting state to 0 again
TreeView_SetItem(hwnd, &tvi);
That's it.
Here is a small demo that shows how to implement NM_TVSTATEIMAGECHANGING:
#include <windows.h>
#include <windowsx.h>
#include <commctrl.h>
#pragma comment( linker, "/manifestdependency:\"type='win32' \
name='Microsoft.Windows.Common-Controls' version='6.0.0.0' \
processorArchitecture='*' publicKeyToken='6595b64144ccf1df' \
language='*'\"")
#pragma comment( lib, "comctl32.lib")
// control IDs
#define IDC_TREEVIEW 2000
// init treeview
BOOL InitTreeView(HWND hwndTV)
{
// enable checkboxes, the way it was recommended in MSDN documentation
DWORD dwStyle = GetWindowLong(hwndTV, GWL_STYLE);
dwStyle |= TVS_CHECKBOXES;
SetWindowLongPtr(hwndTV, GWL_STYLE, dwStyle);
TVINSERTSTRUCT tvis = { 0 };
tvis.item.mask = TVIF_TEXT | TVIF_STATE;
tvis.hInsertAfter = TVI_FIRST;
tvis.hParent = NULL;
tvis.item.pszText = L"Root item";
HTREEITEM hti = (HTREEITEM)TreeView_InsertItem(hwndTV, &tvis);
if (NULL == hti)
return FALSE;
tvis.hParent = hti;
tvis.item.pszText = L"Second child node";
tvis.item.stateMask = TVIS_STATEIMAGEMASK;
tvis.item.state = 0 << 12;
HTREEITEM htiChild = TreeView_InsertItem(hwndTV, &tvis);
if (NULL == htiChild)
return FALSE;
tvis.item.pszText = L"First child node";
tvis.item.stateMask = TVIS_STATEIMAGEMASK;
tvis.item.state = 0 << 12;
htiChild = TreeView_InsertItem(hwndTV, &tvis);
if (NULL == htiChild)
return FALSE;
// remove checkbox
TreeView_SetItemState(hwndTV, htiChild, 0, TVIS_STATEIMAGEMASK);
// expand the root node
TreeView_Expand(hwndTV, hti, TVE_EXPAND);
// if we came all the way here then all is fine, report success
return TRUE;
}
// main window procedure
LRESULT CALLBACK WndProc(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam)
{
switch (msg)
{
case WM_CREATE:
{
//================ create controls
RECT rec = { 0 };
GetClientRect(hwnd, &rec);
HWND hwndTV = CreateWindowEx(0, WC_TREEVIEW, L"TreeView",
WS_CHILD | WS_VISIBLE | WS_BORDER |
TVS_FULLROWSELECT | TVS_HASBUTTONS |
TVS_HASLINES | TVS_LINESATROOT |
TVS_DISABLEDRAGDROP,
10, 10, 200, 200,
hwnd, (HMENU)IDC_TREEVIEW,
((LPCREATESTRUCT)lParam)->hInstance, NULL);
// initialize treeview
if (!InitTreeView(hwndTV))
return -1;
}
return 0L;
case WM_NOTIFY:
{
switch (((LPNMHDR)lParam)->code)
{
case NM_TVSTATEIMAGECHANGING:
{
// if item did not have checkbox, prevent state image change
// NOTE: this approach does not work if you programatically change item's state !!!
return (((LPNMTVSTATEIMAGECHANGING)lParam)->iOldStateImageIndex == 0);
}
break;
default:
break;
}
}
break;
case WM_CLOSE:
::DestroyWindow(hwnd);
return 0L;
case WM_DESTROY:
{
::PostQuitMessage(0);
}
return 0L;
default:
return ::DefWindowProc(hwnd, msg, wParam, lParam);
}
return 0;
}
// WinMain
int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpCmdLine,
int nCmdShow)
{
WNDCLASSEX wc;
HWND hwnd;
MSG Msg;
// register main window class
wc.cbSize = sizeof(WNDCLASSEX);
wc.style = 0;
wc.lpfnWndProc = WndProc;
wc.cbClsExtra = 0;
wc.cbWndExtra = 0;
wc.hInstance = hInstance;
wc.hIcon = LoadIcon(hInstance, IDI_APPLICATION);
wc.hCursor = LoadCursor(NULL, IDC_ARROW);
wc.hbrBackground = GetSysColorBrush(COLOR_WINDOW);
wc.lpszMenuName = NULL;
wc.lpszClassName = L"Main_Window";
wc.hIconSm = LoadIcon(hInstance, IDI_APPLICATION);
if (!RegisterClassEx(&wc))
{
// simple error indication
MessageBeep(0);
return 0;
}
// initialize common controls
INITCOMMONCONTROLSEX iccex;
iccex.dwSize = sizeof(INITCOMMONCONTROLSEX);
iccex.dwICC = ICC_TREEVIEW_CLASSES | ICC_LISTVIEW_CLASSES | ICC_STANDARD_CLASSES;
InitCommonControlsEx(&iccex);
// create main window
hwnd = CreateWindowEx(0, L"Main_Window", L"Demonstration App",
WS_OVERLAPPEDWINDOW,
CW_USEDEFAULT, CW_USEDEFAULT,
CW_USEDEFAULT, CW_USEDEFAULT,
NULL, NULL, hInstance, 0);
if (NULL == hwnd)
{
// simple error indication
MessageBeep(0);
return 0;
}
ShowWindow(hwnd, nCmdShow);
UpdateWindow(hwnd);
while (GetMessage(&Msg, NULL, 0, 0) > 0)
{
TranslateMessage(&Msg);
DispatchMessage(&Msg);
}
return Msg.wParam;
}
It worked for me on Windows 7, all you have to do is copy/paste this code into .cpp file and run it on Windows 10.
According to the comments, NM_TVSTATEIMAGECHANGING does not catch programmatic changes ( see the comment at the very bottom).
You might be better off with TVN_ITEMCHANGING, as suggested in the comments if you think of programmatically changing the state (on button click for example or whatever...).
I'm attempting to add a set of tooltips to a set of checkboxes on a dialog inheriting from CAxDialogImpl, where CSG32GridViewControlDlg is my dialog's class.
Using the example code on MSDN as a base, I've added the following code:
void CSG32GridViewControlDlg::AddCheckboxTooltip(const int toolId, PTSTR tooltipText)
{
HWND hCheckbox = GetDlgItem(toolId);
char label1[501];
::GetWindowText(hCheckbox, label1, 500);
HINSTANCE hInstance = _AtlBaseModule.GetResourceInstance( ); // This bit I'm not sure about...
// Need to create the ToolTip first
HWND hWndToolTip = ::CreateWindowEx(NULL, TOOLTIPS_CLASS, NULL,
WS_POPUP | TTS_ALWAYSTIP | TTS_BALLOON,
CW_USEDEFAULT, CW_USEDEFAULT,
CW_USEDEFAULT, CW_USEDEFAULT,
hCheckbox,
NULL,
hInstance, 0);
if (!hCheckbox || !hWndToolTip)
{
return;
}
TOOLINFO toolInfo = { 0 };
toolInfo.cbSize = sizeof(toolInfo);
toolInfo.hwnd = hCheckbox;
toolInfo.uFlags = TTF_IDISHWND | TTF_SUBCLASS;
toolInfo.uId = (UINT_PTR)hWndToolTip;
toolInfo.lpszText = tooltipText;
LRESULT result = ::SendMessage(hWndToolTip, TTM_ADDTOOL, 0, (LPARAM) &toolInfo);
return hWndToolTip;
}
Debugging the code, I can see that the message gets sent, and the result comes back as "1", which would seem to suggest that everything's worked successfully. But when I mouseover the checkbox in question... no tooltip.
What could I do to check if the tooltip is successfully registered, and why might it not be appearing on Mouseover? Alternatively, is there a better way to approach this?
There are a few problems with your code. The starting point worth mentioning is obviosuly this: How to Create a Tooltip for a Control.
Parent window for the tooltip control should be the dialog, not the checkbox
uId member for the tool with TTF_IDISHWND flag needs to be the tool window, and not tooltip control window
This gives code:
HWND AddCheckboxTooltip(const int toolId, PTSTR tooltipText)
{
HWND hCheckbox = GetDlgItem(toolId);
// Need to create the ToolTip first
HWND hWndToolTip = ::CreateWindowEx(NULL, TOOLTIPS_CLASS, NULL,
WS_POPUP | TTS_ALWAYSTIP | TTS_BALLOON,
CW_USEDEFAULT, CW_USEDEFAULT,
CW_USEDEFAULT, CW_USEDEFAULT,
m_hWnd,
NULL,
_AtlBaseModule.GetModuleInstance(), 0);
if (!hCheckbox || !hWndToolTip)
return NULL;
TOOLINFO toolInfo = { 0 };
toolInfo.cbSize = sizeof(toolInfo);
toolInfo.hwnd = hCheckbox;
toolInfo.uFlags = TTF_IDISHWND | TTF_SUBCLASS;
toolInfo.uId = (UINT_PTR)hCheckbox;
toolInfo.lpszText = tooltipText;
LRESULT result = ::SendMessage(hWndToolTip, TTM_ADDTOOL,
0, (LPARAM) &toolInfo);
return hWndToolTip;
}
LRESULT OnInitDialog(UINT, WPARAM, LPARAM, BOOL& /*bHandled*/)
{
AddCheckboxTooltip(IDC_CHECK1, _T("Checkbox Tooltip Test"));
//...
Which runs as: