How to track focus changes between several TrackBars using WTL? - winapi

I'm trying to track focus changes between several TrackBars (aka sliders, all within the same window) using WTL.
So far I've tried a
MESSAGE_HANDLER(WM_SETFOCUS, func)
as well as one
COMMAND_HANDLER(IDC_SLIDERn, WM_SETFOCUS, func)
for each slider without success.
The about trackbar controls page on msdn says: "WM_SETFOCUS Repaints the trackbar window." ..
edit:
I now have derived the sliders from my own class where I handle WM_SETFOCUS with MESSAGE_HANDLER and notify the parent window by posting the message to it with m_hWnd as lParam so I can check in the parent which slider gained focus.
This works, but is there a more elegant way to do this?

WM_SETFOCUS is sent to the specific window that gets focus, not to the parent, as you've discovered.
However, there's an alternate tecnique that you can use to avoid subclassing; most controls (specifically the 'common controls', which includes sliders) will send a WM_NOTIFY to their parent when certain events happen, allowing the parent to handle these events for a collection of children.
In your case, try listening for WM_NOTIFY message at the parent window, specifically checking for the case where the notification ID is NM_SETFOCUS - from MSDN:
Notifies a control's parent window that the control has received the input focus. This notification code is sent in the form of a WM_NOTIFY message.
...which sounds like what you are looking for. Apparently ATL supports these in the message map using NOTIFY_HANDLER, something like:
NOTIFY_HANDLER(IDC_SLIDERn, NM_SETFOCUS, func)
Note that this works because the Win32 Common Controls support this sort of notification forwarding; if you were to use some other custom control instead, you may not get these notifications and would have to resort to subclassing. But for the common controls, this is the simplest way of doing it.

