I want to dynamically create TImage controls and then drag and drop them. But if I want to assign the procedure used for the dragging to an event of this Image, it gives me:
Error: Wrong number of parameters specified for call to "ClickEvent"
This is my code for the dragging:
procedure ClickEvent(Sender:TObject; Button:TMouseButton; Shift:TShiftstate; X,Y:Integer);
begin
if Button = mbLeft then TControl(Sender).BeginDrag(False);
end;
And here I create the Image and add the properties:
procedure SpawnCard(Ort:TWinControl; sKarte:TKarteClass; Liste: Array of TKarte; BilderListe:Array of TCustomImage);
var
Bild:TImage;
begin
Liste[High(Liste)]:=sKarte.Create();
Bild:=TImage.Create(Combat);
with Bild do
begin
OnMouseDown:=ClickEvent;
Parent:=Ort;
Top:=1;
Left:=200*length(BilderListe);
width:=200;
height:=300;
Proportional:=True;
Stretch:=True;
Picture.LoadFromFile(Liste[High(Liste)].PicPath);
end;
BilderListe[High(BilderListe)]:=Bild;
end;
I don't want to call ClickEvent, I just want to assign it to the event.
TImage.OnMouseDown (inherited from its TControl parent class) is a TMouseEvent property.
TMouseEvent = procedure(Sender: TObject; Button: TMouseButton;
Shift: TShiftState; X, Y: Integer) of object;
As you can see, it's declared as "of object", which means it expects a method pointer (See the Method Pointers section).
Example 1:
Declare the ClickEvent on the form (or any other object):
TForm1 = class(TForm)
Image1: TImage;
public
procedure ClickEvent(Sender: TObject; Button: TMouseButton; Shift: TShiftState; X, Y: Integer);
end;
...
procedure TForm1.ClickEvent(Sender: TObject; Button: TMouseButton; Shift: TShiftState; X, Y: Integer);
begin
if Button = mbLeft then TControl(Sender).BeginDrag(False);
end;
Then you can assign it as follows:
Image1.OnMouseDown := Form1.ClickEvent;
Example 2:
Declare the ClickEvent as a class method:
TMyEventHandlers = class
class procedure ClickEvent(Sender: TObject; Button: TMouseButton; Shift: TShiftState; X, Y: Integer);
end;
...
class procedure TMyEventHandlers.ClickEvent(Sender: TObject; Button: TMouseButton; Shift: TShiftState; X, Y: Integer);
begin
if Button = mbLeft then TControl(Sender).BeginDrag(False);
end;
Then you can assign it as follows:
Image1.OnMouseDown := TMyEventHandlers.ClickEvent;
Example without BalloonHint1 works as designed. No issues with the Hint refresh.
procedure TForm1.ControlList1MouseMove(Sender: TObject;
Shift: TShiftState; X, Y: Integer);
begin
ControlList1.ShowHint:=false;
//ControlList1.CustomHint <-------value is not set as it is not required.
ControlList1.Hint := IntToStr(ControlList1.HotItemIndex);
ControlList1.ShowHint:=true;
end;
When I add a TBalloonHint, the BalloonHint does not display properly.
procedure TForm1.ControlList1MouseMove(Sender: TObject;
Shift: TShiftState; X, Y: Integer);
begin
ControlList1.ShowHint:=false;
BalloonHint1.Delay:=0;
BalloonHint1.HideAfter:=-1;
ControlList1.CustomHint:=BalloonHint1;
ControlList1.Hint := IntToStr(ControlList1.HotItemIndex);
ControlList1.ShowHint:=true;
end;
When I move my mouse over the ControlList for the first time. A BalloonHint does not show.
If I move my mouse over again (for the 2nd time) then the HotItemIndex from the previous movement shows the index.
Is there a way to do a BalloonHint1.Refresh?
I have tested some of the following:
Application.CancelHint; ///something that I dont want to do... but i gave it a try
also
ControlList1.ShowHint:=false;
ControlList1.ShowHint:=true;
The following worked.
procedure TForm1.ControlList1MouseMove(Sender: TObject;
Shift: TShiftState; X, Y: Integer);
begin
BalloonHint1.HideHint;
BalloonHint1.Delay:=0;
BalloonHint1.HideAfter:=-1;
ControlList1.CustomHint:=BalloonHint1;
ControlList1.Hint := IntToStr(ControlList1.HotItemIndex);
BalloonHint1.ShowHint(ControlList1);
end;
I then found that the BalloonHint flickered. So I used advice from the following:
Delphi ListView hint flickers
Create a global variable in which I would store reference to last HotItemIndex for which the hint has been shown. Then verify if the current HotItemIndex is the same as the one we stored controlListHotItemIndex.
procedure TForm1.ControlList1MouseMove(Sender: TObject;
Shift: TShiftState; X, Y: Integer);
begin
if controlListHotItemIndex<>ControlList1.HotItemIndex then
begin
controlListHotItemIndex:=ControlList1.HotItemIndex;
BalloonHint1.HideHint;
BalloonHint1.Delay:=0;
BalloonHint1.HideAfter:=-1;
ControlList1.CustomHint:=BalloonHint1;
ControlList1.Hint := IntToStr(ControlList1.HotItemIndex);
BalloonHint1.ShowHint(ControlList1);
end;
end;
I'm new to Delphi. I need a help for complete my code. I just drawing a path. Now i want to save it to Android local storage as an Image format.
This is my code
procedure TForm1.FormMouseDown(Sender: TObject; Button: TMouseButton;
Shift: TShiftState; X, Y: Single);
begin
if ssLeft in Shift then
Path1.Data.MoveTo(PointF(X,Y) - Path1.BoundsRect.TopLeft);
end;
procedure TForm1.FormMouseMove(Sender: TObject; Shift: TShiftState; X,
Y: Single);
begin
if ssLeft in Shift then
Path1.Data.LineTo(PointF(X,Y) - Path1.BoundsRect.TopLeft);
end;
procedure TForm1.SpeedButton1Click(Sender: TObject);
begin
//save code goes here
end;
my application has 350 edit fields and all of them shall have an OnMouseMove event.
I have generated this code for all of them:
...
type
...
procedure Edit1MouseMove(Sender: TObject; Shift: TShiftState; X, Y: Integer);
procedure Edit2MouseMove(Sender: TObject; Shift: TShiftState; X, Y: Integer);
...
implementation
{$R *.dfm}
...
procedure TForm1.Edit1MouseMove(Sender: TObject; Shift: TShiftState; X,
Y: Integer);
begin
Edit1.SetFocus();
end;
procedure TForm1.Edit2MouseMove(Sender: TObject; Shift: TShiftState; X,
Y: Integer);
begin
Edit2.SetFocus();
end;
...
But I didn't go to the object inspector to doubleclick OnMouseMove.
Is there a way to make this work without the object inspector.
Do you have an example line of code that would make it work for the first edit field?
You can create it once and assign it in code yourself:
type
TForm1=class(TForm)
procedure EditMouseMove(Sender: TObject; Shift: TShiftState;
X, Y: Integer);
procedure FormCreate(Sender: TObject);
//...
end;
implementation
procedure TForm1.EditMouseMove(Sender: TObject; Shift: TShiftState;
X, Y: Integer);
var
CurrEdit: TEdit;
begin
if (Sender is TEdit) then
begin
CurrEdit := TEdit(Sender);
// Do whatever with CurrEdit
end;
end;
procedure TForm1.FormCreate(Sender: TObject);
begin
Edit1.OnMouseMove := EditMouseMove;
Edit2.OnMouseMove := EditMouseMove;
Edit3.OnMouseMove := EditMouseMove;
end;
If you want to assign the same one to every TEdit on the form:
procedure TForm1.FormCreate(Sender: TObject);
var
i: Integer;
begin
for i := 0 to ControlCount - 1 do
if Controls[i] is TEdit then
TEdit(Controls[i]).OnMouseMove := EditMouseMove;
end;
I have a situation where I have a TImage and on top of it a TPanel covering it partially and they share the same parent:
------------------
| Image1 |
| ------------ |
| | Panel1 | |
| ------------ |
| |
------------------
Panel1 is receiving mouse down/move/up events and processing it (so does Image1), but in some situation I would like to "redirect" the mouse down message to Image1 as if to simulate that Image1 was clicked rather than Panel1.
Here is what I did:
procedure TForm1.Image1MouseDown(Sender: TObject; Button: TMouseButton;
Shift: TShiftState; X, Y: Integer);
begin
if (ssLeft in Shift) then
Beep;
end;
procedure TForm1.Image1MouseMove(Sender: TObject; Shift: TShiftState;
X, Y: Integer);
begin
//...
end;
procedure TForm1.Image1MouseUp(Sender: TObject; Button: TMouseButton;
Shift: TShiftState; X, Y: Integer);
begin
ShowMessage('boo!');
end;
procedure TForm1.Panel1MouseDown(Sender: TObject; Button: TMouseButton;
Shift: TShiftState; X, Y: Integer);
var
P: TPoint;
begin
if FRedirectToImage then begin
ReleaseCapture; // do I need to send a WM_LBUTTONUP as well to the panel?
GetCursorPos(P);
P := ScreenToClient(P);
Image1.Perform(WM_LBUTTONDOWN, MK_LBUTTON, Longint(PointToSmallPoint(P)));
Exit;
end;
// Normal handling
if (ssLeft in Shift) then begin
// ...
end;
end;
It works as expected but I'm not sure It's the right way.
My question is, am I doing it right? is there a better or recommended way of doing it?
Update (1) :
Handling WM_NCHITTEST as suggested is a valid answer and I thought about it also. even setting Panel1.Enabled to False will route the mouse messages to the underlying Image1 control.
But (!) consider this situation where I click the x location on the Panel and still need to route the message to Image1:
------------------
| Image1 |
| --------------
| | Panel1 x |
| --------------
| |
------------------
My method works, but WM_NCHITTEST is not applicable in the described scenario. I still didn't get an answer if my method is valid or not. (or maybe I should ask another question with the above scenario?)
Handle wm_NCHitTest messages sent to the panel and return htTransparent. The OS will send the mouse message to the next control down without any further processing required from your program. (From the OS perspective, the "next control down" is the parent control of both the panel and the image; the VCL takes care of routing the mouse message back to the image control, as it does with all TGraphicControl descendants, since they aren't real windowed controls.)
Something like this:
procedure TParentForm.PanelWindowProc(var Msg: TMessage);
begin
FPrevPanelWindowProc(Msg);
if (Msg.Message = wm_NCHitTest) and FRedirectToImage then
Msg.Result := htTransparent;
end;
Assign that method to the panel's WindowProc method. Store the previous value of the property in a field of the form.
var
FPrevPanelWindowProc: TWndMethod;
FPrevPanelWindowProc := Panel.WindowProc;
Panel.WindowProc := Self.PanelWindowProc;
In case when the control from which you want to redirect mouse events will not be in its whole client area inside the control to which those events should be redirected (as you've shown in your question update), then the WM_NCHITTEST message might be send to another control. Then the only one way remains to use IMHO, redirect all mouse messages.
As #David mentioned in his comment, you can do this message redirection in a global way by writing an event handler for the OnMessage event for TApplication. Or use a TApplicationEvents object.
In the following example, you can define the range of messages, that will be redirected as well as specify the list of source and target controls for that redirection. For redirecting is used the OnMessage event of the TApplication object, but since your target is in this case TGraphicControl descendant, you can't only change the recipient of the incoming message, but you have to eat this message and perform the message on the target control through the Perform method by yourself.
Here is the code showing how to redirect all mouse messages from Panel1 to Image1. You can get the whole testing project from here if you want:
unit Unit1;
interface
uses
Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms,
Dialogs, StdCtrls, ExtCtrls;
type
TMsgRange = record
MsgFrom: UINT;
MsgTo: UINT;
end;
TRedirect = record
Source: HWND;
Target: TControl;
end;
type
TForm1 = class(TForm)
Memo1: TMemo;
Panel1: TPanel;
Image1: TImage;
procedure FormCreate(Sender: TObject);
procedure Image1MouseDown(Sender: TObject; Button: TMouseButton;
Shift: TShiftState; X, Y: Integer);
procedure Panel1MouseDown(Sender: TObject; Button: TMouseButton;
Shift: TShiftState; X, Y: Integer);
procedure Image1MouseUp(Sender: TObject; Button: TMouseButton;
Shift: TShiftState; X, Y: Integer);
procedure Panel1MouseUp(Sender: TObject; Button: TMouseButton;
Shift: TShiftState; X, Y: Integer);
private
FRedirectList: array of TRedirect;
FRedirectEnabled: Boolean;
FRedirectMsgRange: TMsgRange;
procedure ApplicationMessage(var AMessage: TMsg; var Handled: Boolean);
public
{ Public declarations }
end;
var
Form1: TForm1;
implementation
{$R *.dfm}
procedure TForm1.ApplicationMessage(var AMessage: TMsg; var Handled: Boolean);
var
I: Integer;
begin
if FRedirectEnabled and (AMessage.message >= FRedirectMsgRange.MsgFrom) and
(AMessage.message <= FRedirectMsgRange.MsgTo) then
begin
for I := 0 to High(FRedirectList) do
if (AMessage.hwnd = FRedirectList[I].Source) and
Assigned(FRedirectList[I].Target) then
begin
Handled := True;
FRedirectList[I].Target.Perform(AMessage.message,
AMessage.wParam, AMessage.lParam);
Break;
end;
end;
end;
procedure TForm1.FormCreate(Sender: TObject);
begin
FRedirectEnabled := True;
FRedirectMsgRange.MsgFrom := WM_MOUSEFIRST;
FRedirectMsgRange.MsgTo := WM_MOUSELAST;
SetLength(FRedirectList, 1);
FRedirectList[0].Source := Panel1.Handle;
FRedirectList[0].Target := Image1;
Application.OnMessage := ApplicationMessage;
end;
procedure TForm1.Image1MouseDown(Sender: TObject; Button: TMouseButton;
Shift: TShiftState; X, Y: Integer);
begin
Memo1.Lines.Add('Image1MouseDown')
end;
procedure TForm1.Image1MouseUp(Sender: TObject; Button: TMouseButton;
Shift: TShiftState; X, Y: Integer);
begin
Memo1.Lines.Add('Image1MouseUp')
end;
procedure TForm1.Panel1MouseDown(Sender: TObject; Button: TMouseButton;
Shift: TShiftState; X, Y: Integer);
begin
Memo1.Lines.Add('Panel1MouseDown')
end;
procedure TForm1.Panel1MouseUp(Sender: TObject; Button: TMouseButton;
Shift: TShiftState; X, Y: Integer);
begin
Memo1.Lines.Add('Panel1MouseUp')
end;
end.
You can derive your panel class to handle WM_NCHITTEST messages to return HTTRANSPARENT for the region you want the control beneath the panel receive mouse messages. E.g.:
procedure TMyPanel.WMNCHitTest(var Message: TWMNCHitTest);
var
Pt: TPoint;
begin
Pt := ScreenToClient(SmallPointToPoint(Message.Pos));
if (Pt.X < 80) and (Pt.Y < 60) then // devise your logic here...
Message.Result := HTTRANSPARENT
else
inherited;
end;
Obviously this is just a test, you can publish a field in your component for it to resolve where that control resides etc..