calling delphi procedure as method - delphi

Here is my simple code which compiles well, but raise Access Violation. It enters MD procedure and debugger displays some X and Y value, but after exiting procedure AV happens. Hope someone can help.
unit Unit1;
interface
uses
Windows, Messages, SysUtils, Variants, Classes, Controls, Forms, ExtCtrls;
type
TForm1 = class(TForm)
Panel1: TPanel;
procedure FormCreate(Sender: TObject);
end;
var
Form1: TForm1;
implementation
{$R *.dfm}
procedure MD(Sender: TObject; Button: TMouseButton; Shift: TShiftState; X, Y: Integer);
begin
form1.caption:= inttostr(x)+ ' '+ inttostr(y);
end;
procedure TForm1.FormCreate(Sender: TObject);
function MakeMethod(data, code: pointer): TMethod;
begin
result.Data:= data;
result.Code:= code;
end;
begin
panel1.OnMouseDown:= TMouseEvent(MakeMethod(nil, #MD));
end;
end.
Thanks

MD signature should include additional hidden parameter; it solves AV issue.
unit Unit1;
interface
uses
Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms,
Dialogs, ExtCtrls;
type
TForm1 = class(TForm)
Panel1: TPanel;
procedure FormCreate(Sender: TObject);
end;
var
Form1: TForm1;
implementation
{$R *.dfm}
procedure MD(Instance, Sender: TObject; Button: TMouseButton; Shift: TShiftState; X, Y: Integer);
begin
form1.caption:= inttostr(x)+ ' '+ inttostr(y);
end;
procedure TForm1.FormCreate(Sender: TObject);
function MakeMethod(data, code: pointer): TMethod;
begin
result.Data:= data;
result.Code:= code;
end;
begin
panel1.OnMouseDown:= TMouseEvent(MakeMethod(nil, #MD));
end;
end.

Try making MD a member of your form class. Edit your example the following way:
In the class definition:
type
TForm1 = class(TForm)
Panel1: TPanel;
procedure FormCreate(Sender: TObject);
procedure MD(Sender: TObject; Button: TMouseButton; Shift: TShiftState; X, Y: Integer);
end;
Later in your code make sure to enter the owner of the MD method like so:
procedure TForm1.MD(Sender: TObject; Button: TMouseButton; Shift: TShiftState; X, Y: Integer);
Finally, when assigning this eventhandler to your component, all you have to do is:
panel1.OnMouseDown:= MD;
I don't know what your intention was, but that is how you take care of eventhandlers at run time.

Related

delphi 7 - using NotXor penmode for resizing rectangles

I made an application that lets you create a rectangle and resize it however you want but it has a problem.
Whenever I want to draw a new rectangle on an existing one happens.
Here is the code for the application :
unit Unit1;
interface
uses
Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms,Dialogs, ExtCtrls, StdCtrls;
type
TForm1 = class(TForm)
Image1: TImage;
Button1: TButton;
procedure Image1MouseDown(Sender: TObject; Button: TMouseButton;
Shift: TShiftState; X, Y: Integer);
procedure Image1MouseMove(Sender: TObject; Shift: TShiftState; X,
Y: Integer);
procedure Button1Click(Sender: TObject);
procedure FormActivate(Sender: TObject);
private
{ Private declarations }
public
{ Public declarations }
end;
var
Form1: TForm1;
xstart,ystart,oldx,oldy,click1,lastx,lasty,copyrect_click:integer;
in_workspace,click_bol,copyrect_bol:boolean;
destrect,sourcerect:trect;
implementation
{$R *.dfm}
procedure TForm1.FormActivate(Sender: TObject);
begin
image1.Canvas.pen.Width:=10;
image1.Canvas.Pen.style:=psSolid;
end;
procedure TForm1.Button1Click(Sender: TObject);
begin
click_bol:=true;
end;
procedure TForm1.Image1MouseDown(Sender: TObject; Button: TMouseButton;
Shift: TShiftState; X, Y: Integer);
begin
if click_bol then
begin
xstart:=x;
ystart:=y;
oldx:=x;
oldy:=y;
image1.Canvas.pen.mode:=pmnotxor;
image1.canvas.rectangle(xstart,ystart,oldx,oldy);
click1:=click1+1;
in_workspace:=true;
if click1 mod 2=0 then
begin
image1.canvas.Pen.mode:=pmCopy;
image1.Canvas.Rectangle(xstart,ystart,x,y);
in_workspace:=false;
end;
end;
end;
procedure TForm1.Image1MouseMove(Sender: TObject; Shift: TShiftState; X,
Y: Integer);
begin
if (in_workspace=true) and (click_bol=true) then
begin
image1.canvas.Rectangle(xstart,ystart,oldx,oldy);
image1.canvas.Rectangle(xstart,ystart,x,y);
oldx:=x;
oldy:=y;
end;
end;
end.
I suspect it's because of the NotXor penmode and as you can see from the code I tried to change it to Copy penmode when it draws the actual rectagle but to no avail.
How can I improve this code in order not to have the rectangles change color then I draw them one on another?
Firstly I don't think that you really understand what you are doing and why it works (sort of). I think that you have dug up some code from somewhere and are trying to migrate it. All those global variables are a bad sign.
(You also have no mouse-up function - so what you have published will never produce the image you have shown.)
So, what does it do? Well, the canvas background is white and the pen is black (these are default values that you don't change) so if you XORed them the result would actually be white - it would appear to do nothing (unless you changed the pen colour to white), and the NotXOR makes it black. The original program I am guessing used XOR as that was more normal in older programs.
However, these days graphics have greatly improved, and, as David Heffernan says, better to just draw on the old background, and as you progress your program to using colours, this will become more important.
This shows the equivalent to what you are trying to achieve using this method.
Notice that there is much less code. I have removed the button click - it is just a waste of time. Better to put that in the mouse-down event. I have also not dealt with right mouse click etc. (but then, neither did you).
unit Unit10;
interface
uses
Vcl.Graphics, Vcl.Controls, Vcl.Forms, Vcl.StdCtrls, Vcl.ComCtrls,
Vcl.Grids, Vcl.Buttons, VCL.ExtCtrls,
System.Classes;
type
TForm1 = class(TForm)
Image1: TImage;
procedure FormActivate(Sender: TObject);
procedure Image1MouseDown(Sender: TObject; Button: TMouseButton;
Shift: TShiftState; X, Y: Integer);
procedure Image1MouseMove(Sender: TObject; Shift: TShiftState; X,
Y: Integer);
procedure Image1MouseUp(Sender: TObject; Button: TMouseButton;
Shift: TShiftState; X, Y: Integer);
private
{ Private declarations }
fSave : TPicture;
xstart,ystart:integer;
click_bol:boolean;
public
{ Public declarations }
end;
var
Form1: TForm1;
implementation
{$R *.dfm}
procedure TForm1.FormActivate(Sender: TObject);
begin
fSave := TPicture.Create;
image1.Canvas.pen.Width:=10;
image1.Canvas.Pen.style:=psSolid;
end;
procedure TForm1.Image1MouseDown(Sender: TObject; Button: TMouseButton;
Shift: TShiftState; X, Y: Integer);
begin
xstart:=x;
ystart:=y;
fSave.Assign( image1.Picture);
click_bol:=true;
end;
procedure TForm1.Image1MouseMove(Sender: TObject; Shift: TShiftState; X,
Y: Integer);
begin
if Click_bol then
begin
image1.Picture.Assign( fSave );
image1.Canvas.pen.Width:=10;
image1.Canvas.Pen.style:=psSolid;
image1.Canvas.Brush.Style := bsClear;
image1.Canvas.Rectangle(xstart,ystart,x,y);
end;
end;
procedure TForm1.Image1MouseUp(Sender: TObject; Button: TMouseButton;
Shift: TShiftState; X, Y: Integer);
begin
click_bol := FALSE;
end;
initialization
end.

How can I let a user move or drag a button on a form?

I have a form where I have created a button programmatically like this :
unit Unit1;
interface
uses
Winapi.Windows, Winapi.Messages, System.SysUtils, System.Variants,
System.Classes, Vcl.Graphics, Vcl.Controls, Vcl.Forms, Vcl.Dialogs,
Vcl.StdCtrls;
type
TForm1 = class(TForm)
Button1: TButton;
procedure Button1Click(Sender: TObject);
private
FTableButton : TButton;
public
{ Public declarations }
end;
var
Form1: TForm1;
implementation
{$R *.dfm}
procedure TForm1.Button1Click(Sender: TObject);
begin
if not Assigned(FTableButton) then begin
FTableButton := TButton.Create(self);
FTableButton.Parent := self;
end;
end;
end.
How can I let the user move FTableButton on the form by dragging it?
By implementing the OnMouseDown, OnMouseMove and OnMouseUp events of a control you can allow the user to move it like this :
unit Unit1;
interface
uses
Winapi.Windows, Winapi.Messages, System.SysUtils, System.Variants, System.Classes, Vcl.Graphics,
Vcl.Controls, Vcl.Forms, Vcl.Dialogs, Vcl.StdCtrls;
type
TForm1 = class(TForm)
Button1: TButton;
procedure Button1Click(Sender: TObject);
private
FTableButton : TButton;
FTableButtonDragging : boolean;
FMouseDownLocation : TPoint;
FButtonStartingLocation : TPoint;
procedure TableButtonMouseDown(Sender: TObject; Button: TMouseButton;
Shift: TShiftState; X, Y: Integer);
procedure TableButtonMouseMove(Sender: TObject; Shift: TShiftState; X,
Y: Integer);
procedure TableButtonMouseUp(Sender: TObject; Button: TMouseButton;
Shift: TShiftState; X, Y: Integer);
public
{ Public declarations }
end;
var
Form1: TForm1;
implementation
{$R *.dfm}
procedure TForm1.Button1Click(Sender: TObject);
begin
if not Assigned(FTableButton) then begin
FTableButton := TButton.Create(self);
FTableButton.Parent := self;
FTableButton.Caption := 'I am New';
FTableButton.OnMouseDown := TableButtonMouseDown;
FTableButton.OnMouseMove := TableButtonMouseMove;
FTableButton.OnMouseUp := TableButtonMouseUp;
end;
end;
procedure TForm1.TableButtonMouseDown(Sender: TObject; Button: TMouseButton;
Shift: TShiftState; X, Y: Integer);
begin
FTableButtonDragging := true;
FMouseDownLocation := Mouse.CursorPos;
FButtonStartingLocation := TPoint.Create(FTableButton.Left, FTableButton.Top);
end;
procedure TForm1.TableButtonMouseMove(Sender: TObject; Shift: TShiftState; X,
Y: Integer);
begin
if FTableButtonDragging then begin
FTableButton.Left := FButtonStartingLocation.X + (Mouse.CursorPos.X - FMouseDownLocation.X);
FTableButton.Top := FButtonStartingLocation.Y + (Mouse.CursorPos.Y - FMouseDownLocation.Y);
FTableButton.Invalidate;
end;
end;
procedure TForm1.TableButtonMouseUp(Sender: TObject; Button: TMouseButton;
Shift: TShiftState; X, Y: Integer);
begin
FTableButtonDragging := false;
end;
end.
Here we've added three new procedures to the form :
procedure TableButtonMouseDown(Sender: TObject; Button: TMouseButton;
Shift: TShiftState; X, Y: Integer);
procedure TableButtonMouseMove(Sender: TObject; Shift: TShiftState;
X, Y: Integer);
procedure TableButtonMouseUp(Sender: TObject; Button: TMouseButton;
Shift: TShiftState; X, Y: Integer);
and we have assigned these procedures as handlers for the new FTableButton's events :
FTableButton.OnMouseDown := TableButtonMouseDown;
FTableButton.OnMouseMove := TableButtonMouseMove;
FTableButton.OnMouseUp := TableButtonMouseUp;
When clicking on the button you need to store both the control's location and the mouse position when you placed the click, as well as that the mouse is currently down. Three new fields are used for this :
FTableButtonDragging : boolean;
FMouseDownLocation : TPoint;
FButtonStartingLocation : TPoint;
When moving the mouse, then, you can update the position of the control based on its original position and the difference between the current mouse position and the mouse position when the click was made.

How to write custom event that fires when DBGrid.SelectedRows.Count changes?

How to write a custom event that fires when DBGrid.SelectedRows.Count changes?
I need this events to conditionally show/hide a panel when the selected rows in a DBGrid are [zero | one] or more than one.
Since now I'm using the following code, but IMO coding a custom event is more appropriate here:
TForm3.DBGrid1DrawColumnCell(Sender: TObject; const Rect: TRect; Field: TField; State: DBGridDrawState);
begin
Panel1.Visible := TDBGrid(Sender).SelectedRows.Count > 1;
end;
To catch all events changing the internal Bookmarklist yoe will have to override
LinkActive
KeyDown
MouseDown
above example just as interposer class, could be changed to a new component.
unit Unit1;
interface
uses
Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms,
Dialogs, StdCtrls, Grids, DBGrids, DB, ADODB;
type
TDBGrid=Class(DBGrids.TDBGrid)
private
FOnSelectionChanged: TNotifyEvent;
procedure LinkActive(Value: Boolean); override;
procedure KeyDown(var Key: Word; Shift: TShiftState);override;
procedure MouseDown(Button: TMouseButton; Shift: TShiftState;
X, Y: Integer); override;
published
published
property OnSelectionChanged:TNotifyEvent read FOnSelectionChanged write FOnSelectionChanged;
End;
TForm1 = class(TForm)
ADODataSet1: TADODataSet;
DataSource1: TDataSource;
DBGrid1: TDBGrid;
Button1: TButton;
procedure MyOnSelectionChanged(Sender: TObject);
procedure FormCreate(Sender: TObject);
procedure Button1Click(Sender: TObject);
private
{ Private-Deklarationen }
public
{ Public-Deklarationen }
end;
var
Form1: TForm1;
implementation
uses unit3;
{$R *.dfm}
procedure TForm1.Button1Click(Sender: TObject);
begin
AdoDataset1.Active := Not AdoDataset1.Active;
end;
procedure TForm1.FormCreate(Sender: TObject);
begin
DBGrid1.OnSelectionChanged := MyOnSelectionChanged;
end;
procedure TForm1.MyOnSelectionChanged(Sender: TObject);
begin
Caption := IntToStr(TDBGrid(Sender).SelectedRows.Count);
end;
{ TDBGrid }
procedure TDBGrid.KeyDown(var Key: Word; Shift: TShiftState);
begin
inherited;
if Assigned(FOnSelectionChanged) then FOnSelectionChanged(self);
end;
procedure TDBGrid.LinkActive(Value: Boolean);
begin
inherited;
if Assigned(FOnSelectionChanged) then FOnSelectionChanged(self);
end;
procedure TDBGrid.MouseDown(Button: TMouseButton; Shift: TShiftState; X, Y: Integer);
begin
inherited;
if Assigned(FOnSelectionChanged) then FOnSelectionChanged(self);
end;
end.

Is it possible to 'clip' the lower part of TAreaSeries?

I would like to get some hints on working with TeeChart TAreaSeries, and specifically on creating NOT overlapping series.
When I create two Area series on the same plot, related to the same BottomAxis and LeftAxis I get something like this:
https://skydrive.live.com/redir?resid=9966BBBE2447AA89!116&authkey=!AKm6DMvrxleX5ps
And if I scroll the plot vertically I will see these two series expanding downwards endlessly to the negative infinity (Y coordinate).
But I wonder if it is possible to 'cut' the lower part of the series at some Y point?
So that I could retrieve something like this:
https://skydrive.live.com/redir?resid=9966BBBE2447AA89!115&authkey=!AGaejDREPKnPYMY
(Excuse me for the links instead of images, I don't have permission to post them due to the reputation restrictions)
Yes, you can do something as in the All Features\Welcome!\Axes\Opaque zones example at the new features demo, available at TeeChart's program group, for example:
unit Unit1;
interface
uses
Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms,
Dialogs, TeeGDIPlus, TeEngine, Series, ExtCtrls, TeeProcs, Chart;
type
TForm1 = class(TForm)
Chart1: TChart;
Series1: TAreaSeries;
Series2: TAreaSeries;
procedure FormCreate(Sender: TObject);
procedure Series1Click(Sender: TChartSeries; ValueIndex: Integer;
Button: TMouseButton; Shift: TShiftState; X, Y: Integer);
procedure Chart1MouseDown(Sender: TObject; Button: TMouseButton;
Shift: TShiftState; X, Y: Integer);
private
{ Private declarations }
ClipRect: TRect;
procedure SeriesBeforeDraw(Sender: TObject);
procedure SeriesAfterDraw(Sender: TObject);
public
{ Public declarations }
end;
var
Form1: TForm1;
implementation
{$R *.dfm}
uses TeCanvas;
procedure TForm1.FormCreate(Sender: TObject);
begin
Series1.BeforeDrawValues:=SeriesBeforeDraw;
Series1.AfterDrawValues:=SeriesAfterDraw;
end;
procedure TForm1.SeriesBeforeDraw(Sender: TObject);
Function SeriesRect(Series:TChartSeries):TRect;
begin
With result do
begin
Left:=Series.GetHorizAxis.IStartPos;
Right:=Series.GetHorizAxis.IEndPos;
Top:=Series.GetVertAxis.IStartPos;
Bottom:=Series.GetVertAxis.CalcYPosValue(700);
end;
end;
begin
ClipRect:=SeriesRect( Sender as TChartSeries );
{ make opaque }
With Chart1 do
if CanClip then
Canvas.ClipRectangle(ClipRect);
end;
procedure TForm1.SeriesAfterDraw(Sender: TObject);
begin
Chart1.Canvas.UnClipRectangle;
end;
procedure TForm1.Series1Click(Sender: TChartSeries; ValueIndex: Integer;
Button: TMouseButton; Shift: TShiftState; X, Y: Integer);
begin
Caption:=IntToStr(ValueIndex);
end;
procedure TForm1.Chart1MouseDown(Sender: TObject; Button: TMouseButton;
Shift: TShiftState; X, Y: Integer);
begin
if (Series1.Clicked(X,Y)<>-1) then
Chart1.CancelMouse:=not PointInRect(ClipRect,X,Y);
end;
end.
which produces this chart:

How To Use RxChar ComPort in another form

I have problem with delphi code... I have code:
MAIN FORM
unit MainForm;
interface
uses
Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms,
Dialogs, CPort, Menus, ComObj, StdCtrls;
type
TMainForm = class(TForm)
MainMenu1: TMainMenu;
Berkas1: TMenuItem;
Alat1: TMenuItem;
erminal1: TMenuItem;
ComPort1: TComPort;
Button1: TButton;
Memo1: TMemo;
Button2: TButton;
procedure erminal1Click(Sender: TObject);
procedure Button1Click(Sender: TObject);
procedure ComPort1RxChar(Sender: TObject; Count: Integer);
procedure Button2Click(Sender: TObject);
private
{ Private declarations }
public
{ Public declarations }
end;
var
MainForm: TMainForm;
implementation
uses
ChildForm;
{$R *.dfm}
procedure TMainForm.erminal1Click(Sender: TObject);
var
ChildForm: TChildForm;
begin
ChildForm := TChildForm.Create(Application);
ChildForm.Show;
end;
procedure TMainForm.Button1Click(Sender: TObject);
begin
ComPort1.ShowSetupDialog;
end;
procedure TMainForm.ComPort1RxChar(Sender: TObject; Count: Integer);
var
ComPort: TComPort;
data: string;
begin
inherited;
ComPort := TComPort.Create(Self);
ComPort1.ReadStr(data, 5);
ChildForm.Memo1.Text := ChildForm.Memo1.Text+''+data+'';
end;
end.
CHILD FORM:
unit ChildForm;
interface
uses
Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms,
Dialogs, StdCtrls, ComObj;
type
TChildForm = class(TForm)
Memo1: TMemo;
Button1: TButton;
Button2: TButton;
Button3: TButton;
procedure FormClose(Sender: TObject; var Action: TCloseAction);
procedure Button1Click(Sender: TObject);
private
{ Private declarations }
public
{ Public declarations }
end;
var
ChildForm: TChildForm;
implementation
uses
MainForm;
{$R *.dfm}
procedure TChildForm.FormClose(Sender: TObject; var Action: TCloseAction);
begin
Action := caFree;
end;
procedure TChildForm.Button1Click(Sender: TObject);
begin
MainForm.ComPort1.Open;
end;
end.
I want to show data input from my device to memo in child form. I put the comport component in main form. But when I run the program, it says:
Project Data.exe raised exception class EAccessViolation with message 'Access violation at address 00466051 in module 'Data.exe'. Read of address 000002F8'. Process stopped. Use Step or Run to continue.
How can i solve the problem?
There are many problems with your code as mentioned in the comments.
To make a better implementation of your parent/child form interaction with the comport component,
do as follows:
Create a TDataModule (ex: DataModule1), put the comport component there.
Now you can access the comport component from the main form and the child form.
Add a private method to your child form:
procedure TChildForm.ComPort1RxChar(Sender: TObject; Count: Integer);
var
data: string;
begin
DataModule1.ComPort1.ReadStr(data, 5);
Self.Memo1.Text := Self.Memo1.Text+''+data+'';
end;
When you open the comport in the child form, set the comport OnRxChar event to your TChildForm.ComPort1RxChar method.
In the TChildForm.OnClose event, set the comport OnRxChar event to nil and close the comport.

Resources