Capturing specific keystrokes from a Listbox - winapi

I'm trying to make my main window to receive notifications when user presses a Delete key on a listbox's item. I've tried this:
case WM_CHARTOITEM:
if( lParam == (LPARAM)hwndListBox )
{
sprintf( debug, "0x%x", LOWORD(wParam) );
MessageBoxA(0, debug, 0, 0);
}
break;
..and I got all keystrokes except the Delete (and End, Home, Inert, PageUp, PageDown, arrows etc). Though I got Numpad's Delete keystroke.
Is there a way to do this?
Thank you.

Those keys don't produce a WM_CHAR message. You'll need WM_VKEYTOITEM to see them.

Related

Any suggestions to effectively update the status bar of an application?

The status bar window of this program needs to be updated every time the user press a key that is likely to move the caret of the EDIT control, and the code below works like a charm! In a nutshell, pressing a key on the keyboard will update some values and send a message "ECM_GETLINEINFOS" that is next processed in the main window procedure (code below)
However, there is flickering that is not disturbing, of course, but I wonder if it's related to how I set the text on the status bar (maybe too many updates ?) or just a problem with the drawing part.
PS: The flickering occurs on the text, not the status bar in itself, so that is why I'm questioning how I should manage the update of my window.
constexpr int failed_val = -1;
LRESULT MainWindow::HandleMessage(UINT msg, WPARAM wParam, LPARAM lParam)
{
switch (msg)
{
// Custom message sent by an EDIT control, I
// use this message to tell the status bar it must update its text.
case CEM_GETLINEINFO:
{
const size_t buffSz = 24;
std::wstring buffer(buffSz, L'\0');
int line = LOWORD(wParam);
int column = HIWORD(wParam);
int count = _snwprintf_s(buffer.data(), buffer.size(),
_TRUNCATE, L"Ln %d, Col %d", line, column);
if (count != failed_val) {
// Param 1 : The text to be displayed
// Param 2 : Which status bar part
m_statusBar->SetText(buffer, 0);
}
}
return 0;
}
}
Just as Flicker-Free Displays Using an Off-Screen DC directed by the answer said,
What makes this window flicker when we update it frequently? The
answer is that Windows asks the window procedure to repaint the window
as a two-step process. First, it sends a WM_ERASEBKGND message and
then a WM_PAINT message. The default handling for the WM_ERASEBKGND
message is to fill the area with the current window background color.
So the sequence of events is first to fill the area with solid color
and then to draw the text on top. The net result of doing this
frequently is that the window state alternates between its erased
state and its drawn stateā€”it flickers.
And
To prevent the control from flickering when we update it frequently,
we need to make two changes to how the control handles messages.
First, we need to prevent Windows from providing the default handling
of WM_ERASEBKGND messages. Secondly, we need to handle WM_PAINT
messages so that the background is painted with the window background
color and so that the changes to the control's client area happen at
once.
A status bar flicker free solution in .NET: Searching Visual Studio .NET style status bar. Or Simple Mode Status Bars could be enough.

Disable cursor changing to SizeWE for some items in Win32 header

I need to prevent resizing of some items in a Win32 header control. No problem to process the HDN_BEGINTRACK notification message and cancel it - the problem is in the cursor indicating that the item can be resized. For instance, if the first item can't be resized, I see this:
, but I'd prefer to see this:
I can ignore the cursor change by suppressing the WM_SETCURSOR message, but the problem is how to know the header item WM_SETCURSOR is generated for. I can detect the item under the mouse pointer in WM_MOUSEMOVE using the HDM_HITTEST message, but WM_MOUSEMOVE is sent to window procedure only after WM_SETCURSOR. I analyzed all notification messages for the Win32 header control, and it seems, it does not have an equivalent of the MouseEnter event that is sent to the window procedure before WM_SETCURSOR.
Any ideas how to solve this problem?
You need to sub-class the header control if you haven't already.
In the sub-class, intercept the WM_SETCURSOR message, and use GetMessagePos() to get the coordinates of the mouse. These are in screen coordinates, so you need to convert them to client coordinates for the header control hit test.
// in the window sub-class
if (uMsg == WM_SETCURSOR)
{
DWORD dwPos = GetMessagePos();
HDHITTESTINFO hti;
hti.pt.x = GET_X_LPARAM(dwPos);
hti.pt.y = GET_Y_LPARAM(dwPos);
ScreenToClient(hWnd, &hti.pt);
SendMessage(hWnd, HDM_HITTEST, 0, reinterpret_cast<LPARAM>(&hti));
if (...) // test for items we want to block
{
SetCursor(LoadCursor(0, IDC_ARROW));
return TRUE;
}
// pass through to regular WndProc
}

Double click in empty area of CListBox does not call my double click function

I am using Visual Studio MFC for GUI programming.
I currently have a CListBox, and I want it to call a function when I double click on an empty part of it. (when no item is selected) Currently, I am only able to add items to it by pressing a separate button.
I made the following test code to test whether the CListBox is responding to a double click at an empty spot.
BEGIN_MESSAGE_MAP(CScnBuildDlg, CDialog)
ON_LBN_DBLCLK(IDC_EVENT_LIST, OnDblclkEventList)
END_MESSAGE_MAP()
void CScnBuildDlg::OnDblclkEventList()
{
exit(-1); //Currently, it only exits when double clicking on a specific item, not on an empty space
}
Any ideas on how to fix this?
Thanks.
An additional way of trapping this event is possible by using CWnd::Oncommand. If you add this event handler to your dialog code as follows, you will be able to trap the double-click.
BOOL CScnBuildDlg::OnCommand(
WPARAM wParam,
LPARAM lParam
)
{
if (LOWORD(wParam) == IDC_EVENT_LIST && HIWORD(wParam) == LBN_DBLCLK)
DoSomething ();
return CDialog::OnCommand(wParam, lParam);
}
However, you'll need to be careful because this event will trap the double-click on an existing list box item as well. You'll also need to make sure that you allow the base class a chance to handle the WM_COMMAND message. If not, you may experience some strange bugs.

