I have a requirement to provide a button that executes ActionA when
clicked normally and ActionB when clicked with CTRL being depressed.
The important thing here is that the logic to decide what to do is not
contained within the execute call of the action. It needs to be two
seperate actions.
From an IDE designer stand point it would be good to be able to assign
both the normal action and the CTRL action at design time. A TButton
descendant adding a new published ActionCtrl : TAction would do this.
Then the trick would be to determine which action to invoke etc based
on when the button is clicked and of course to update the button
caption as soon as CTRL is pressed.
As I will have a number of these buttons across different forms (that
may be visible at the same time) and potentially this may extend to
other controls in the future I was interested in any ideas / approaches
on how to achieve the above in an elegant, maintainable and extensible
fashion.
All help, suggestions greatly appreciated.
--
-- Donovan
> From an IDE designer stand point it would be good to be able to
> assign both the normal action and the CTRL action at design time.
> A TButton descendant adding a new published ActionCtrl : TAction
> would do this. Then the trick would be to determine which action to
> invoke etc based on when the button is clicked
One way would be to implement a custom descendant of the TButtonActionLink
class, and then override the virtual GetActionLinkClass() method in your
button component to return that class type. You can then override your
ActionLink's virtual Execute() method to Execute() whichever Action you need
based on the CTRL key's current state (you can use GetKeyState() for that).
For example (untested - you might need more hooks in place to get a
secondary TAction working fully):
type
TMyButton = class;
TMyButtonActionLink = class(TButtonActionLink)
protected
FClient: TMyButton;
procedure AssignClient(AClient: TObject); override;
function IsOnExecuteLinked: Boolean: override;
public
function Execute(AComponent: TComponent = nil): Boolean;
override;
end;
TMyButton = class(TButton)
protected
FCtrlAction: TBasicAction;
function GetActionLinkClass: TControlActionLinkClass; override;
procedure SetCtrlAction(Value: TBasicAction);
protected
property CtrlAction: TBasicAction read FCtrlAction write
SetCtrlAction;
end;
procedure TMyButtonActionLink.AssignClient(AClient: TObject);
begin
inherited AssignClient(AClient);
FClient := AClient as TMyButton;
end;
function TMyButtonActionLink.IsOnExecuteLinked: Boolean;
begin
Result := True;
end;
function TMyButtonActionLink.Execute(AComponent: TComponent): Boolean;
var
LAction: TBasicAction;
begin
if ((GetKeyState(VK_CONTROL) and $8000) <> 0) and
(FClient.CtrlAction <> nil) then
LAction := FClient.CtrlAction
else
LAction := FClient.Action;
if LAction.ActionComponent <> AComponent then
begin
if Assigned(LAction.ActionComponent) then
LAction.ActionComponent.RemoveFreeNotification(LAction);
if Assigned(AComponent) then
AComponent.FreeNotification(LAction);
LAction.ActionComponent := AComponent;
end;
Result := LAction.Execute;
end;
function TMyButton.GetActionLinkClass: TControlActionLinkClass;
begin
Result := TMyButtonActionLink;
end;
procedure TMyButton.SetCtrlAction(Value: TBasicAction);
begin
if FCtrlAction <> nil then FCtrlAction.RemoveFreeNotification(Self);
FCtrlAction := Value;
if FCtrlAction <> nil then FCtrlAction.FreeNotification(Self);
end;
> and of course to update the button caption as soon as CTRL is pressed.
I am not sure if you will be able to do that.
Gambit
>
> "Donovan J. Edye" <don...@nospam.namsys.com.au> wrote in message
> news:4844...@newsgroups.borland.com...
>
> > From an IDE designer stand point it would be good to be able to
> > assign both the normal action and the CTRL action at design time.
> > A TButton descendant adding a new published ActionCtrl : TAction
> > would do this. Then the trick would be to determine which action to
> > invoke etc based on when the button is clicked
>
> One way would be to implement a custom descendant of the
> TButtonActionLink class, and then override the virtual
> GetActionLinkClass() method in your button component to return that
> class type. You can then override your ActionLink's virtual
> Execute() method to Execute() whichever Action you need based on the
> CTRL key's current state (you can use GetKeyState() for that).
>
Thanks for that. Ultimately I went for a central class that the
components talk to to register their normal and ctrl action. That
central class has a global keyhook which then wathes key state and then
updates each registered components action property based on what was
originally supplied.
--
-- Donovan