I am having a problem getting an Action assigned to a custom component's inherited Action property to work when the code is entirely created at run time (i.e. no form designer components). If I use an ActionList in the form designer and then use the same code things work fine.
Here is my constructor of a component derived from 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;
If I create the custom control with this code, add it to the toolbar, place it on a form in a new VCL Forms application and then run the application, when I press the shortcut key nothing happens. If I create the control without this code, place it on a form and assign an Actionlist to the form, and then put the code lines just involving creating an action and assigning it to the component's Action property into an onclick event handler for the button, it then responds to the shortcut keypress correctly. For the life of me I can't see what is different, but hopefully you Actions Delphi gurus can...
The purpose of this Action is to allow the developer to assign a custom shortcut to the button in the Object Inspector via a property. I would like to assign directly to the "built in" Action but cannot find out how to access its Shortcut Property. (Obviously I could do this via the other HotKey delphi functionality and will if I have to but I also want to understand Actions and this seems a good place to start...)
Summary
There is no built-in Action component in
TControl. It is an Action property that is unassigned by default. The user of the control can assign the property with whatever Action is desired. The designer of the control (you) does not have to provide an Action nor ActionList.The actual problem
That built-in Action is by default just an unassigned
TActionproperty. And if the property is not assigned, i.e. the property does not point to an Action component, then its ShortCut property does not exist.If that is your sole goal, then simply publish the Action property and do nothing further:
This will result in the appearance of the property in the developer's Object Inspector. The developer simply has to assign one of his own actions to it, and to set the ShortCut property of thát action. Thus the actual solution is to get rid of all your current code.
Why your current code doesn't work
Self.Parentisnilduring the constructor. Two things about that:Solution for the current code
First, some well-intentioned comments on your code:
Selfis implicit and is not needed, nor customary.Nameproperty set.VisibleandEnabledproperties of an action are True by default.Secondly, as Dalija Prasnikar already said, the ActionList is not needed at design time. And the ActionList has to be indirectly owned by the form that the control owns. So the control can own the ActionList too (XE2).
Somehere before XE2, at least still in D7, the ActionList had to be registered by the form that the control owns. (There is more to it, but since it is unlikely that the control is parented by another form nor that the action is invoked when another form is focussed, this simplification can be made). Registration could be done by making the form the owner of the ActionList. Since you give ownership of the ActionList beyond the control, let the ActionList notify its possibly destruction to the control with
FreeNotification. (Ok, this is far-fetched, since typically the control then will be destroyed as well, but this is how it strictly should be done).Note that when
GetOwningFormreturnsFalse(when the developer creates the control without owner), the ActionList is not created because it cannot resolve the owning form. Overriding SetParent could fix that.Because transfering ownership to another component feels unnecessary (and could give problems with the IDE's streaming system when the code is run if
csDesigning in ComponentState), there is another way to register the ActionList to the form by adding it to the protectedFActionListsfield:Reflection on this solution:
TControl.Actionis a public property, andTControl.SetActionis not virtual. This means that the user of the control can assign a different Action, rendering this Action useless, and you cannot do anything about nor against it. (Not publishing is not enough). Instead, declare another Action property, or - again - offer a separate Action component.