You don't need to derive your class, subclassing with CContainedWindowT is just fine.
BEGIN_MSG_MAP_EX(CDialog)
// ...
ALT_MSG_MAP(IDC_TRACKBAR)
MSG_WM_SETFOCUS(OnControlSetFocus)
MSG_WM_KILLFOCUS(OnControlKillFocus)
END_MSG_MAP()
// ...
CContainedWindowT<CTrackBarCtrl> m_TrackBar;
// ...
CDialog() :
m_TitleListView(this, IDC_TRACKBAR)
// ...
LRESULT OnInitDialog(HWND, LPARAM)
{
// ...
ATLVERIFY(m_TrackBar.SubclassWindow(GetDlgItem(IDC_TRACKBAR)));
// ...
// ...
LRESULT OnControlSetFocus(...) { }
LRESULT OnControlKillFocus(...) { }

Related

Child windows does not receive WM_DESTROY?

I packaged the winapi CreateWindowEx into a simple class. Since every window sharing a same wndProc(hwnd,msg,wparam,lparam), I put every window created by CreateWindowEx into a map to distribute msg, like this:
wndProc(hwnd, msg, wparam, lparam){
if(map[hwnd]!=nil){
switch(msg){
map[hwnd].wndProc(...)
}
}
}
And each time a window or its parent window being destroyed, remove it from the map:
case WM_DESTROY: delete(map, hwnd)
But things like buttons do not receive WM_DESTROY. I printed all msg in WM_NOTIFY and WM_COMMAND but i got noting.
So how can I remove those child windows form the map at the right time? Or a way distribute the msg without creating a hwnd map?
They certainly do get that message. But their window procedure is inside Windows, not inside your program. So you never see it. Something you can read in the MSDN documentation, note how WM_DESTROY doesn't get any special treatment. Nor generate a notification that your parent window can see.
Short from automatically removing all the child windows when you see the parent window destroyed, subclassing controls is a pretty standard requirement for C++ class library wrappers. Best to not invent your own btw, this has been done many times already.
So how can I remove those child windows form the map at the right time?
You have to subclass every window you create, either with SetWindowLongPtr(GWL_WNDPROC) or SetWindowSubClass(), then you will receive all of the WM_DESTROY messages.
Or a way distribute the msg without creating a hwnd map?
Frameworks like ATL and VCL handle that by dynamically allocating a thunk for each window and putting the object pointer right in the thunk, then use the thunk as the window procedure. That way, whenever the thunk is called, it passes the message directly to its associated object.

Owner-drawn CTabCtrl in WTL

WTL/WIN32 newbie here, struggling to understand how messages are passed around.
I'm trying to write an owner-drawn CTabCtrl in WTL. For some (at least to me) incomprehensible reason, WM_DRAWITEM is sent to the parent window, not to the window that actually needs to know. Which makes it difficult to make a nice, self-contained GUI class to simply replace CTabCtrl. I could always capture the message in the parent and pass it on to the tab control, but that would be poor OO design. Is there a way to intercept the message, without requiring extra re-routing code in the owner/parent class?
EDIT: After a bit of googling, I now have
class CQueryTabCtrl :
public CWindowImpl<CQueryTabCtrl, CTabCtrl>,
public COwnerDraw<CQueryTabCtrl>
{
public:
DECLARE_WND_SUPERCLASS(NULL, CTabCtrl::GetWndClassName())
BEGIN_MSG_MAP(CQueryTabCtrl)
CHAIN_MSG_MAP(COwnerDraw<CQueryTabCtrl>)
DEFAULT_REFLECTION_HANDLER()
END_MSG_MAP()
BOOL PreTranslateMessage(MSG* pMsg)
{
pMsg;
return FALSE;
}
void DeleteItem(LPDELETEITEMSTRUCT /*lpDeleteItemStruct*/)
{
}
void DrawItem ( LPDRAWITEMSTRUCT lpdis )
{
CDCHandle dc = lpdis->hDC;
CDC dcMem;
dcMem.CreateCompatibleDC ( dc );
dc.SaveDC();
dcMem.SaveDC();
dc.FillSolidRect ( &lpdis->rcItem, RGB(255,0,0) );
dcMem.RestoreDC(-1);
dc.RestoreDC(-1);
}
};
Which is obviously utterly wrong, since DrawItem() is never called.
WM_DRAWITEM is sent to parent by design.
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.
You handle it on hosting window, and with WTL you might leverage COwnerDraw class on it and/or reflect messages there so that they are sent back to the window where your subclassing WindowProc would handle them as you perhaps originally planned.
This answer is a bit late to the party, but might help others...
There's no way to directly achieve reflected messages w/o extra routing code, because that's how Window's window messaging works, as Roman points out.
ATL however has mechanisms to reflect messages onto child windows, which at least helps in keeping self-written code to a minimum.
1) Opt-In to reflection
This requires an extra step in your parent window, in order to tell it to reflect messages from child windows onto them, using the REFLECT_NOTIFICATIONS() macro:
// Just a made-up dialog class for outlining message reflection installed on the parent window
class SomeDialog : public CDialogImpl<SomeDialog, CWindow>
{
public:
BEGIN_MSG_MAP_EX(SomeDialog)
REFLECT_NOTIFICATIONS()
END_MSG_MAP()
};
2) Handle reflected (owner-draw) messages
Because your control will receive reflected messages, and the COwnerDraw mixin provides an alternative message map for those, you simply chain to that message map, using the CHAIN_MSG_MAP_ALT() macro.
class CQueryTabCtrl :
public CWindowImpl<CQueryTabCtrl, CTabCtrl>,
public COwnerDraw<CQueryTabCtrl>
{
public:
DECLARE_WND_SUPERCLASS(NULL, CTabCtrl::GetWndClassName())
BEGIN_MSG_MAP(CQueryTabCtrl)
CHAIN_MSG_MAP_ALT(COwnerDraw<CQueryTabCtrl>, 1)
END_MSG_MAP()
void DrawItem(LPDRAWITEMSTRUCT)
{
// ...
}
};
Also see a complete owner-drawn tablist control from the wtlext repository, which can serve as an example (Dislaimer: I am associated with the author FireDaemon Technologies Ltd).

Creating a separate window against each click on push button

