Listbox selection events (LBN_SELCHANGE and LBN_DBLCLK) not firing - c++

I am creating a listbox control like this:
HWND hAvailable = CreateWindowEx(WS_EX_CLIENTEDGE, L"Listbox", NULL,
WS_CHILD | WS_VISIBLE | WS_VSCROLL |
LBS_DISABLENOSCROLL | LBS_SORT,
0, 0, 0, 0, hWnd, (HMENU)IDC_AVAILABLELIST,
hInst, NULL);
and listening for events in my callback function, under WM_COMMAND like this:
LRESULT CALLBACK WndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam) {
int wmId, wmEvent;
switch (message) {
case WM_COMMAND:
wmId = LOWORD(wParam);
wmEvent = HIWORD(wParam);
switch (wmId) {
case IDC_AVAILABLELIST:
// We get here, with LBN_SETFOCUS and LBN_KILLFOCUS
if (wmEvent == LBN_SELCHANGE || wmEvent == LBN_DBLCLK) {
// Never gets here!
}
[ ... ]
The issue is that when I change the selection, I can see the new item being selected but I am not receiving the appropriate event.
When I select an item, I get an LBN_SETFOCUS event, followed by an LBN_KILLFOCUS event. Any attempt to get the currently selected index with int idx = SendMessage(hAvailable, LB_GETCURSEL, 0, 0); returns -1 even though an item is in fact selected.

If you read the documentation for LBN_SELCHANGE and LBN_DBLCLK, they both say:
This notification code is sent only by a list box that has the LBS_NOTIFY style.
The documentation for LBS_NOTIFY says:
LBS_NOTIFY
Causes the list box to send a notification code to the parent window whenever the user clicks a list box item (LBN_SELCHANGE), double-clicks an item (LBN_DBLCLK), or cancels the selection (LBN_SELCANCEL).
You are not enabling that style on your ListBox control.
As for LB_GETCURSEL, it returns LB_ERR (-1) when there is no selection.

I had the same problem. This is true. The LBS_NOTIFY needs to be in the style.
What is very peculiar about this issue is that yesterday my code did not need the LBS_NOTIFY but today it does. So weird right! I'm using Visual Studios 2019. Also, adding the LBS_NOTIFY resolved the problem only after a reboot.

Related

Module loading and unloading when calling createwindow

I am trying to create a simple dropdown menu in a dialog box. Here is the bit of code that actually does it:
BOOL CALLBACK Remove(HWND hDlgc, UINT message, WPARAM wParam, LPARAM lParam)
//message handler for remove category box
{
//UNREFERENCED_PARAMETER(lParam);
HINSTANCE current = GetModuleHandle(NULL);
//GetModuleHandleExA(GET_MODULE_HANDLE_EX_FLAG_PIN, "comctl32.dll", NULL);
CreateWindow(WC_COMBOBOXW, _TEXT(""), CBS_DROPDOWN | CBS_HASSTRINGS | WS_CHILD | WS_OVERLAPPED | WS_VISIBLE, 100, 100, 200, 200, hDlgc, NULL, NULL, NULL, NULL);
This will work and it will show the combo box, but only after waiting for 2 minutes or so... very undesirable! my program will go into a not responding state before the combo box shows up. The output shows that comctl32.dll get loaded and unloaded about 1500 times before the combo box shows up. When it does, it is still unresponsive and I have to wait more until it begins to work. I tried pinning the module to stop the loading and unloading but that did not do anything. Any help appreciated. As you can see I am very new to win32 programming. I got the backend of my program to work nicely, its just this gui that is bugging me.
EDIT: here is the as short as i could get it code. Just create a blank desktop project in VS, and then replace the "about" function in the bottom with the following: (and also include commctrl.h)
INT_PTR CALLBACK About(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)
{
UNREFERENCED_PARAMETER(lParam);
HWND dd_Hand = CreateWindow(WC_COMBOBOXW, _TEXT(""), CBS_DROPDOWNLIST | CBS_HASSTRINGS | WS_CHILD | WS_OVERLAPPED | WS_VISIBLE,
20, 20, 200, 200, hDlg, NULL, NULL, NULL);
switch (message)
{
case WM_INITDIALOG:
return (INT_PTR)TRUE;
case WM_COMMAND:
if (LOWORD(wParam) == IDOK || LOWORD(wParam) == IDCANCEL)
{
EndDialog(hDlg, LOWORD(wParam));
return (INT_PTR)TRUE;
}
break;
}
return (INT_PTR)FALSE;
}
If I do this, I get the symptoms described previously.
EDIT AGAIN: I put the createwindow function for the combobox into the WM_CREATE case of WndProc, and everything works as it should, loads instantly. I am starting to doubt that this is the right way to create a combobox within a dialog box. Any suggestions for doing this another way (havent been able to find a way to do this with a splitbutton resource) are also welcome.
Solution was simple. just put this code:
HWND dd_Hand = CreateWindow(WC_COMBOBOXW, _TEXT(""), CBS_DROPDOWNLIST | CBS_HASSTRINGS | WS_CHILD | WS_OVERLAPPED | WS_VISIBLE,
20, 20, 200, 200, hDlg, NULL, NULL, NULL);
and the code that loads the combobox so it runs only once. No more problems. Also another even simpler way to do this would be to create a combobox resource and use the SendMessage() function.

Win32 - Subclassing Rich Edit

I'm trying to create a Windows desktop app which has a Rich Edit control, and I want to handle certain keystrokes within the control e.g. hitting enter will execute some code based on what's been typed on that line, similar to a console app. I started with the regular Windows Desktop App template in Visual Studio, and created my control within the WndProc:
case WM_CREATE:
{
LoadLibrary(TEXT("Msftedit.dll"));
//Console creation
hwndEdit = CreateWindowEx(
WS_EX_CLIENTEDGE, // extended styles
MSFTEDIT_CLASS, // Predefined class
NULL, // text
WS_TABSTOP | WS_CHILD | WS_VISIBLE | WS_VSCROLL |
ES_LEFT | ES_MULTILINE | ES_AUTOVSCROLL, // Styles
0, // x position - size and position handled separately
0, // y position
0, // Button width
0, // Button height
hWnd, // Parent window
(HMENU)ID_CONSOLE, // Control ID
(HINSTANCE)GetWindowLongPtr(hWnd, GWLP_HINSTANCE),
NULL); // Pointer not needed
}
break;
So far, so good. However, when I try to create a procedure for the control and attach it, it doesn't seem to do anything and to be honest, based on the documentation and a lot of Google searches, I'm still not entirely sure how it should be done. My procedure looks like the below:
LRESULT CALLBACK EditControlProc(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam, UINT_PTR uIdSubclass, DWORD_PTR dwRefData)
{
//static HWND hwndEdit;
switch (uMsg)
{
case WM_PAINT:
return TRUE;
// Other cases...
case WM_KEYDOWN:
switch (wParam)
{
case VK_RETURN:
MessageBox(NULL, L"You pressed enter!", L"Title", NULL);
}
/*case WM_CREATE:
{
HWND console = GetDlgItem(hWnd, ID_CONSOLE);
SetWindowSubclass(console, EditControlProc, 0, 0);
return TRUE;
}*/
}
return DefSubclassProc(hWnd, uMsg, wParam, lParam);
}
and my attempt to attach that to the control (which I based on the example here) was:
case WM_CREATE:
{
HWND console = GetDlgItem(hWnd, ID_CONSOLE);
SetWindowSubclass(console, EditControlProc, 0, 0);
return TRUE;
}
I tried handling this within various messages (the last of which was WM_CREATE as above - the link shows WM_INITDIALOG but that didn't work) with no success, and to be honest I'm not sure where I should add this in my case.
I know this is a really basic question and I feel like there should be loads of examples to show me how it's done and I might just be making a stupid mistake (far from unheard of in my short programming studentship), but I've been searching for days and haven't found what I'm looking for so would really appreciate a pointer or two.
Thanks!

How to correctly create push buttons on winApi as well as Handle its messages

So my program works, all apart from one thing, I would like for my button, 'pushBtn' , aka BTN_PUSH_TALK , to send a BN_PUSHED or BN_UNPUSHED message so I can handle it accordingly.
Following steps online, as well as trial and improvement, right now the only response I ever get is once I am done holding / clicking the button.
pushBtn = CreateWindowEx(0, L"BUTTON", L"TALK", WS_CHILD |
WS_VISIBLE |
BS_DEFPUSHBUTTON , 0 , 290 , 50, 50,
hWnd,(HMENU)BTN_PUSH_TALK, GetModuleHandle(NULL), NULL);
Handler (or at least what matters) :
LRESULT CALLBACK WndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM
lParam)
{
bool asd;
switch (message)
{
case WM_COMMAND:
{
int wmId = LOWORD(wParam);
// Parse the menu selections:
switch (wmId)
{
case BTN_PUSH_TALK:
switch (HIWORD(wParam))
{
case BN_UNPUSHED:
if (connected && inputChoiceStr == "Push To Talk") {
tplug->setDuck(false);
}
break;
case BN_PUSHED:
if (connected && inputChoiceStr == "Push To Talk") {
tplug->setDuck(true);
}
break;
}
break;
I expected once i clicked and held down the button , that the BN_PUSHED case would be entered, however it is not.
On letting go, I expect the BN_UNPUSHED case to be entered, but this was not the case either.
case BTN_PUSH_TALK is reached, meaning the button is identifiable, however the switch case within this block of code is never reached.
Buttons send WM_COMMAND on click. To achieve a push/release notification you must subclass the button class (SetWindowLongPtr() with GWLP_WNDPROC) and then handle WM_LBUTTONDOWN and WM_LBUTTONUP in your new Window Proc.
If I'm reading the question right, your goal is to get notifications when a standard push button is initially pushed by the user, whereas standard notification behavior of buttons only posts WM_COMMANDs on "clicks" where a click is the whole mouse down plus mouse up sequence.
Historically in order to get the BN_PUSHED and BN_UNPUSHED notifications in your WM_COMMAND handler you had to use the BS_NOTIFY window style when creating the button. However, if you read the documentation for BN_PUSHED or BN_UNPUSHED you will see
This notification code is provided only for compatibility with 16-bit versions of Windows earlier than version 3.0. Applications should use the BS_OWNERDRAW button style and the DRAWITEMSTRUCT structure for this task.
These were very old notifications that from what I can tell are not just deprecated but no longer even supported. You can do, however, as the documentation suggests: use an owner drawn button i.e. a button created with the BS_OWNERDRAW style.
This turns out to be more difficult than just creating the button with BS_NOTIFY turned on, because the button will no longer perform default painting by itself. Given this added chore, I'd recommend not doing it this way unless you want to custom paint your buttons anyway -- unless you happen to want some nonstandard visual look-and-feel for these buttons as well as nonstandard notification behavior. Otherwise, I would probably just do Win32 subclassing as someone else suggested to trap WM_LBUTTONDOWN etc. and then call the standard button WNDPROC after doing some action on the events i cared about.
Anyway the minimal owner drawn button that reports button down and button up events is like the following. (I post the button events as custom messages but you could do whatever you wish there)
#include <windows.h>
#define BTN_ID 101
#define WM_PUSHBUTTONDOWN WM_APP + 1
#define WM_PUSHBUTTONUP WM_APP + 2
HINSTANCE g_instance = 0;
LRESULT CALLBACK WndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam);
int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpCmdLine, int nCmdShow)
{
g_instance = hInstance;
MSG msg = { 0 };
WNDCLASS wc = { 0 };
wc.lpfnWndProc = WndProc;
wc.hInstance = hInstance;
wc.hbrBackground = reinterpret_cast<HBRUSH>(COLOR_BACKGROUND);
wc.lpszClassName = L"owner_draw_btn";
if (!RegisterClass(&wc))
return -1;
if (!CreateWindow(wc.lpszClassName, L"foobar", WS_OVERLAPPEDWINDOW | WS_VISIBLE, 0, 0, 640, 480, 0, 0, hInstance, NULL))
return -1;
while (GetMessage(&msg, NULL, 0, 0)) {
TranslateMessage(&msg);
DispatchMessage(&msg);
}
return 0;
}
LRESULT HandleDrawItem(HWND hWnd, WPARAM wParam, LPARAM lParam)
{
auto* dis = reinterpret_cast<DRAWITEMSTRUCT*>(lParam);
if (dis->CtlType != ODT_BUTTON)
return 0;
auto style = (dis->itemState & ODS_SELECTED) ?
DFCS_BUTTONPUSH | DFCS_PUSHED :
DFCS_BUTTONPUSH;
auto rect = &dis->rcItem;
DrawFrameControl(dis->hDC, rect, DFC_BUTTON, style);
TCHAR text[512];
auto n = GetWindowText(dis->hwndItem, text, 512);
DrawText(dis->hDC, text, n, rect, DT_SINGLELINE | DT_VCENTER | DT_CENTER);
if (dis->itemAction == ODA_SELECT) {
PostMessage(
hWnd,
(dis->itemState & ODS_SELECTED) ? WM_PUSHBUTTONDOWN : WM_PUSHBUTTONUP,
dis->CtlID,
0
);
}
return 0;
}
LRESULT CALLBACK WndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam)
{
switch (message)
{
case WM_CREATE:
CreateWindow(
L"button", L"foobar",
BS_OWNERDRAW | WS_CHILD | WS_VISIBLE,
10, 10, 150, 35, hWnd,
(HMENU) BTN_ID,
g_instance,
0
);
return 0;
case WM_DRAWITEM:
return HandleDrawItem(hWnd, wParam, lParam);
case WM_PUSHBUTTONDOWN:
OutputDebugString(L"Button down event\n");
break;
case WM_PUSHBUTTONUP:
OutputDebugString(L"Button up event\n");
break;
case WM_CLOSE:
PostQuitMessage(0);
return 0;
}
return DefWindowProc(hWnd, message, wParam, lParam);
}

WIN32 How to make multiple checkboxes and manage them differently?

I am trying to write a program in c++ win32 in which i need to have multiple check boxes. To be more specific, this program has multiple "pages" (after the user clicks the next button i delete any child windows and replace them with new ones).
So, to keep the code understandable, i need a function that when is called brings up a couple of fields (i know how to handle those) and some check boxes. The problem is that i don't know how to manage the HWND for each one and so any box i click just runs the same code (and does not check any of the boxes).
MSDN does not offer much help and the only source of information i found was this how to make checkbox in win32?
This is the part in my code in which the problem arrives.
/* checked_box1 , checked_box2 are globals */
LRESULT CALLBACK WindowProcedure (HWND hwnd, UINT message, WPARAM wParam,
LPARAM lParam)
{
switch (message)
{
case WM_CREATE:
draw_obtions_screen(hwnd);
break;
case WM_COMMAND:
checked_box1 = IsDlgButtonChecked(draw_object, 1);
if (checked_box1) {
CheckDlgButton(hwnd, 1, BST_UNCHECKED);
SetWindowText(hwnd, TEXT("y"));
} else {
CheckDlgButton(hwnd, 1, BST_CHECKED);
SetWindowText(hwnd,"x");
}
checked_box2 = IsDlgButtonChecked(draw_object1, 1);
if (checked_box2) {
CheckDlgButton(hwnd, 1, BST_UNCHECKED);
SetWindowText(hwnd, TEXT("Y"));
} else {
CheckDlgButton(hwnd, 1, BST_CHECKED);
SetWindowText(hwnd,"X");
}
case WM_DESTROY:
PostQuitMessage (0);
break;
default:
return DefWindowProc (hwnd, message, wParam, lParam);
}
return 0;
}
......
/* HWND draw_object,draw_object1 are global. The function is also prev declared*/
void draw_obtions_screen(HWND hwnd)
{
draw_object = CreateWindow ("BUTTON","check",
WS_VISIBLE | WS_CHILD | BS_CHECKBOX,
110, 10 ,100 ,30,
hwnd, NULL,NULL,NULL);
CheckDlgButton(hwnd, 1, BST_CHECKED);
draw_object1 = CreateWindow ("BUTTON","check another",
WS_VISIBLE | WS_CHILD | BS_CHECKBOX,
110, 60 ,130 ,30,
hwnd, NULL,NULL,NULL);
CheckDlgButton(hwnd, 1, BST_UNCHECKED);
}
The code changes the title of the window to "X" (on any box i click) and then nothing.
I will apreciate any kind of information source or answer. Also please leave some information sources for me to further learn. Thanks!
You assign the child ID through the HMENU parameter to CreateWindowo for any window that has the WS_CHILD style (that is, a child window gets a ID rather than a menu).
That ID is then sent as part of all the WM_COMMAND messages.

Button Control disappears on click - Click Handler does not get fired on Clicking Button - Win32

I am working on Zoom SDK which is based on win32 gui.
I have created 3 buttons using CreateWindow method on the window handle, which is provided by the ZoomSDK.
Code + Screenshot - 1
Now there are two problems with this.
As soon as I click the buttons, they disappear.
See the Screen Shots BEFORE
See the Screen Shots AFTER
I want to know the reason why this is happening and how can I fix this?
HWND hFirstView, hSecondView;
cntrl->GetMeetingUIWnd(hFirstView, hSecondView);
cntrl->MoveFloatVideoWnd(100, 100);
HWND btnHwnd = CreateWindow(
TEXT("button"),
L"Open App",
WS_CHILD | WS_VISIBLE | BS_DEFPUSHBUTTON,
0, 0,
50, 25,
hFirstView,
(HMENU)100,
hInst,
NULL);
HWND btnHwnd2 = CreateWindow(
TEXT("button"),
L"Other",
WS_CHILD | WS_VISIBLE | BS_DEFPUSHBUTTON,
50, 0,
50, 25,
hFirstView,
(HMENU)101,
hInst,
NULL);
HWND btnHwnd3 = CreateWindow(
TEXT("button"),
L"Raise Hand",
WS_CHILD | WS_VISIBLE | BS_DEFPUSHBUTTON,
100, 0,
50, 25,
hFirstView,
(HMENU)103,
hInst,
NULL);
HDC hdc = GetDC(btnHwnd);
SetBkColor(hdc, GetSysColor(COLOR_BTNSHADOW));
SetTextColor(hdc, GetSysColor(COLOR_BACKGROUND));
ReleaseDC(btnHwnd, hdc);
int btnId = GetDlgCtrlID(btnHwnd);
//oldWndProc = (WNDPROC) GetWindowLong(hFirstView, GWL_WNDPROC);
oldWndProc = (WNDPROC) SetWindowLong(hFirstView,
GWL_WNDPROC, (LONG)WndProc);
SendMessage(btnHwnd, BM_SETSTATE, 1, 0);
SetWindowText(hFirstView, L"Title");
I want to handle click event for these buttons. I have tried to use SetWindowsLong to set another WndProc
LRESULT CALLBACK WndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam)
{
int id = GetWindowLong(hWnd, GWL_ID);
switch (message)
{
case WM_COMMAND: {
MessageBox(NULL, L"Sign of releaf!", L"Whoaa!", 0);
if (wParam == 1023) {
MessageBox(NULL, L"Sign of releaf!", L"Whoaa!", 0);
}
}
break;
}
return CallWindowProc(oldWndProc, hWnd, message, wParam, lParam);
}
Now, this WndProc gets called for other events such mouse move etc. It is not working for my three buttons. I want to handle click event i.e WM_COMMAND or any other technique possible.
Because I can not go inside the sdk (they don't provide sources, only .lib) so I can not change their WndProc, nor their internal WM_PAINT. The buttons are sort of overlay on top.
You should not be calling SetBkColor() and SetTextColor() from outside a WM_PAINT handler. The correct way to color a button is to either:
have the parent window handle the WM_CTLCOLORBTN notification.
The WM_CTLCOLORBTN message is sent to the parent window of a button before drawing the button. The parent window can change the button's text and background colors. 
give the button the BS_OWNERDRAW style, and then have the parent window handle the WM_DRAWITEM notification.
Sent to the parent window of an owner-drawn button, combo box, list box, or menu when a visual aspect of the button, combo box, list box, or menu has changed.
Also, when a button sends a BN_CLICKED notification to its parent window, your subclass WndProc() doesn't need to use GetWindowLong(GWL_ID). First, you are calling it on the wrong HWND. And second, the button ID is carried in the message's wParam data.
LRESULT CALLBACK WndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam) {
switch (message) {
case WM_COMMAND: {
if (HIWORD(wParam) == BN_CLICKED) {
switch (LOWORD(wParam)) {
case 100:
case 101:
case 103:
MessageBox(NULL, L"Sign of relief!", L"Whoaa!", 0);
break;
}
}
break;
}
}
return CallWindowProc(oldWndProc, hWnd, message, wParam, lParam);
}