Delphi 运行时分配的操作';的快捷方式在自定义组件中不触发

Delphi 运行时分配的操作';的快捷方式在自定义组件中不触发,delphi,components,action,hotkeys,Delphi,Components,Action,Hotkeys,当代码完全在运行时创建(即没有表单设计器组件)时,我在获取分配给自定义组件的继承操作属性的操作时遇到问题。如果我在表单设计器中使用ActionList,然后使用相同的代码,那么一切都很好 这是我从TCustomControl派生的组件的构造函数: self.fButtonCachionList:=TActionList.Create(self.Parent); self.fButtonCActionList.Name:=“ButtonCActionList”; self.fbuttoncacti

当代码完全在运行时创建(即没有表单设计器组件)时,我在获取分配给自定义组件的继承操作属性的操作时遇到问题。如果我在表单设计器中使用ActionList,然后使用相同的代码,那么一切都很好

这是我从
TCustomControl
派生的组件的构造函数:

self.fButtonCachionList:=TActionList.Create(self.Parent);
self.fButtonCActionList.Name:=“ButtonCActionList”;
self.fbuttoncaction:=TAction.Create(self.fbuttoncactionlist);
self.fbuttoncaction.Name:=“ClickShortcutAction”;
self.fbuttoncaction.OnExecute:=self.ExecuteButtonShortcut;
self.fbuttoncaction.ShortCut:=TextToShortCut('CTRL+K');
self.fbuttoncaction.Enabled:=真;
self.fbuttoncaction.Visible:=真;
self.fButtonCaction.ActionList:=self.fButtonCactionList;
self.Action:=fButtonCachation;
如果使用此代码创建自定义控件,请将其添加到工具栏,将其放置在新VCL Forms应用程序的窗体上,然后运行该应用程序,当我按快捷键时,不会发生任何事情。如果我创建的控件没有此代码,将其放置在表单上并为表单分配Actionlist,然后将仅涉及创建操作并将其分配给组件的action属性的代码行放入按钮的onclick事件处理程序中,然后它会正确响应快捷键。就我的一生而言,我看不出有什么不同,但希望各位大师能

此操作的目的是允许开发人员通过属性为对象检查器中的按钮指定自定义快捷方式。我想直接分配给“内置”操作,但找不到如何访问其快捷方式属性。(显然,我可以通过其他热键delphi功能完成这项工作,如果有必要,我也会这样做,但我也想了解操作,这似乎是一个很好的开始…

总结
t控件中没有内置的操作组件。它是默认情况下未指定的操作属性。控件的用户可以通过所需的任何操作分配属性。控件的设计者(您)不必提供操作或操作列表

实际问题 我想直接分配给“内置”操作,但找不到如何访问其快捷方式属性

默认情况下,该内置操作只是一个未分配的
TAction
属性。如果未指定属性,即属性未指向动作组件,则其快捷方式属性不存在

此操作的目的是允许开发人员(红色。组件/控件的用户)通过属性为对象检查器中的按钮指定自定义快捷方式

如果这是您的唯一目标,则只需发布Action属性,不做任何进一步的操作:

type
  TMyControl = class(TCustomControl)
  published
    property Action;
  end;
这将导致属性出现在开发人员的对象检查器中。开发人员只需将自己的一个操作分配给它,并设置Thatát action的快捷方式属性。因此,实际的解决方案是去掉所有当前代码

为什么您当前的代码不起作用
Self.Parent
在构造函数期间为
nil
。关于这一点,有两点:

  • 除非您自己在de destructor中销毁ActionList,否则将发生内存泄漏
  • 对于默认快捷方式处理,应用程序将遍历当前焦点窗体或主窗体(间接)拥有的所有操作列表。您的操作列表没有所有者,因此它的快捷方式永远不会被计算
当前代码的解决方案 首先,对代码进行一些善意的评论:

  • Self
    是隐含的,不需要,也不习惯
  • 运行时生成的组件不需要
    名称
    属性集
  • 默认情况下,操作的
    可见属性
    启用属性
    为真
第二,如前所述,在设计时不需要ActionList。ActionList必须由控件所拥有的表单间接拥有。因此控件也可以拥有ActionList(XE2)

在XE2之前,至少在D7中,ActionList必须由控件拥有的表单注册。(还有更多内容,但由于控件不太可能由另一个窗体作为父控件,也不太可能在聚焦另一个窗体时调用该操作,因此可以进行这种简化)。注册可以通过使表单成为ActionList的所有者来完成。由于您将ActionList的所有权授予了无法控制的权限,因此让ActionList使用
FreeNotification
将其可能的销毁通知给控件。(好的,这很牵强,因为通常控件也会被破坏,但严格来说应该这样做)

对该解决方案的思考:
  • 这种做法是不可取的。不应在自定义控件中创建动作组件。如果必须,请分别提供它们,以便控件的用户可以决定将自定义操作添加到哪个操作列表中。另见:
  • TControl.Action
    是公共属性,
    TControl.SetAction
    不是虚拟属性。这意味着控件的用户可以指定不同的操作,使此操作无效,并且您不能对其做任何更改或反对。(不出版是不够的)。相反,声明另一个Action属性,或者再次提供一个单独的Action组件

    • 您不需要在设计时创建操作列表。在创建方法中使用以下代码:

        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;
      
      在运行时创建控件的过程中,可能会出现这样的情况:传递给控件的AOOwner将不是窗体本身,而是另一个控件。在这种情况下,您需要调用
      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;
      
      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;
      
      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;
      
        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;
      
      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));