I am working on Windows GUI Application.There are 20 Push Buttons on my Window. Against each button click, I want to open up a new window for displaying some information. It can be anything which can display some information but since I am new to all this and I didn’t want to try anything complicated, I decided to go with the idea of creating a pop up window against each button click.
Now the problem that I am facing is that since there are 20 different windows, do I need to create 20 different window callback functions? Though there is only one control in the pop up window i.e. a Close sign, but even for that I need to have a CALLBACK function.
I had been trying with this but now this idea looks senseless. Is there any other option in which I can achieve the desired functionality?
Waiting for help.
If all of the windows should behave the same way, then you can create a single window procedure (what you're calling the CALLBACK function) that is shared by all of the pop-up windows.
Window procedures do not have to be unique to individual windows, but if multiple windows share the same window procedure, they will react identically to all messages that they receive.
For example:
// Message procedure for all pop-up windows.
LRESULT CALLBACK MyPopupWndProc(HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam)
{
switch (msg)
{
// handle any messages you want here in order to
// customize the behavior of your pop-up window
}
return DefWindowProc(hwnd, msg, wParam, lParam);
}
You specify a pointer to the MyPopupWndProc function when you register the window class for your pop-up windows (using the RegisterClassEx function), and then you pass the name of the registered window class when you call CreateWindowEx to create/display a pop-up window whenever one of the buttons on your main window is clicked.
Of course, if you're just wanting something simple for testing purposes, remember that you can always call the MessageBox function! No window procedures or class registration required.

Modeless dialog keyboard handling (winapi)

I've got an application with a main window which has a bunch of controls, including the spacebar, which is handled by a simple method called onSpacebar(). On top of that main window, I've got a persistent modeless dialog.
I need the spacebar to behave the exact same way, regardless of whether the dialog has focus, or the main window has focus.
This dialog is backed by a DialogProc which looks something like this:
BOOL CALLBACK DialogProc(HWND hDlg, UINT uMsg, WPARAM wParam, LPARAM lParam)
{
switch(uMsg)
{
case WM_NOTIFY:
std::cout<< "WM_NOTIFY" <<std::endl;
switch(LOWORD(wParam))
{
// which component caused the message?
case COMP_TREE:
if(((LPNMHDR)lParam)->code == NM_DBLCLK){
onDoubleclk()
}
//...
break;
// other components...
}
break;
case WM_CLOSE:
// the dialog can only be closed when the whole app is closed
//EndDialog(hDlg, IDCANCEL);
return TRUE;
case WM_DESTROY:
PostQuitMessage(0);
return TRUE;
}
return FALSE;
}
From what I gather, I should call my onSpacebar() method from within the DialogProc, similarily how I handle the double click. I can see that WM_NOTIFY is received by the dialog when the spacebar is pressed (the phrase WM_NOTIFY is printed to cout), but I can't seem to differentiate the spacebar notification from the other numerous notifications the dialog receives.
Please, tell me how to recognize that the particular WM_NOTIFY was in response to a spacebar keypress.
A WM_NOTIFY message is not the standard way that a window processes key press events. When a key is pressed, your window should be receiving WM_KEYDOWN, WM_KEYUP, and possibly WM_CHAR messages. WM_NOTIFY serves an entirely different purpose altogether: passing on a message from a common control to its parent window.
So the fact that you're receiving a WM_NOTIFY message in response to a key press is a fairly unusual thing, explainable when you understand how focus works (which is key to solving your ultimate question).
In Windows, only one window can be focused at a time, and the currently focused window is the one that receives all keyboard input. Thus, if a dialog box has the focus, it will receive key press notifications. If a child control on that dialog box has the focus, it (not its parent dialog) will receive key press notifications. And there is a focusable child control on a dialog box, it will always receive the focus in preference to its parent dialog, therefore it will also always receive key press notifications.
So the likely explanation for your curious WM_NOTIFY messages is that one of the common controls on your dialog has the focus, it is receiving the space key press event, and after processing it, passing on a notification to its parent window (your dialog) in the form of a WM_NOTIFY message. As you might imagine, this is not a reliable method of detecting that the space bar has been pressed.
Instead, you need to figure out some way of trapping key press notifications before they get sent to the focused control. To do that, you'll need to modify your application's message loop to trap WM_KEYDOWN or WM_KEYUP messages before calling either DispatchMessage or IsDialogMessage.
If the key event corresponds to the space bar, you will call your onSpacebar function and indicate that the message was handled, preventing it from being passed on and processed by another window.
If the key event does not correspond to the space bar, then you will need to handle the message as you usually would, ensuring that it does get passed on and processed by the other window.
Since this approach filters out space key presses at a global level, it solves both the problems of child controls on a dialog stealing the key press and the other modeless dialog. However, you do need to be careful because it's very easy to screw things up so that the user can't navigate your dialog using the keyboard at all.
More fundamentally, I think your idea to handle presses of the space bar is fundamentally flawed. The logic of certain common controls basically requires that they process presses of the space bar. For example, consider a textbox: if you filter out all presses of the space bar at a global level, the user will never be able to type a space in a textbox. If you insist on handling the space bar, you will need to check the focused control in your global handler, and if it's a textbox (or other common control that you wish to receive spaces), pass it on; otherwise, handle it yourself.
Honestly, what I'd do instead is choose a more unique key combination (like, I don't know, Ctrl+Space) and set that up as an accelerator. Presumably, your global message loop is already processing accelerator keys by calling the TranslateAccelerator function, so that would take care of all the dirty work for you. No code is even required—you'd do everything simply by editing the accelerators resource file in your project. The MSDN documentation on keyboard accelerators is here, but you'll probably have an easier time consulting your favorite book on Visual C++.

