- Mark as New
- Bookmark
- Subscribe
- Mute
- Subscribe to RSS Feed
- Permalink
- Report Inappropriate Content
I am using CVF 6.6 and created a Win32 program. It contains a modal dialog box (created through Resource Editor, and DialogBoxParam) that consists of some 400 Radiobuttons and about 80 "Statics" with text. The whole content is surrounded by a group box. It has a standard vertical scroll bar.
I got the scrolling to work perfectly using mouse buttons and even mouse wheel. However, trapping e.g. VK_HOME and VK_END in order to activate SB_TOP and SB_BOTTOM seems to be not readily possible. So far I managed to use the ENTER-key (because that is one of the few keys a modal dialog box listens to) by detecting wParam=1 and then:
ret = SendMessage (hDlg, WM_VSCROLL, SB_TOP, 0)
This works, but not with the keys that should do it.
There have been cases described where an Edit-control is used for trapping keystrokes, but I don't see how that could help scrolling to Top and Bottom of the dialog box itself.Are there any tricks or work-arounds to achieve the wanted keystroke trapping?
Hope someone can help,
Jan Somer
I got the scrolling to work perfectly using mouse buttons and even mouse wheel. However, trapping e.g. VK_HOME and VK_END in order to activate SB_TOP and SB_BOTTOM seems to be not readily possible. So far I managed to use the ENTER-key (because that is one of the few keys a modal dialog box listens to) by detecting wParam=1 and then:
ret = SendMessage (hDlg, WM_VSCROLL, SB_TOP, 0)
This works, but not with the keys that should do it.
There have been cases described where an Edit-control is used for trapping keystrokes, but I don't see how that could help scrolling to Top and Bottom of the dialog box itself.Are there any tricks or work-arounds to achieve the wanted keystroke trapping?
Hope someone can help,
Jan Somer
Link Copied
7 Replies
- Mark as New
- Bookmark
- Subscribe
- Mute
- Subscribe to RSS Feed
- Permalink
- Report Inappropriate Content
The stuff works like (approximately) like this: when you hit a key, IsDialogMessage in your own or modal dialog's message loop sends WM_GETDLGCODE to DefDlgProc, which in turn sends it to the control with keyboard focus. The control responds with a DLGC_ code, and further processing depends on whether it said that it "wanted" (processed) the key. If it did, further processing is halted, otherwise it's subject to default processing (if any). For example, Enter key will cause WM_COMMAND for default button unless a control with the focus responds with DLGC_WANTsomething.
I don't have time to test it myself, but I suggest that you use Spy++ and watch the messages for the dialog and the control with focus on Home/End/PgUp/PgDown etc. keys and see who gets WM_KEYDOWN and who gets WM_GETDLGCODE. It is possible that one of controls "eats" the keys (an edit box probably will).
By the way, wouldn't (a set of) single-selection list boxes/combo boxes be far more user-friendly than 400 radio buttons on a scrollable surface?
I don't have time to test it myself, but I suggest that you use Spy++ and watch the messages for the dialog and the control with focus on Home/End/PgUp/PgDown etc. keys and see who gets WM_KEYDOWN and who gets WM_GETDLGCODE. It is possible that one of controls "eats" the keys (an edit box probably will).
By the way, wouldn't (a set of) single-selection list boxes/combo boxes be far more user-friendly than 400 radio buttons on a scrollable surface?
- Mark as New
- Bookmark
- Subscribe
- Mute
- Subscribe to RSS Feed
- Permalink
- Report Inappropriate Content
I see. Is it a student test or something similar? Never mind...
I was just trying to explain the mechanisms beyond the scenes. A modal dialog has its own message loop (embedded witin DialogBox(Indirect) function) containing IsDialogMessage. However, I did a quick test with Spy++ and here are my findings (actually, IsDialogMessage is probably a red herring).
* When a control (check box or radio button) has keyboard focus, only its window receives WM_KEYDOWN events, and they don't propagate to the parent dialog. (That's actually the normal windows behavior).
* When no control has focus (e.g. if dialog is previously activated by alt+tab or by clicking on title bar), the dialog does not receive WM_KEYDOWN but WM_SYSKEYDOWN.
Obviously, 1) is bigger issue. Your options are:
1) subclass all controls in the dialog so that they pass up VK_PAGEUP etc. to the dialog.
2) install a keyboard hook for the dialog (SetWindowsHookEx)
3) change the dialog to modeless, but disable parent window (if any) and write your own message loop, filtering WM_KEYDOWN messages (in effect, you get a modal dialog again).
Here's a sketch of solution (2) (maybe simplest, untested)
I was just trying to explain the mechanisms beyond the scenes. A modal dialog has its own message loop (embedded witin DialogBox(Indirect) function) containing IsDialogMessage. However, I did a quick test with Spy++ and here are my findings (actually, IsDialogMessage is probably a red herring).
* When a control (check box or radio button) has keyboard focus, only its window receives WM_KEYDOWN events, and they don't propagate to the parent dialog. (That's actually the normal windows behavior).
* When no control has focus (e.g. if dialog is previously activated by alt+tab or by clicking on title bar), the dialog does not receive WM_KEYDOWN but WM_SYSKEYDOWN.
Obviously, 1) is bigger issue. Your options are:
1) subclass all controls in the dialog so that they pass up VK_PAGEUP etc. to the dialog.
2) install a keyboard hook for the dialog (SetWindowsHookEx)
3) change the dialog to modeless, but disable parent window (if any) and write your own message loop, filtering WM_KEYDOWN messages (in effect, you get a modal dialog again).
Here's a sketch of solution (2) (maybe simplest, untested)
I'm not sure about bit 29 -- check out the docs of WM_KEYDOWN more carefully than I did.
USE MyGlobals, ONLY: hHook
case (WM_INITDIALOG)
...
hwndDialog = hWnd
hHook = SetWindowsHookEx(WH_KEYBOARD, LOC(MyKbHook), &
GetModuleHandle(NULL), GetCurrentThreadID())
case (WM_KEYDOWN, WM_SYSKEYDOWN)
!TODO add your arrow/home/end handling here, affecting the
!scrollbar
case (WM_DESTROY)
iSt = UnhookWindowsHookEx(hHook)
...
INTEGER FUNCTION MyKbHook(iCode, iVkeyCode, iFlags)
!DEC$ATTRIBUTES STDCALL...
USE MyGlobals, ONLY: hHook, hwndDialog
...
hFocus = GetFocus()
IF (IsChild(hwndDialog, hFocus) .OR. hFocus.EQ.hwndDialog) THEN
IF (ANY(iVkeyCode.EQ.(/VK_PRIOR, VK_NEXT, .../)))
IF (BTEST(iCode,29).EQ.0) THEN
iSt = SendMessage(hwndDialog, WM_KEYDOWN, iVkeyCode, iFlags)
ELSE
iSt = SendMessage(hwndDialog, WM_KEYUP, iVkeyCode, iFlags)
END IF
END IF
END IF
iSt = CallNextHookEx(hHook, iCode, iVkeyCode, iFlags)
END FUNCTION MyKbHook
- Mark as New
- Bookmark
- Subscribe
- Mute
- Subscribe to RSS Feed
- Permalink
- Report Inappropriate Content
Quote from MSDN:
Return Values
If nCode is less than zero, the hook procedure must return the value returned by CallNextHookEx.
If nCode is greater than or equal to zero, and the hook procedure did not process the message, it is highly recommended that you call CallNextHookEx and return the value it returns; otherwise, other applications that have installed WH_KEYBOARD hooks will not receive hook notifications and may behave incorrectly as a result. If the hook procedure processed the message, it may return a nonzero value to prevent the system from passing the message to the rest of the hook chain or the target window procedure.
I don't think the presence of hook should affect the previous arrow/tab/enter behavior; maybe it got screwed by the bad return value (well, as you see, message bouncing back and forth is quite mind-boggling, so I can't guarantee it). The bottom line is, you should return zero for all keys you want the default behavior to be applied.
Return Values
If nCode is less than zero, the hook procedure must return the value returned by CallNextHookEx.
If nCode is greater than or equal to zero, and the hook procedure did not process the message, it is highly recommended that you call CallNextHookEx and return the value it returns; otherwise, other applications that have installed WH_KEYBOARD hooks will not receive hook notifications and may behave incorrectly as a result. If the hook procedure processed the message, it may return a nonzero value to prevent the system from passing the message to the rest of the hook chain or the target window procedure.
I don't think the presence of hook should affect the previous arrow/tab/enter behavior; maybe it got screwed by the bad return value (well, as you see, message bouncing back and forth is quite mind-boggling, so I can't guarantee it). The bottom line is, you should return zero for all keys you want the default behavior to be applied.
- Mark as New
- Bookmark
- Subscribe
- Mute
- Subscribe to RSS Feed
- Permalink
- Report Inappropriate Content
Jugoslav may have a better solution, but the function MyKbHook, as he wrote it, doesn't set the return value. I suggest setting it to 0 unless there's a better value it should have. All functions need to define their return value. If the return value is never used, then it would be better as a subroutine.
- Mark as New
- Bookmark
- Subscribe
- Mute
- Subscribe to RSS Feed
- Permalink
- Report Inappropriate Content
It's not magic. The hook function gets called by Windows and it expects some sort of status returned. I haven't researched it, but in many cases a return of 0 means "everything is ok". When you assigned MyKbHook=MyKbHook you were assigning an uninitialized value, which could be anything.
- Mark as New
- Bookmark
- Subscribe
- Mute
- Subscribe to RSS Feed
- Permalink
- Report Inappropriate Content
Like I said, my code above was just a sketch, written off the top of the head. Thus, I entirely forgot the issues about return value. Your approach with returning zero might work (the zero/non-zero return value probably matters for the rest of the system). However, it's best to follow the documentation and always return the return value of CallNextHookEx (which would ''probably'' be zero in most cases, but you shouldn't rely on that). I recall having problems with hooks on different Windows OSes when I did not set up the hook chain correctly--it's better to be safe than sorry.
- Mark as New
- Bookmark
- Subscribe
- Mute
- Subscribe to RSS Feed
- Permalink
- Report Inappropriate Content
Re radio-buttons navigation: you can fix this by:
Adding BS_NOTIFY style to all radio buttons in design time
Handle WM_COMMAND/BN_SETFOCUS for radio buttons, like:
Re radio buttons: Enter doesn't work by default for selecting them -- only Space bar does. When Enter key is pressed, your dialog receives a WM_COMMAND with ID of the default button (IDOK by default), and by handling that you can achieve whatever you want, e.g. SendMessage(GetFocus(), BM_SETCHECK). If you don't want the default button visible, you can even hide it.
IF (HIWORD(wParam).EQ.BN_SETFOCUS) THEN
hwndRadio = GetDlgItem(hWnd, LOWORD(wParam))
b = GetWindowRect(hwndRadio, rectRadio)
b = GetWindowRect(hWnd, rectDlg)
IF (IntersectRect(rectDummy, rectDlg, rectRadio).EQ.0) THEN
IF (rectRadio%Top .LT. rectDlg%Top) THEN
!Scroll downwards e.g. half screen
ELSE
!Scroll upwards
END IF
END IF

Reply
Topic Options
- Subscribe to RSS Feed
- Mark Topic as New
- Mark Topic as Read
- Float this Topic for Current User
- Bookmark
- Subscribe
- Printer Friendly Page