npinti
npinti

Reputation: 52185

C++ Window losing focus when child button is pressed

I am trying to get a GUI running for a C++ application but I am having an issue with key press events. Basically, everything works fine, as long as I do not click on any buttons (the main window registers key events), but as soon as I click on a button, the main window loses focus and it no longer captures key events. This might be a stupid question, but I am very new to C++. This is some of the code I am using:

Creation of the main window:

hwnd = CreateWindowEx (
               0,                   /* Extended possibilites for variation */
               szClassName,         /* Classname */
               "Application Name",  /* Title Text */
               WS_OVERLAPPEDWINDOW, /* default window */
               CW_USEDEFAULT,       /* Windows decides the position */
               CW_USEDEFAULT,       /* where the window ends up on the screen */
               540,                 /* The programs width */
               250,                 /* and height in pixels */
               HWND_DESKTOP,        /* The window is a child-window to desktop */
               NULL,                /* No menu */
               hThisInstance,       /* Program Instance handler */
               NULL                 /* No Window Creation data */
           );

Creation of one of the buttons:

CreateWindow(TEXT("button"), TEXT("Start"),
                 WS_VISIBLE | WS_CHILD,
                 x, y, width, height,
                 hwnd, (HMENU) 6, NULL, NULL);

I have also noticed that whenever I click on a button, the WM_KILLFOCUS event is fired, which is why I think that this is a focus issue. I have also tried capturing the WM_KILLFOCUS event and then set the focus again with SetActiveWindow but that crashed my program.

Any help would be appreciated :)

Upvotes: 1

Views: 4470

Answers (3)

user13947194
user13947194

Reputation: 402

The first answer was partially accurate. Subclassing the button can get "rid" of the "problem"; however handling WM_SETFOCUS be it in parent window, or subclass procedure or BN_SETFOCUS will result in unresponsive UI if you take the focsus from the button.

What you should override in the subclass procedure is WM_LBUTTONUP. Since by the time you release the mouse button you have already clicked the windows button.

Note I think this is utter rubbish for a button to be stealing focus. There should be a style like BS_NO_STEAL_FOCUS, that prevents this. As it is very cumbersome when you want another window to be handling key presses or scrolling.

/** Procedure for subclass. 
      This procedure is called first for the widget/control.
      Unhandled or partially handled message can goes to
      original procedure by calling DefSubclassProc(...).
      buttonProcEx takes all four params of normal procedure, plus a
       param for user defined object. 
      Note this is what win32 should have done in the first place.
 */
LRESULT CALLBACK buttonProcEx(HWND hwnd,uint msg,WPARAM,LPARAM,DWORD_PTR)
{
    if(msg == WM_LBUTTONUP) 
    {
     setFocus(GetParent(hwnd));
     return 0; //do not allow default behaviour
    }
    else return DefSubclassProc(hwnd,msg,wparam,lparam);
}


//Call this after creating your button

SetWindowSubclass((HWND)button,buttonProcEx,0,NULL);

or

struct Content {...}content; //lifetime should be long enough
SetWindowSubclass((HWND)button,buttonProcEx,0,(DWORD_PTR)&content);

Upvotes: 0

npinti
npinti

Reputation: 52185

It turned out that I was using the wrong function (SetWindowActive). Assaf Levy's answer seemed to complex for me and I thought that there might be another way around this. I managed to find the SetFocus function which gives the focus to any given window by providing it it's handle.

To make it work, what I needed to do was to, once that the the necessary code was executed within the WM_COMMAND block, I called the SetFocus function with the handle of the main window. This gave focus back to the main window and allowed it to receive events.

Note, putting the SetFocus in the WM_KILLFOCUS block will cause the buttons and any other component in it to become unresponsive to events.

Upvotes: 4

Assaf Levy
Assaf Levy

Reputation: 1302

This is by design. The main window is a window, but so the button is a window, and only one can have focus at any given time. If you don't want the button to "steal" the focus, add an OnFocus handler (or intercept WM_SETFOCUS) and immediately return focus to the previous window (I believe it's in the WPARAM of WM_SETFOCUS).

An easy hack would be:

  1. hMyButton = CreateWindow("button", ...).
  2. Define a MyButtonProc(HWND, UINT, WPARAM, LPARAM) function.
  3. Call SetWindowLong(hMyButton, GWL_WNDPROC, (LONG)MyButtonProc). Save the value returned by this function in a g_OldButtonProc.
  4. Inside MyButtonProc() catch WM_SETFOCUS, and call SetFocus(hMyMainWindow). Always return CallWindowProc(h_OldButtonProc, hwnd, msg, ...) at the end of your MyButtonProc() function, unless the message was WM_SETFOCUS.

That will do the trick (tested).

Upvotes: 2

Related Questions