Link to home
Start Free TrialLog in
Avatar of kavan
kavan

asked on

Possible to prevent CWnd objects from receiving focus?

is there a window style to prevent CWnd objects from receiving the input focus? i need to keep the active view focused at all times and focus is transferred every time i click on a button in one of my dialogbars. i don't like catching WM_SETFOCUS and re-focusing back to the active view, this seems ugly. i can see the flicker in the caption bar text when it does this.
Avatar of Tommy Hui
Tommy Hui

Most of the standard controls will accept focus. The only control that does not grab focus is the static control. It does this by ignoring the WM_SETFOCUS message and when it does receive focus through some other means, it sets the focus to the next child. The other controls will rely on being in focus to do their work. If you don't want this behavior and you don't want to override WM_SETFOCUS, there are two things to do:

1. Catch WM_KILLFOCUS in the view and say no, I want the focus back:

  CMyView::OnKillFocus()
  {
    SetFocus();
  }

2. Don't use the regular controls. Instead, create your own controls that don't grab focus.


One way is to apple the WS_DISABLED style or call CWnd::EnableWindow() to disable the window.
Assuming that you want the user to be able to click on menus and toolbar items there is no way without managing the focus state.  Window moves the focus to a control before sending it messages.  So if the user clicks on a toolbar the 1st thing that happens is focus is moved.
Avatar of kavan

ASKER

thui - i cannot re-focus in the view by SetFocus() when it gets a WM_KILLFOCUS. the reason is because i have a CDialogBar which is floating outside my main MDI window, which contains a grid of CBitmapButtons, a CButton, and a CSliderCtrl. the main problem i am having is that once the user clicks on one of those controls in the CDialogBar, whichever one was clicked steals the focus from the view. if i reset focus immediately upon receiving WM_KILLFOCUS in my view, then the clicked control loses the mouse input and it's as though it wasn't clicked at all.

here is the reason i need to maintain focus in my view. the CDialogBar is in essence a tool palette, and the CBitmapButtons represent my "tools." clicking on a tool causes the cursor to change to the appropriate bitmap which represents the tool which is now currently active. however, the cursor only changes when passing over the view, thus i use CMyView::OnSetCursor() to handle a WM_SETCURSOR message. this works fine and good. now, if the user holds down a certain key or keys while a tool is selected, this should effectively transform the tool temporarily into some other tool until the key or keys are released.

now consider the following situation...the user clicks on a tool CBitmapButton in the CDialogBar, selecting a tool, then proceeds to move the cursor back into the view. as the mouse enters the view, the cursor will change to the appropriate tool's bitmap because i maintain the current tool as a global and now my view is receiving a WM_SETCURSOR message so it looks at this global, then sets the appropriate cursor. now the user stops moving the mouse, but it remains inside the view. the mouse is stationary. the user happens to decide to press one of the "modifier" keys to change the tool into something more useful...BREAK. focus remains back at the CBitmapButton in the CDialogBar so the view does not receive the WM_KEYDOWN where i implemented the handler for this which changes the cursor and tool appropriately. thus i need focus to remain in the view at all times, without ever going over to those controls in the CDialogBar. do you see what i'm saying?

i am now in the process of scrapping the CDialogBar and CMiniDockFrameWnd because i am drawing my own custom caption bar. right now there are no CBitmapButtons or CButtons, but focus still goes to my popup window when i click on one of the tool bitmaps in my tool grid. so i am still having the same problem.

i guess i need to know if it is possible to catch a WM_LBUTTONDOWN before Windows has a chance to set the focus to that particular CWnd object. where exactly does Windows determine that it needs to change the focus after a WM_LBUTTONDOWN? i have been assuming that this is part of the DefWindowProc(), but i thought with MFC i was always able to handle things before they ever got to DefWindowProc()? can't i hook in somewhere above Windows and supersede it? i know that static controls do not accept the focus...how do they accomplish this? (it's not by ignoring WM_SETFOCUS, because this message signifies that focus has ALREADY been set to the CWnd object).

thanks in advance for any more help...-phil
Kavan,
maybe you are trying to find the solution on the wrong (or more difficult) way. I would not rely on the focus in the view, nor I would handle the key messages from the CView class. Try moving your OnXXXX methods to the CDocument class; when you press a key (if this is an accelerator), the command will be sent to the application main window, and then down to the message handling chain up to your CDocument. You will not care where the focus is.....

 
One possible answer is top replace the buttons with toolbar.
However, if you want/need the real buttons, the question is tough. Contrary to the popular belief, the focus is not set in plain window object's event handler. Each  control sets or not sets focus to self according to what it wants. In most cases it happens on WM_MOUSEACTIVATE event. Try overriding that event and disable the default processing. If that does not help, then you have to override ON_LBUTTONDOWN, thus losing the entire button mouse handling functionality.
I looked for a style to prevent the control from accepting focus - found none.

Arkadiy.
ASKER CERTIFIED SOLUTION
Avatar of IgorGrebnev
IgorGrebnev

Link to home
membership
This solution is only available to members.
To access this solution, you must be a member of Experts Exchange.
Start Free Trial
Dear kavan,
I am sorry, I just send not completed answer by mistake. The comment following the answer is full one.

thanks to all who helped out with this!

what i ended up doing to keep things simple was to write a base class which outlined a "palette-style" window, essentially a CWnd derivative having the WS_POPUP style whose parent was my main MDI frame window. inside this class i handle all important nonclient-area messages myself. in this way i catch WM_NCLBUTTONDOWN as it comes in, do my own window dragging, effectively preventing this from activating the window by not calling the corresponding CWnd handler. i sync activation with the main frame window as is done in CMiniFrameWnd, calc my own client area, and paint my own caption and frame/border area in OnNcPaint(). OnNcHitTest() always results in HT_CAPTION, so a click in the nonclient area will result in dragging the window. no system menu, no more focus problems. now all that's left is the client area of the window.

for this i extended this base palette class into another class to handle client-area messages. first, i handle WM_MOUSEACTIVATE, where i return MA_NOACTIVATE to indicate that i don't want the window to be activated on a mouse click, preventing focus that way. all buttons are bitmaps, and may be in the up/down/disabled state. we created bitmaps named with the "u", "d", and "x" extensions much as you would for a CBitmapButton, but i use a simple structure for each button's storage instead of a CBitmapButton so i don't carry vtable overhead for each button. this structure contains two BOOLs, pushed and disabled, which help me determine which state the button is in, telling me which of its bitmaps to draw. of course CBitmapButton would do this for me but by handling clicks in a button manually i get to catch WM_LBUTTONDOWN in my window class and not allow the button to be focused. WM_PAINT is caught to paint all the buttons and custom controls we are using inside this palette. in effect, every control is not a child window but a part of the window itself.

the only problem i ended up having was that we were using a CSliderCtrl on the palette, but after MUCH digging i discovered that the default window proc for this common control immediately sets the focus on WM_LBUTTONDOWN. simply extending the CSliderCtrl class and overriding OnLButtonDown() wouldn't work, because i had no way to see what the default window proc's handler did after setting the focus (can't make much of the asm), although i do know it sets a WM_TIMER and other things in there, affecting other handlers. so i ended up writing my own slider control class.

other than that, we now have a palette window that we can adjust the frame/border size, caption size, client-area size, and number of columns in. it's much more dynamic for us then the MFC floating toolbar seemed to be. (we have a lot of sizing issues).

being fairly new to MFC (or for that matter Win32, i've only been out of school for 6 months and i did mostly MacOS/UNIX before this) i'd have to say this was quite the learning experience.

thanks again...