How to handle double click exactly?

I want to handle action double click by clicking left_mouse 2 times.Between 2 click-time,I sleep 100ms
SendInput(LEFT_CLICK...); Sleep(100); SendInput(LEFT_CLICK...);
It works OK in my PC,but not works correctly in virtual machine
May be,There is a delay-time when machine do function "SendInput"Eventhough I remove "Sleep(100)",It just clicks 2 times and doesn't "double click" as I want.
How to handle double click exactly in this case
Please suggest me anyway to do it
Thanks,
Btw you should specify what environment you're working in and make your code a bit more detailed. Using SendInput is one option, I don't know what you're trying to do exactly, but I'll give you two more options to try for simulating clicks. Something like this would works fine (I code in python but it should be the same idea):
def leftClick(x=0, y=0):
win32api.SetCursorPos((x,y)) #set the cursor to where you wanna click
win32api.mouse_event(win32con.MOUSEEVENTF_LEFTDOWN, x, y, 0, 0) #generate a mouse event
win32api.mouse_event(win32con.MOUSEEVENTF_LEFTUP, x, y, 0, 0)
return True
def doubleClick(x=0, y=0):
leftClick(x,y)
leftClick(x,y)
You could sleep for 50 ms in between time.sleep(0.05) but it works for me without it and I've tested in a vm.
Another option if you want to perform silent clicks without having to move the cursor, you can send a message to the window where you want to click knowing the window handle (hwnd), here I assume you pass the handle as a parameter.
def leftClick(x=0, y=0, hwnd):
lParam = win32api.MAKELONG(x,y) # create a c long type to hold your click coordinates
win32gui.SendMessage(hwnd, win32con.WM_LBUTTONDOWN, win32con.MK_LBUTTON, lparam) # send a message to the window that the mouse left button is down.
win32gui.SendMessage(hwnd, win32con.WM_LBUTTONUP, 0, lparam) # send a message to the window that the mouse left button is up.
return True
def doubleClick(x=0, y=0, hwnd):
leftClick(x,y, hwnd)
leftClick(x,y, hwnd)
or you can send the message WM_LBUTTONDBLCLK up to you.

EN_UPDATE does not send to the subclassed edit box procedure

I have subclassed Edit control window procedure and then found out it no longer
sent the EN_UPDATE.
Am I missing something , and could somebody suggest me a workaround on this one?
LRESULT CALLBACK EditBoxProc_textbox3( HWND hwnd, UINT message , WPARAM wParam, LPARAM lParam )
{
int previous_position = 0 ;
WCHAR previous_text [1024];
WCHAR current_text [1024];
switch (message)
{
case WM_CREATE:
previous_text[0] = L'\0';
current_text[0] = L'\0';
break;
case EN_SETFOCUS:
// :TODO: read the current text of the textbox3 and update textbox2
// text according to it. //
Edit_Enable(hwndTextBox2,FALSE);
break;
case EN_UPDATE:
MessageBox(NULL,L"EN_UPDATE", lpszAppName,MB_OK);
GetWindowText( hwndTextBox3, current_text ,1024);
if( is_valid_textbox3(current_text))
{
wcscpy(previous_text,current_text);
previous_position = LOWORD(Edit_GetSel(hwndTextBox3));
update_textbox2(NULL);
}else
{
SetWindowText(hwndTextBox3, previous_text );
Edit_SetSel(hwndTextBox3,previous_position, previous_position);
}
break;
case EN_KILLFOCUS:
Edit_Enable(hwndTextBox2,TRUE);
break;
default:
break;
}
return CallWindowProc(edit_old_wndproc_textbox3,hwnd,message,\
wParam,lParam);
}
and then found out it no longer sent the EN_UPDATE
It never sent EN_UPDATE in the first place. It is a notification message that's actually sent as a WM_COMMAND message. And it is sent to the parent of the Edit control, not the control itself. Same goes for EN_SET/KILLFOCUS.
The design philosophy here is that an Edit control can simply be put on, say, a dialog. And the custom code that makes the dialog behave in a certain way is written in the parent window's procedure with no requirement to subclass the control. Which is fine but it makes it difficult to create a customized edit control that can have its own behavior. Or in other words, it makes it hard to componentize an edit control. The EN_SET/KILLFOCUS notifications are not a problem, you can simple detect their corresponding WM_SET/KILLFOCUS messages. But you'll hit the wall on EN_UPDATE, the control doesn't send any message like that to itself. Only the parent window can detect it.
Componentizing an edit control is pretty desirable and actively pursued by object-oriented class libraries like Winforms and Qt. They have a class wrapper for the control (TextBox, QLineEdit) that has a virtual method that can be overridden (OnTextChanged, changeEvent) so that the control can be customized into a derived class with its own behavior. And generate an event (aka signal) that anybody can subscribe to, not just the parent (TextChanged, textChanged). To make that work, the parent window needs to participate. When it gets the WM_COMMAND message, it must echo the notification back to the child control. Either by sending a special message back or by calling a virtual method on the child class.
You can of course implement this yourself as well, albeit that you are liable of reinventing such a class library. Consider using an existing one instead.

Resources