在将代码完全在运行时创建时(即没有表单设计器组件),我将问题分配给自定义组件的继承Action属性时遇到问题。如果我在表单设计器中使用ActionList然后使用相同的代码,那么工作正常。
这是我从TCustomControl
派生的组件的构造函数:
self.FButtonSCActionList := TActionList.Create( self.Parent );
self.FButtonSCActionList.Name := 'ButtonSCActionList';
self.FButtonSCAction := TAction.Create( self.FButtonSCActionList );
self.FButtonSCAction.Name := 'ClickShortcutAction';
self.FButtonSCAction.OnExecute := self.ExecuteButtonShortcut;
self.FButtonSCAction.ShortCut := TextToShortCut('CTRL+K');
self.FButtonSCAction.Enabled := TRUE;
self.FButtonSCAction.Visible := TRUE;
self.FButtonSCAction.ActionList := self.FButtonSCActionList;
self.Action := FButtonSCAction;
如果我使用此代码创建自定义控件,将其添加到工具栏,将其放在新VCL Forms应用程序中的表单上,然后运行应用程序,当我按下快捷键时没有任何反应。如果我在没有此代码的情况下创建控件,则将其放在表单上并为表单指定一个Actionlist,然后将代码行放在一起,仅涉及创建一个操作并将其分配给组件的Action属性为该按钮的onclick事件处理程序,然后它正确响应快捷键。对于我的生活,我看不出有什么不同,但希望你的动作德尔福大师可以......
此操作的目的是允许开发人员通过属性为Object Inspector中的按钮指定自定义快捷方式。我想直接分配给“内置”操作,但无法找到如何访问其快捷方式属性。 (显然我可以通过其他HotKey delphi功能实现这一点,如果我必须这样做,但我也想了解动作,这似乎是一个好的开始......)
答案 0 :(得分:3)
您无需在设计时创建ActionList。在Create方法中使用以下代码:
FButtonSCAction := TAction.Create(Self);
FButtonSCAction.SetSubComponent(true);
FButtonSCAction.OnExecute := ExecuteButtonShortcut;
FButtonSCAction.ShortCut := TextToShortCut('CTRL+K');
FButtonSCAction.Enabled := TRUE;
FButtonSCAction.Visible := TRUE;
Action := FButtonSCAction;
if not (csDesigning in ComponentState) then
begin
FButtonSCActionList := TActionList.Create(aOwner);
FButtonSCAction.ActionList := FButtonSCActionList;
end;
在运行时创建控件期间,您可能会遇到传递给控件的aOwner不会自身形成的情况,而是另一个控件。在这种情况下,您不必使用aOwner创建动作列表,而是必须调用将从aOwner参数中为您提供表单的函数。
function GetOwnerForm(Component: TComponent): TComponent;
begin
Result := Component;
while (Result <> nil) and (not (Result is TCustomForm)) do
begin
Result := Result.Owner;
end;
end;
FButtonSCActionList := TActionList.Create(GetOwnerForm(aOwner));
答案 1 :(得分:2)
TControl
中没有内置的Action 组件。它是默认情况下未分配的Action 属性。控件的用户可以为属性分配所需的操作。控件的设计者(你)不必提供Action或ActionList。
我想直接分配给“内置”操作,但无法找到如何访问其快捷方式属性。
内置操作默认情况下只是一个未分配的TAction
属性。如果未分配属性,即属性未指向Action组件,则其ShortCut属性不存在。
此操作的目的是允许开发人员(红色。组件/控件的用户)通过属性为Object Inspector中的按钮分配自定义快捷方式。
如果这是您的唯一目标,那么只需发布Action属性,不做任何进一步的操作:
type
TMyControl = class(TCustomControl)
published
property Action;
end;
这将导致开发人员的Object Inspector中出现该属性。开发人员只需要为其分配一个自己的操作,并设置该操作的ShortCut属性。因此,实际的解决方案是摆脱所有当前的代码。
构造函数中self.FButtonSCActionList := TActionList.Create( self.Parent );
Self.Parent
为nil
。关于这一点的两件事:
首先,对您的代码进行一些善意的评论:
Self
是隐含的,不需要,也不是惯例。Name
属性集。Visible
和Enabled
属性为True。其次,正如Dalija Prasnikar已经说过的那样,在设计时不需要ActionList。 ActionList必须由控件拥有的表单间接拥有。因此控件也可以拥有ActionList(XE2)。
constructor TMyControl.Create(AOwner: TComponent);
begin
inherited Create(AOwner);
FButtonSCAction := TAction.Create(Self);
FButtonSCAction.OnExecute := ExecuteButtonShortcut;
FButtonSCAction.ShortCut := TextToShortCut('CTRL+K');
Action := FButtonSCAction;
if not (csDesigning in ComponentState) then
begin
FButtonSCActionList := TActionList.Create(Self);
FButtonSCAction.ActionList := FButtonSCActionList;
end;
end;
在XE2之前的某个地方,至少仍然在D7中,ActionList必须通过控件拥有的表单进行注册。 (还有更多内容,但由于控件不太可能是另一个表单的父级,也不是在关注另一个表单时调用该操作,因此可以进行简化)。可以通过使表单成为ActionList的所有者来完成注册。由于您将ActionList的所有权赋予控制权以外的权限,因此让ActionList通过FreeNotification
将其可能的销毁通知给控件。 (好吧,这是牵强附会的,因为通常控件也会被销毁,但这是严格应该如何完成的。)
type
TMyControl = class(TCustomControl)
private
FButtonSCActionList: TActionList;
FButtonSCAction: TAction;
protected
procedure ExecuteButtonShortcut(Sender: TObject);
procedure Notification(AComponent: TComponent; Operation: TOperation);
override;
public
constructor Create(AOwner: TComponent); override;
end;
constructor TMyControl.Create(AOwner: TComponent);
var
Form: TCustomForm;
function GetOwningForm(Component: TComponent): TCustomForm;
begin
repeat
if Component is TCustomForm then
Result := TCustomForm(Component);
Component := Component.Owner;
until Component = nil;
end;
begin
inherited Create(AOwner);
FButtonSCAction := TAction.Create(Self);
FButtonSCAction.OnExecute := ExecuteButtonShortcut;
FButtonSCAction.ShortCut := TextToShortCut('CTRL+K');
Action := FButtonSCAction;
if not (csDesigning in ComponentState) then
begin
Form := GetOwningForm(Self);
if Form <> nil then
begin
FButtonSCActionList := TActionList.Create(Form);
FButtonSCActionList.FreeNotification(Self);
FButtonSCAction.ActionList := FButtonSCActionList;
end;
end;
end;
procedure TMyControl.ExecuteButtonShortcut(Sender: TObject);
begin
//
end;
procedure TMyControl.Notification(AComponent: TComponent;
Operation: TOperation);
begin
inherited Notification(AComponent, Operation);
if (AComponent = FButtonSCActionList) and (Operation = opRemove) then
FButtonSCActionList := nil;
end;
请注意,当GetOwningForm
返回False
时(当开发人员创建没有所有者的控件时),不会创建ActionList,因为它无法解析拥有的表单。重写SetParent可以解决这个问题。
因为将所有权转移到另一个组件是不必要的(并且在csDesigning in ComponentState
运行代码时可能会给IDE的流系统带来问题),还有另一种注册 ActionList的方法将表单添加到受保护的FActionLists
字段中的表单:
type
TCustomFormAccess = class(TCustomForm);
constructor TMyControl.Create(AOwner: TComponent);
var
Form: TCustomForm;
function GetOwningForm(Component: TComponent): TCustomForm;
begin
repeat
if Component is TCustomForm then
Result := TCustomForm(Component);
Component := Component.Owner;
until Component = nil;
end;
begin
inherited Create(AOwner);
FButtonSCAction := TAction.Create(Self);
FButtonSCAction.OnExecute := ExecuteButtonShortcut;
FButtonSCAction.ShortCut := TextToShortCut('CTRL+K');
Action := FButtonSCAction;
if not (csDesigning in ComponentState) then
begin
Form := GetOwningForm(Self);
if Form <> nil then
begin
FButtonSCActionList := TActionList.Create(Self);
FButtonSCAction.ActionList := FButtonSCActionList;
if TCustomFormAccess(Form).FActionLists = nil then
TCustomFormAccess(Form).FActionLists := TList.Create;
TCustomFormAccess(Form).FActionLists.Add(FButtonSCActionList)
end;
end;
end;
TControl.Action
是公共属性,TControl.SetAction
不是虚拟的。这意味着控件的用户可以分配不同的Action,使此Action无用,并且您无法对其执行任何操作。 (不发布是不够的)。相反,声明另一个Action属性,或者 - 再次 - 提供一个单独的Action组件。答案 2 :(得分:0)
非常感谢所有的帮助!对于那些将稍后使用这个问题google-fu的人(这些日子里我不是在Delphi IDE中的 live )这里是自定义组件的最终完整功能代码:
unit ActionTester;
interface
uses
Winapi.windows,
Vcl.ExtCtrls,
System.Types,
System.SysUtils ,
System.Classes,
Vcl.Controls,
Vcl.Forms,
Vcl.Graphics,
Messages,
Vcl.Buttons,
System.Variants,
System.UITypes,
Dialogs,
Vcl.ExtDlgs,
Generics.Collections,
System.Actions,
Vcl.ActnList,
Clipbrd,
TypInfo,
Rtti,
Menus;
type
TActionTester = class(TCustomControl)
private
{ Private declarations }
protected
{ Protected declarations }
FButtonSCActionList: TActionList;
FButtonSCAction: TAction;
procedure ExecuteButtonShortcut(Sender: TObject);
procedure Notification(AComponent: TComponent; Operation: TOperation);
override;
public
{ Public declarations }
constructor Create(AOwner: TComponent); override;
Procedure Paint; override;
Destructor Destroy; Override;
published
{ Published declarations }
Property OnClick;
end;
procedure Register;
implementation
procedure Register;
begin
RegisterComponents('Samples', [TActionTester]);
end;
{ TActionTester }
constructor TActionTester.Create(AOwner: TComponent);
var
Form: TCustomForm;
function GetOwningForm(Component: TComponent): TCustomForm;
begin
result := NIL;
repeat
if Component is TCustomForm then
Result := TCustomForm(Component);
Component := Component.Owner;
until Component = nil;
end;
begin
inherited Create(AOwner);
FButtonSCAction := TAction.Create(Self);
FButtonSCAction.OnExecute := ExecuteButtonShortcut;
FButtonSCAction.ShortCut := TextToShortCut('CTRL+K');
FButtonSCAction.SetSubComponent(true);
if not (csDesigning in ComponentState) then
begin
Form := GetOwningForm(Self);
if Form <> nil then
begin
FButtonSCActionList := TActionList.Create(Form);
FButtonSCActionList.FreeNotification(Self);
FButtonSCAction.ActionList := FButtonSCActionList;
end;
end;
end;
destructor TActionTester.Destroy;
begin
FreeAndNil( self.FButtonSCAction );
inherited;
end;
procedure TActionTester.ExecuteButtonShortcut(Sender: TObject);
begin
if assigned( self.OnClick ) then self.OnClick( self );
end;
procedure TActionTester.Notification(AComponent: TComponent; Operation: TOperation);
begin
inherited Notification(AComponent, Operation);
if (AComponent = FButtonSCActionList) and (Operation = opRemove) then
FButtonSCActionList := nil;
end;
procedure TActionTester.Paint;
begin
inherited;
self.Canvas.Brush.Color := clGreen;
self.Canvas.Brush.Style := bsSolid;
self.Canvas.FillRect( self.GetClientRect );
end;
end.
就像一个魅力! NGLN,David和Dalija的主要荣誉!