我想在Frames中有一个KeyPreview功能,我的意思是,当输入(例如,选择了一个框架的控件,或者鼠标在里面)是在框架中(这将有几个面板)和其他控件)然后用户按下的键首先由帧处理。
有办法做到这一点吗?我没有在TFrame中找到类似于KeyPreview的属性。
我正在使用RAD Studio的XE5版本,尽管我主要使用的是C ++ Builder。
答案 0 :(得分:6)
感谢我最近的"When does a ShortCut fire"调查,我为您的框架制定了一个独立的解决方案。
简而言之:所有关键信息都输入到活动控件的TWinControl.CNKeyDwon
中。该方法调用TWinControl.IsMenuKey
遍历所有父级,同时确定该消息是否为ShortCut。是通过调用其GetPopupMenu.IsShortCut
方法来实现的。我已经覆盖了Frame的GetPopupMenu
方法,如果它不存在则创建一个方法。请注意,您始终可以自己向框架添加PopupMenu。通过继承TPopupMenu
并覆盖IsShortCut
方法,可以调用Frame的KeyDown
方法,该方法可用作您需要的KeyPreview功能。 (我也可以分配OnKeyDdown事件处理程序。)
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.
答案 1 :(得分:2)
如果您在表单上只有一个框架,那么您可以使用表单KeyPreview功能并将必要的信息转发到框架。
如果您只是转发信息,则无需对原始VCL代码进行任何更改,只需修改TFrame类即可。所以没有什么可以让你打破整个VCL。
以下是一个快速代码示例:
MainForm代码:
unit Unit2;
interface
uses
Winapi.Windows, Winapi.Messages, System.SysUtils, System.Variants, System.Classes, Vcl.Graphics,
Vcl.Controls, Vcl.Forms, Vcl.Dialogs, Unit3, Vcl.ExtCtrls, Vcl.StdCtrls;
type
TForm2 = class(TForm)
Panel1: TPanel;
ModifiedFrame: TModifiedFrame;
Edit1: TEdit;
procedure FormKeyDown(Sender: TObject; var Key: Word; Shift: TShiftState);
procedure FormCreate(Sender: TObject);
private
{ Private declarations }
public
{ Public declarations }
end;
var
Form2: TForm2;
implementation
{$R *.dfm}
procedure TForm2.FormCreate(Sender: TObject);
begin
//This is required since I'm asigning frames OnKeyDown event method manually
ModifiedFrame.OnKeyDown := ModifiedFrame.FrameKeyDown;
end;
procedure TForm2.FormKeyDown(Sender: TObject; var Key: Word;
Shift: TShiftState);
begin
//Forward key down information to ModifiedFrame
ModifiedFrame.DoKeyDown(Sender, Key, Shift);
if Key = 0 then
MessageDlg('Key was handled by the modified frame!',mtInformation,[mbOK],0)
else
MessageDlg('Key was not handled!',mtInformation,[mbOK],0);
end;
end.
ModifiedFrame代码:
unit Unit3;
interface
uses
Winapi.Windows, Winapi.Messages, System.SysUtils, System.Variants, System.Classes,
Vcl.Graphics, Vcl.Controls, Vcl.Forms, Vcl.Dialogs, Vcl.StdCtrls;
type
TModifiedFrame = class(TFrame)
Edit1: TEdit;
//Normally this method would be added by the Delphi IDE when you set the
//OnKeyDown event but here I created this manually in order to avoid crating
//design package with modified frame
procedure FrameKeyDown(Sender: TObject; var Key: Word; Shift: TShiftState);
private
{ Private declarations }
FOnKeyDown: TKeyEvent;
public
{ Public declarations }
//This is used to recieve forwarded key down information from the Form
procedure DoKeyDown(Sender: TObject; var Key: Word; Shift: TShiftState);
published
//Property to alow setting the OnKeyDown event at design-time
//NOTE: In order for this to work properly you have to put this modified
//frame class into separate unti and register it as new design time component
property OnKeyDown: TKeyEvent read FOnKeyDown write FOnKeyDown;
end;
implementation
{$R *.dfm}
procedure TModifiedFrame.DoKeyDown(Sender: TObject; var Key: Word; Shift: TShiftState);
begin
//Check to see if OnKeyDownEvent has been assigned. If it is foward the key down
//information to the event procedure
if Assigned(FOnKeyDown) then FOnKeyDown(Self, Key, Shift);
end;
procedure TModifiedFrame.FrameKeyDown(Sender: TObject; var Key: Word;
Shift: TShiftState);
begin
//Do something
if Key = VK_RETURN then
begin
MessageBeep(0);
Key := 0;
end;
end;
end.
使用simillar方法,您可以转发其他关键事件。
答案 2 :(得分:0)
如果您愿意更改VCL代码,这是可行的。
KeyPreview以TWinControl.DoKeyDown
方式处理。从具有焦点的代码控件可以看出,如果启用了DoKeyDown
,它将查找其父表单并调用其KeyPreview
方法。
function TWinControl.DoKeyDown(var Message: TWMKey): Boolean;
var
ShiftState: TShiftState;
Form, FormParent: TCustomForm;
LCharCode: Word;
begin
Result := True;
// Insert modification here
{ First give the immediate parent form a try at the Message }
Form := GetParentForm(Self, False);
if (Form <> nil) and (Form <> Self) then
begin
if Form.KeyPreview and TWinControl(Form).DoKeyDown(Message) then
Exit;
{ If that didn't work, see if that Form has a parent (ie: it is docked) }
if Form.Parent <> nil then
begin
FormParent := GetParentForm(Form);
if (FormParent <> nil) and (FormParent <> Form) and
FormParent.KeyPreview and TWinControl(FormParent).DoKeyDown(Message) then
Exit;
end;
end;
with Message do
begin
ShiftState := KeyDataToShiftState(KeyData);
if not (csNoStdEvents in ControlStyle) then
begin
LCharCode := CharCode;
KeyDown(LCharCode, ShiftState);
CharCode := LCharCode;
if LCharCode = 0 then Exit;
end;
end;
Result := False;
end;
要更改此行为,您需要更改TWinControl.DoKeyDown
代码以扫描框架,或者为要使用的每个WM_KEYDOWN
后代拦截WM_SYSKEYDOWN
和TWinControl
,最后将KeyPreview
字段添加到基类Frame类。
可能最好的选择是声明IKeyPreview
接口,并在扫描父窗体/帧时测试父级是否实现该接口。如果找不到,您可以回退到原始代码。这将仅包含VCL代码更改为TWinControl.DoKeyDown
方法,您可以在需要的框架中轻松实现接口。
注意:在具有焦点的Windows控件上接收关键事件。因此,只有当某些控件具有焦点时,上述修改才能找到帧。鼠标是否在框架上方不会对功能产生任何影响。
更详细的代码如下所示:
Frame必须实现的接口定义:
IKeyPreview = interface
['{D7318B16-04FF-43BE-8E99-6BE8663827EE}']
function GetKeyPreview: boolean;
property KeyPreview: boolean read GetKeyPreview;
end;
查找实现IKeyPreview
接口的父框架的函数应放在Vcl.Controls
实现部分的某处:
function GetParentKeyPreview(Control: TWinControl): IKeyPreview;
var
Parent: TWinControl;
begin
Result := nil;
Parent := Control.Parent;
while Assigned(Parent) do
begin
if Parent is TCustomForm then Parent := nil
else
if Supports(Parent, IKeyPreview, Result) then Parent := nil
else Parent := Parent.Parent;
end;
end;
TWinControl.DoKeyDown
修改(在原始代码中插入):
var
PreviewParent: IKeyPreview;
PreviewParent := GetParentKeyPreview(Self);
if PreviewParent <> nil then
begin
if PreviewParent.KeyPreview and TWinControl(PreviewParent).DoKeyDown(Message) then
Exit;
end;