Adding ToolTip to a ComboBoxEx fails - c++

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);

Related

c++ WinAPI Layered Window, children do not paint

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);
}

How to use the SetWindowPos function?

I'd like to create a Windows app consisting of a main parent window and several child windows. Here is an excerpt of the code I have so far:
...
// -----> CHILD WINDOWS <-----
HWND hWnd_child1 = CreateWindowW(L"STATIC", L"Child 1", WS_CHILD,
0, 0, 100, 80, hParentWnd, nullptr, hInstance, nullptr);
if (!hWnd_child1) {
MessageBox(NULL, L"CreateWindowW Child 1", L"Error!", MB_ICONEXCLAMATION | MB_OK);
return FALSE;
}
HWND hWnd_child2 = CreateWindowW(L"STATIC", L"Child 2", WS_CHILD,
10, 10, 160, 120, hParentWnd, nullptr, hInstance, nullptr);
if (!hWnd_child2) {
MessageBox(NULL, L"CreateWindowW Child 2", L"Error!", MB_ICONEXCLAMATION | MB_OK);
return FALSE;
}
HWND hWnd_child3 = CreateWindowW(L"STATIC", L"Child 3", WS_CHILD,
20, 20, 160, 120, hParentWnd, nullptr, hInstance, nullptr);
if (!hWnd_child3) {
MessageBox(NULL, L"CreateWindowW Child 3", L"Error!", MB_ICONEXCLAMATION | MB_OK);
return FALSE;
}
ShowWindow(hWnd_child3, nCmdShow);
SetWindowPos(hWnd_child2, HWND_TOP, 10, 10, 100, 80, NULL);
ShowWindow(hWnd_child2, nCmdShow);
SetWindowPos(hWnd_child1, HWND_TOP, 0, 0, 100, 80, NULL);
ShowWindow(hWnd_child1, nCmdShow);
ShowWindow(hParentWnd, nCmdShow);
UpdateWindow(hParentWnd);
// -------------------
...
The problem is with the SetWindowPos() function. I can't understand how it really works. I thought that calling it like this:
ShowWindow(hWnd_child3, nCmdShow);
SetWindowPos(hWnd_child2, HWND_TOP, 10, 10, 100, 80, NULL);
ShowWindow(hWnd_child2, nCmdShow);
SetWindowPos(hWnd_child1, HWND_TOP, 0, 0, 100, 80, NULL);
ShowWindow(hWnd_child1, nCmdShow);
Would move the Child 1 window to the top of all the app windows (as the doc says for the HWND_TOP option: Places the window at the top of the Z order).
BUT, the windows are still drawned in the creation order:
Shouldn't SetWindowPos() move firstly Child 2 over Child 3, and next Child 1 over Child 2, making the windows laid up in the reverse order than they were created, with Child 1 on top?
Make the child windows all have the window style WS_CLIPSIBLINGS along with WS_CHILD etc.
From Microsoft's documentation:
If WS_CLIPSIBLINGS is not specified and child windows overlap, it is
possible, when drawing within the client area of a child window, to
draw within the client area of a neighboring child window.
Basically if the child windows do not clip each other then the order in which they are painted (which is arbitrary) determines the visual z-order.
Below for instance is your code with the message box stuff removed, using WS_VISIBLE instead of ShowWindow, adding a border for visibility, and using WS_CLIPSIBLINGS.
BOOL CreateChildren(HWND hParentWnd) {
HWND hWnd_child1 = CreateWindowEx(WS_EX_CLIENTEDGE, L"STATIC", L"Child 1", WS_VISIBLE | WS_CHILD | WS_CLIPSIBLINGS,
0, 0, 100, 80, hParentWnd, nullptr, g_hInstance, nullptr);
HWND hWnd_child2 = CreateWindowEx(WS_EX_CLIENTEDGE, L"STATIC", L"Child 2", WS_VISIBLE | WS_CHILD | WS_CLIPSIBLINGS,
10, 10, 160, 120, hParentWnd, nullptr, g_hInstance, nullptr);
HWND hWnd_child3 = CreateWindowEx(WS_EX_CLIENTEDGE, L"STATIC", L"Child 3", WS_VISIBLE | WS_CHILD | WS_CLIPSIBLINGS,
20, 20, 160, 120, hParentWnd, nullptr, g_hInstance, nullptr);
SetWindowPos(hWnd_child2, HWND_TOP, 10, 10, 100, 80, NULL);
SetWindowPos(hWnd_child1, HWND_TOP, 0, 0, 100, 80, NULL);
UpdateWindow(hParentWnd);
return TRUE;
}
which yields
child1 is on top.

Adding Tooltips to Checkboxes in ATL-based C++ Dialogs

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:

CWnd::CreateEx() fails after replacing WS_CHILD with WS_POPUP