how to route a message to the control's standard WNDPROC

When a standard window control (e.g. an "EDIT" control) is created, its WNDPROC is defined as part of the window class (i.e. "EDIT" has a specific WNDPROC that is designed to make the window display & behave as an edit-control).
MFC allows you to interact with such controls via their wrapper classes, such as a CEdit wrappers the specialized messages for an "EDIT" window control.
MFC further allows you to bind an instance of an "EDIT" window to a C++ subclass of CEdit, say a CMyEdit, where you can override inherited virtual functions of CEdit and CWnd, and you can define a message table to gain access / override messages sent to the window instance itself.
There is CWnd::Default(), which calls this->DefWndProc with the current message arguments. This appears to lookup the WNDPROC for the HWND it is associated with. So is this the correct answer: call DefWndProc() (or equally, Default()), which will hand it off to the WNDPROC of the windows control?
Obviously, this is different than other message table handlers which can return FALSE to indicate that they didn't handle the message, and MFC will automatically route the message up the class inheritance hierarchy to the next message handler for this message, or, I assume, to Default() to be handled by the native WNDPROC?
If I define an arbitrary message handler, say for WM_SETTEXT, what is the correct way to pass this message on to the "EDIT" WNDPROC?
I'd also love to know if there is a way to pass the message up to a superclass (C++ class hierarchy) for handling? Many OnXXX style handlers do have a way to do so, but is there a mechanism that works for ON_MESSAGE handlers?
class CDynamicMenuControlEdit : public CEdit
{
...
LRESULT OnSetText(WPARAM wParam, LPARAM lParam);
...
}
BEGIN_MESSAGE_MAP(CDynamicMenuControlEdit, CEdit)
...
ON_MESSAGE(WM_SETTEXT, OnSetText)
...
END_MESSAGE_MAP()
LRESULT CDynamicMenuControlEdit::OnSetText(
WPARAM wParam, // not used; must be zero
LPARAM lParam // window-text string (LPCTSTR)
)
{
if (m_bHasFocus)
{
// do normal thing
// !!! THIS IS MY QUESTION: IS THIS CALLING EDIT's WNDPROC, or ::DefWinProc()? !!!
return DefWindowProc(WM_SETTEXT, wParam, lParam);
}
...
}
Clarification
You can have multiple MFC subclasses at the C++ level -
so C inherits B inherits A, where A is an MFC class (e.g. CEdit).
Each of those can have an MFC message table - i.e. BEGIN_MESSAGE_MAP ... END_MESSAGE_MAP which can each have a handler for any arbitrary windows message, such as WM_MESSAGE(WM_SETTEXT, OnSetText) - and that OnSetText member is not necessarily virtual - just a static member (each MFC subclass can route that message in any arbitrary way).
My question is - since a WM_MESSAGE dispatch entry doesn't have a return value, how do I allow MFC to walk up the MFC dispatch tables from C to B to A before handing back to the real windows 'EDIT' class's wndproc?
Or are all such entries intended at the MFC design-level NOT to be walked? i.e. the most subclassed layer's dispatcher is intended to be the only one called? And if it wants to leverage an inherited member it has to manually make that call - MFC simply doesn't have any specific generic structure for this?
Calling Default() will cause the "normal" processing that would have happened in response to a message to occur. I'm not entirely clear on what you are trying to achieve, but it seems to me that calling Default() is what you want to do.
If you look at a lot of the handlers from Windows messages in the CWnd handlers (and handlers from classes derived from CWnd such as CEdit) you will see that they call Default().
Word to the wise, that Default() will actually use whatever parameters the original message had - you cannot change them.
You're doing it right. CWnd::DefWindowProc() uses the WINDOWPROC you subclassed from and will call the EDIT window's window procedure.

Resources