Solved

Possible to prevent CWnd objects from receiving focus?

Posted on 1997-11-22
9
1,801 Views
Last Modified: 2013-11-20
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.
0
Comment
Question by:kavan
9 Comments
 
LVL 15

Expert Comment

by:Tommy Hui
ID: 1310544
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.


0
 
LVL 23

Expert Comment

by:chensu
ID: 1310545
One way is to apple the WS_DISABLED style or call CWnd::EnableWindow() to disable the window.
0
 
LVL 1

Expert Comment

by:kargo
ID: 1310546
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.
0
 

Author Comment

by:kavan
ID: 1310547
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
0
How your wiki can always stay up-to-date

Quip doubles as a “living” wiki and a project management tool that evolves with your organization. As you finish projects in Quip, the work remains, easily accessible to all team members, new and old.
- Increase transparency
- Onboard new hires faster
- Access from mobile/offline

 
LVL 1

Expert Comment

by:winmeister
ID: 1310548
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.....

 
0
 
LVL 1

Expert Comment

by:Arkadiy
ID: 1310549
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.
0
 
LVL 1

Accepted Solution

by:
IgorGrebnev earned 100 total points
ID: 1310550
You can use windows hooks to prevent setting of the focus. Use WH_CBT hook. Your hook funciton will be called before the windows set the focus to the window, if you return non-sero value from the hook, the focus will not be set. The only problem that the hook funciton is called for the all windows in your application, so you need to differ carefully between you buttons and other windows.
Here I give the example of setiing the hook ( the one you need )

LRESULT CALLBACK MyCBTHookProc( int nCode, WPARAM wParam, LPARAM lParam )

{ if ( nCode == HCBT_SETFOCUS )
  {


}


  return 0;
}


//------- set the hook
HHOOK m_cbtHook = SetWindowsHookEx( WH_CBT, MyCBTHookProc, AfxGetApp()->m_hInstance, GetCurrentThreadId() );

0
 
LVL 1

Expert Comment

by:IgorGrebnev
ID: 1310551
Dear kavan,
I am sorry, I just send not completed answer by mistake. The comment following the answer is full one.

0
 
LVL 1

Expert Comment

by:IgorGrebnev
ID: 1310552
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...
0

Featured Post

Top 6 Sources for Identifying Threat Actor TTPs

Understanding your enemy is essential. These six sources will help you identify the most popular threat actor tactics, techniques, and procedures (TTPs).

Join & Write a Comment

Suggested Solutions

Introduction: Load and Save to file, Document-View interaction inside the SDI. Continuing from the second article about sudoku.   Open the project in visual studio. From the class view select CSudokuDoc and double click to open the header …
Exception Handling is in the core of any application that is able to dignify its name. In this article, I'll guide you through the process of writing a DRY (Don't Repeat Yourself) Exception Handling mechanism, using Aspect Oriented Programming.
This video will show you how to get GIT to work in Eclipse.   It will walk you through how to install the EGit plugin in eclipse and how to checkout an existing repository.
It is a freely distributed piece of software for such tasks as photo retouching, image composition and image authoring. It works on many operating systems, in many languages.

760 members asked questions and received personalized solutions in the past 7 days.

Join the community of 500,000 technology professionals and ask your questions.

Join & Ask a Question

Need Help in Real-Time?

Connect with top rated Experts

26 Experts available now in Live!

Get 1:1 Help Now