When user click a button, I would like to bring up another window. So in the OnBnClicked..() function, I added following code:
CWnd* window = new CWnd;
BOOL result = window->CreateEx(WS_EX_CLIENTEDGE, _T("STATIC"), _T("Hi"), WS_CHILD | WS_VISIBLE, 0, 0, 20, 20, m_hWnd, (HMENU)1234);
window->ShowWindow(SW_SHOW);
This works, except it created a child window. What I need is a separate window. So I modified the code by changing "WS_CHILD" to "WS_POPUP", and "m_hWnd" to "NULL".
CWnd* window = new CWnd;
BOOL result = window->CreateEx(WS_EX_CLIENTEDGE, _T("STATIC"), _T("Hi"), WS_POPUP | WS_VISIBLE, 0, 0, 20, 20, NULL, (HMENU)1234);
window->ShowWindow(SW_SHOW);
But now, the CreateEx() call returns FALSE. I would appreciate some pointer.
[Edit:]
I updated the code as following. But still seeing the same issue. Also, even when CreateEx() returns FALSE, GetLastError() actually returns 0.
CWnd* window = new CalibrationWindow;
LPCTSTR className = NULL;
if (!className) {
className = AfxRegisterWndClass(
CS_VREDRAW | CS_HREDRAW,
::LoadCursor(NULL, IDC_ARROW),
(HBRUSH) ::GetStockObject(WHITE_BRUSH),
::LoadIcon(NULL, IDI_APPLICATION));
}
BOOL isValid = ::IsWindow(GetSafeHwnd());
BOOL result = window->CreateEx(0, _T(className), _T("Hi"), WS_POPUP | WS_VISIBLE, 0, 0, 20, 20, m_hWnd, (HMENU)1234);
DWORD errorCode;
if (!result) {
errorCode = GetLastError();
}
window->ShowWindow(SW_SHOW);
The nID or nIDorHMenu parameter for CWnd::CreateEx is an 'overloaded' parameter that either specifies a control ID or a menu ID.
For a child window (WS_CHILD window styles) it is the control identifier. You can specify any value as long as it is unique among all child windows with the same parent window.
For a popup window on the other hand (a window that does have WS_CHILD window style) it is the menu identifier. In this case it has to be a valid HMENU. Passing a made up value will cause window creation to fail.
Here's how to create a window out of your CWnd-derived class using a different method:
CWndDerived myWnd;
LPCTSTR className = AfxRegisterWndClass(CS_VREDRAW | CS_HREDRAW,
nullptr,
(HBRUSH) ::GetStockObject(WHITE_BRUSH),
nullptr);
myWnd.CreateEx(
0, // WS_EX_TOPMOST
className,
_T("Title"),
WS_POPUP | WS_BORDER | WS_CAPTION | WS_MAXIMIZEBOX | WS_SYSMENU,
0, 0, 300, 200,
m_hWnd,
nullptr,
nullptr);
This allows you to have your CWndDerived myWnd as a member variable (should you need it) of your other window and retain all the goodies that come with MSVC's MFC class editor.
I hope this helps

Wrong symbols in the buffer using GetWindowText

There is combo box with two items and button on the main window. Combobox:
HWND hCombo;
hCombo = CreateWindow(L"COMBOBOX", L"combobox",
WS_CHILD | WS_VISIBLE | CBS_SORT | CBS_DROPDOWNLIST,
10, 55, 232, 500, hWnd, 0, hInstance, 0);
const wchar_t *langEnglish = L"English";
const wchar_t *langRussian = L"Russian";
SendMessage(hCombo, CB_ADDSTRING, 0, (LPARAM)langEnglish);
SendMessage(hCombo, CB_ADDSTRING, 0, (LPARAM)langRussian);
SendMessage(hCombo, CB_SETCURSEL, 0, 0);
I am trying to get selected item text in the WndProc by clicking on the button:
case WM_COMMAND:
{
switch(LOWORD(wParam))
{
case IDC_BUTTON_OK:
wchar_t buf[10];
hCombo = GetDlgItem(hWnd, IDC_COMBO);
GetDlgItemText(hCombo, IDC_COMBO, (LPWSTR)buf, 10);
MessageBox(hWnd, (LPCWSTR)buf, NULL, MB_OK);
break;
}
} break;
I am using breakpoint in the MSVS2010 to see buf variable. It contains chinese symbols!!! Message box shows empty message (With the title "Error"). I want to see english text. What is wrong?
This code
nIndex = SendMessage(hCombo, CB_GETCURSEL, 0, 0);
SendMessage(hCombo, CB_GETLBTEXT, nIndex, (LPARAM)buf);
fills buf with the same chinese symbols
SOLUTION:
hCombo = CreateWindow(L"COMBOBOX", L"combobox",
WS_CHILD | WS_VISIBLE | CBS_SORT | CBS_DROPDOWNLIST,
10, 55, 232, 500, hWnd, (HMENU)IDC_COMBO, hInstance, 0);
In order to get currently selected item from CBS_DROPDOWNLIST styled combo box you need CB_GETCURSEL to get selection index and then CB_GETLBTEXT to get the string.