I am implementing an AutoComplete, but one that will match text anywhere in the list of enumerated strings—rather than just the beginning of a suggestion as is the behavior of the built-in SHAutoComplete control
For example:
The "popup" window is a TForm that must:
set
CS_DROPSHADOW
style during CreateParams (subject to checkingSPI_GETDROPSHADOW
of course; I'm not a heathen who doesn't pay his taxes)Params.WindowClass.Style := Params.WindowClass.Style or CS_DROPSHADOW;
and of course set its
WndParent
to the form that holds the TEdit (so that the drop-down appears on top of the form it's popping up over)Params.WndParent := FWndParent;
I have the Up, Down, Return, Escape keyboard navigation working fine. The typist can quickly select the item they want, and the list of suggestions closes.
The form contains a TListView for the actual items (much like CAutoComplete
SHAutoComplete class does in Windows).
Clicking takes focus
If the user clicks an item in the drop-down, then using Inspect (the accessiblity tool in the SDK) we can see that focus was given to one of the listitems in the (now hidden) drop-down menu:
You can even see the title-bar indicates that the entire window on longer has focus.
How can I prevent my window from obtaining input focus?
Attempts
Return MA_NOACTIVATE
I tried responding to the WM_MOUSEACTIVATE
window message and return MA_NOACTIVATE
:
Return code/value | Description |
---|---|
MA_ACTIVATE (1) | Activates the window, and does not discard the mouse message. |
MA_ACTIVATEANDEAT (2) | Activates the window, and discards the mouse message. |
MA_NOACTIVATE (3) | Does not activate the window, and does not discard the mouse message. |
MA_NOACTIVATEANDEAT (4) | Does not activate the window, but discards the mouse message. |
Such as:
procedure TfrmAutoComplete.WMMouseActivate(var Msg: TMessage);
begin
inherited;
Msg.Result := MA_NOACTIVATE;
end;
But it still gets focus.
SetFocus during WM_ACTIVATE
I tried messing with things that ought not be messed with, and calling SetFocus during WM_ACTIVATE
:
procedure TfrmAutoComplete.WMActivate(var Msg: TMessage);
begin
inherited;
Msg.Result := MA_NOACTIVATE;
Windows.SetFocus(FEdit.Handle);
end;
Raymond Chens talks about why this is a bad idea; although i must confess i don't understand:
The dangers of messing with activation when handling a WM_ACTIVATE message
Raymond Chen, August 9th, 2005
This is basically the same thing as The dangers of playing focus games when handling a WM_KILLFOCUS message, just with activation in place of focus. One developer discovered the hard way that if you mess with activation inside your WM_ACTIVATE handler, things get weird. The author noted that if he posted a message and did the work from the posted message, then everything came out okay.
A follow-up to the original message noted that passing the SWP_NOACTIVATE flag to the SetWindowPos function solves the problem. Do you understand why?
And, of course, when the TEdit loses focus, it hides the suggestion drop-down. So there's probably a focus lock-out race going on.
Short Version
Is there a better way?
WS_EX_NOACTIVATE
.SetWindowLong(hwnd, GWL_EXSTYLE, WS_EX_NOACTIVATE);