I would like to have a KeyPreview functionality within Frames, I mean, that when the input (say, one of the controls of the frame is selected, or the mouse is inside) is in a frame (which would have several panels and other controls) then the keys pressed by the user are first processed by the frame.
Is there a way to do this? I haven't found a property similar to KeyPreview in TFrame.
I'm using version XE5 of RAD Studio, altough I mostly work with C++Builder.
Thanks to my recent "When does a ShortCut fire"-investigation, I have worked out a stand alone solution for your Frame.
In short: all key messages enter in TWinControl.CNKeyDwon
of the active control. That method calls TWinControl.IsMenuKey
which traverses all parents while determining whether the message is a ShortCut. Is does so by calling its GetPopupMenu.IsShortCut
method. I have overridden the Frame's GetPopupMenu
method by creating one if it is not present. Note that at all time you still can add a PopupMenu to the Frame yourself. By subclassing TPopupMenu
and overriding the IsShortCut
method, the Frame's KeyDown
method is called, which serves as the KeyPreview functionality you require. (I could also have assigned the OnKeyDdown event handler).
unit Unit2;
interface
uses
Winapi.Messages, System.Classes, Vcl.Controls, Vcl.Forms, Vcl.Menus,
Vcl.StdCtrls;
type
TPopupMenu = class(Vcl.Menus.TPopupMenu)
public
function IsShortCut(var Message: TWMKey): Boolean; override;
end;
TFrame2 = class(TFrame)
Label1: TLabel;
Edit1: TEdit;
private
FPreviewPopup: TPopupMenu;
protected
function GetPopupMenu: Vcl.Menus.TPopupMenu; override;
procedure KeyDown(var Key: Word; Shift: TShiftState); override;
end;
implementation
{$R *.dfm}
{ TPopupMenu }
function TPopupMenu.IsShortCut(var Message: TWMKey): Boolean;
var
ShiftState: TShiftState;
begin
ShiftState := KeyDataToShiftState(Message.KeyData);
TFrame2(Owner).KeyDown(Message.CharCode, ShiftState);
Result := Message.CharCode = 0;
if not Result then
Result := inherited IsShortCut(Message);
end;
{ TFrame2 }
function TFrame2.GetPopupMenu: Vcl.Menus.TPopupMenu;
begin
Result := inherited GetPopUpMenu;
if Result = nil then
begin
if FPreviewPopup = nil then
FPreviewPopup := TPopupMenu.Create(Self);
Result := FPreviewPopup;
end;
end;
procedure TFrame2.KeyDown(var Key: Word; Shift: TShiftState);
begin
if (Key = Ord('X')) and (ssCtrl in Shift) then
begin
Label1.Caption := 'OH NO, DON''T DO THAT!';
Key := 0;
end;
end;
end.