Thanks! I tried this and it didn't work (well, did and didn't). I
discovered that our frames were showing before they fire this IsShortcut
event. The reason appeared to be that, even though they were displayed on
the screen, they didn't have an object the system recognized as having
focus. This was true even when the grid had a highlighted row showing.
> In article <3941b308@dnews>, Tom Alexander wrote:
> > Does anyone understand when and how action lists fire?
> > An application I'm working on (developed by others, so my intimate
knowledge
> > is limited) uses action lists as a methodology of invoking Fx key
actions on
> > various frames. This app has only the main form descending from the
Tform
> > class, everything else is something of the frame variety. Each module
of
> > the app has it's own frame on which one or more other frames and panels
> > live. Each module (frame) has an action list, a pop-up menu list and/or
a
> > number of toolbuttons.
> > The problem arises when I want to do something different for, say.. the
'F6'
> > key, in one module than in another. If I start in one of the inner
modules
> > and press F6, the proper event fires most of the time. If I start in
> > another module and press F6, the proper thing for that frame happens
again.
> > If I go back to the first module I tried and press F6 again, the action
from
> > the other module fires. This behavior occurs usually, but not all the
time.
> > Sometimes after a series of the above errors, the behavior starts acting
as
> > I would expect.
> Tom,
> key handling in Delphi is fairly complex. The sequence of events as they
> relate to your question are:
> - user hits a key, a keydown message is placed into the message queue
> - Application.ProcessMessage gets it from the queue, feeds it through a
> number of Application methods, one of these is IsKeyMsg.
> - IsKeyMsg sends the key down as a CN_KEYDOWN message to the control
> with focus.
> - TwinControl.CNKeyDown calls TwinControl.IsMenuKey
> - after checking the popup menu of the control (if any) for the shortcut
> without success IsMenuKey locates the controls parent form and calls its
> IsShortcut method.
> - Tcustomform.IsShortcut first checks the forms menu, if any. If not
> finding a matching shortcut it then walks over the action lists
registered
> with the form and calls each lists IsShortcut method, which in turn
walks
> over the actions in the list looking for a shortcut.
> - If nobody wanted the key as shortcut yet a CM_APPKEYDOWN message is send
to
> the Application window for the key, TApplication.WndProc handles this by
> calling the main forms IsShortCut (if the window is enabled only, not
when
> a modal form is up). Note that this means that the main forms IsShortcut
> will be called twice if the active control is on the main form and the
> key is not a shortcut!
> The critical part for you is the sequence in which TCustomform.IsShortcut
> walks over the action lists. A TFrame will add its TActionlist to the
parent
> forms actionlists list when the parent is assigned to its Parent property
(see
> TCustomFrame.SetParent) and remove it when the Parent changes or the
> actionlist is destroyed. As far as i see the sequence of the actionlists
does
> not change afterwards. So if you have several enabled frames on your form
> their action lists will be queried in the sequence the frames were added
to
> the form, so the form containing the active control may not be the first
to be
> queried.
> What this boils down to is this: if you have colliding shortcuts that may
act
> differently on different frames on your form then you need to override the
> forms IsShortcut method and take steps there to make sure the frame the
active
> control is on gets first crack at the shortcut. Something like this:
> Function TMyform.IsShortcut( var Message: TWMKey): Boolean; { override }
> Var
> ctrl: TWinControl;
> comp: TComponent;
> i: Integer;
> Begin
> ctrl := ActiveControl;
> If ctrl <> Nil Then Begin
> Repeat
> ctrl := ctrl.Parent
> Until (ctrl = nil) or (ctrl Is TCustomframe);
> If ctrl <> nil Then Begin
> For i:= 0 To ctrl.componentcount-1 Do Begin
> comp:= ctrl.Components[i];
> If comp Is TCustomActionList Then Begin
> result := TCustomActionList(comp).IsShortcut( message );
> If result Then
> Exit;
> End;
> End;
> End;
> End;
> inherited;
> End;
> Untested!
> For performance reason you may want to make sure the actionlist on each
frame
> is the first item created (set it to first in the designers createorder
menu)
> and you may also want to assume only one action list will be on a frame
and
> Exit the for loop once you found it, regardless of Result.
> Peter Below (TeamB) 100113.1...@compuserve.com)
> No e-mail responses, please, unless explicitly requested!