MouseDown event using dynamic buttons in Delphi
I am dynamically creating buttons from an array that is dynamically loaded. I am storing a piece of needed information in the buttons hint.
procedure TForm3.CreateAppButton(sBtnCapt: string);
var
hIcon: THandle;
nIconId: DWORD;
Icon: TIcon;
NewButton: TSpeedButton;
PicConvert: TBitmap;
sPathNew: string;
AppData: TAppDetails;
begin
AppData := TAppDetails.Create(sBtnCapt);
NewButton := TSpeedButton.Create(self);
with NewButton do
begin
Width := 67;
Height := 50;
Left := (Width + 5) * (self.ControlCount - 1);
Top := 5;
Parent := self;
Caption := AppData.Caption;
Name := AppData.ButtonName;
Font.Size := 7;
// extract a 16x16 icon for display on the buttom
sPathNew := '';
sPathNew := sPath + AppData.Exe;
if PrivateExtractIcons(PChar(sPathNew), 0, 16, 16, @hIcon, @nIconId, 1, LR_LOADFROMFILE)
<> 0 then
try
PicConvert := TBitmap.Create;
Icon := TIcon.Create;
try
Icon.Handle := hIcon;
PicConvert.Width := Icon.Width;
PicConvert.Height := Icon.Height;
PicConvert.Canvas.Draw(0, 0, Icon);
Glyph := PicConvert;
finally
Icon.Free;
PicConvert.Free;
end;
finally
DestroyIcon(hIcon);
end;
OnMouseDown := btnMouseDown;
Hint := AppData.Exe;
ShowHint := False;
Layout := blGlyphTop;
AppData.Free;
end;
end;
Where my issue arises is on the mouse down event. I want to somehow use the information stored in the buttons hint property to trigger the needed action. You can see what I am trying to in the left click and where I am coming from in the right click. Designing the right click way would require me to put in ~80 it also requires that I declare each button on the form so that I can build and compile.
procedure TForm3.btnMouseDown(Sender: TObject; Button: TMouseButton; Shift: TShiftState;
X, Y: Integer);
var
sApp: string;
AppData: TAppDetails;
begin
sApp := '';
if Button = mbLeft then
begin
sApp := btnFoo.Hint;
ShellExecute(sel开发者_StackOverflowf.Handle, 'open', PChar(sApp), nil, nil, SW_SHOWNORMAL);
end
else if Button = mbRight then
begin
if Sender = btnCC3 then
begin
sApp := 'CaseClaims3.exe';
end
else if Sender = btnODBC then
begin
sApp := 'ODBCMgr.exe';
end
else if Sender = btnCredMaint then
begin
sApp := 'CreditorMaint';
end;
AppData := TAppDetails.Create(sApp);
ShellExecute(self.Handle, 'open', PChar(AppData.Wiki), nil, nil, SW_SHOWNORMAL);
AppData.Free;
end;
end;
Can anyone point me in a direction to dynamically use the mousedown event without declaring the component ahead of time? Appdata is a separate pas file containing all the needed information on each application in case anyone is wondering.
Also please forgive my rather horrendous code, I know it could use a lot of work.
I agree that you should not use the Hint
property. Not least because you may at some point in time wish to use the Hint
property for something else. I am also somewhat dubious of solutions that use Tag
.
I think I would use an instance of TDictionary<TSpeedButton,TAppPaths>
to map between buttons and the application details. Here I'm imagining that TAppPaths
is a record containing two strings, one for the left click and one for the right click, e.g.
TAppPaths= record
Left: string;
Right: string;
end;
For sake of argument, let us suppose that this instance is called ButtonDetails
.
When ever you add a new button you write code like this:
AppPaths.Left := AppDetails.Exe;
AppPaths.Right := AppDetails.SomeOtherFunction;
ButtonDetails.Add(NewButton, AppPaths);
Then in the form's mouse event handler you write it like this:
AppPaths := ButtonDetails[Sender as TSpeedButton];
And now AppPaths
has both paths ready for you to use.
This idea can easily be extended to include as much information as you like in the TAppPaths
record.
As an aside, I think you should be handling MouseUp
rather than MouseDown
since in Windows, buttons are clicked when you release the mouse button rather than when you press it.
The Sender
of the event is the button, so save the "appData" into button's tag (when you create it) and then in the mouse event use it, ie
procedure TForm3.CreateAppButton(sBtnCapt: string);
...
begin
AppData := TAppDetails.Create(sBtnCapt);
with NewButton do
begin
Tag := NativeInt(AppData);
...
end;
//AppData.Free; // NB! do not free here!
end;
procedure TForm3.btnMouseDown(Sender: TObject; Button: TMouseButton; Shift: TShiftState;X, Y: Integer);
var AppData: TAppDetails;
begin
...
else if Button = mbRight then
begin
AppData := TAppDetails((Sender as TSpeedButton).Tag);
// use the appData
ShellExecute(self.Handle, 'open', PChar(AppData.Wiki), nil, nil, SW_SHOWNORMAL);
//AppData.Free; // NB! do not free here!
end;
end;
procedure TForm3.DeleteAppButton(aBtn: TSpeedButton);
begin
TAppDetails(aBtn.Tag).Free;
aBtn.Free;
end;
Note that you have to free the objects you saved into button's tag when the buttons are freed (unless the buttons are not freed until app terminates, then you don't have to worry about it althought it isn't good style).
I'm not sure I understand the problem completely, but from what I've gathered so far, you probably need to do something like this:
…
sApp := TSpeedButton(Sender).Hint
…
whenever/wherever you need to retrieve the Hint
information in the handler.
Do not use hints to store informations, use component Tag property as index on your information data array instead!
EDIT:
When you are creating button than you assign also button data in to button Tag like:
TButtonData =record
InfoStr1: string;
InfoStr2: string;
a : cardinal;
//...
end;
TButtonDataAr = array of TButtonData;
var
ButtonDataAr: TButtonDataAr;
when you creating button assign...
SetLength(ButtonDataAr, Length(ButtonDataAr) + 1);
ButtonDataAr[Length(ButtonDataAr)].InfoStr1 = 'TEST';
Button[n].Tag := Length(ButtonDataAr); //Set array index as Tag
When you want to retrive information from sender just use component Tag as ButtonDataAr index,
精彩评论