Error when you change the order of columns in Dbgrid - delphi

I wrote the code for the form rendering settings database table Dbgrid in another form. It depends on the selected items are defined Chetsklistboks visible columns in Dbgrid. I also wrote the code to move with drag & drop items and columns Chetsklistbox Dbgrid conformity. But start at some point (and in my opinion with an attempt to change the item with the lowest index in the item with a large index) errors when drag & drop from the Argument out of range and when you close the Invalid Pointer Operation. Help resolve the error.
unit SettingOfShowData;
interface
uses
Winapi.Windows, Winapi.Messages, System.SysUtils, System.Variants, System.Classes, Vcl.Graphics,
Vcl.Controls, Vcl.Forms, Vcl.Dialogs, Vcl.StdCtrls, Vcl.CheckLst, Vcl.ExtCtrls,
VirtualTrees, DatabaseClasses, MainForm, ListOfTables;
type
TNodeField=record
NameField : string;
end;
PNodeField=^TNodeField;
type
TfmSettings = class(TForm)
Panel1: TPanel;
VT: TVirtualStringTree;
Edit1: TEdit;
Edit2: TEdit;
Edit3: TEdit;
CheckListBox1: TCheckListBox;
procedure VTGetNodeDataSize(Sender: TBaseVirtualTree;
var NodeDataSize: Integer);
procedure VTGetText(Sender: TBaseVirtualTree; Node: PVirtualNode;
Column: TColumnIndex; TextType: TVSTTextType; var CellText: string);
procedure FormCreate(Sender: TObject);
procedure VTFreeNode(Sender: TBaseVirtualTree; Node: PVirtualNode);
procedure FormActivate(Sender: TObject);
function IsPrimaryKey(InputTableName : string; InputFieldName : string) : Boolean;
procedure VTNodeClick(Sender: TBaseVirtualTree; const HitInfo: THitInfo);
procedure VTNodeDblClick(Sender: TBaseVirtualTree; const HitInfo: THitInfo);
procedure FormClose(Sender: TObject; var Action: TCloseAction);
procedure CheckListBox1ClickCheck(Sender: TObject);
procedure CheckListBox1DragDrop(Sender, Source: TObject; X, Y: Integer);
procedure CheckListBox1DragOver(Sender, Source: TObject; X, Y: Integer;
State: TDragState; var Accept: Boolean);
procedure CheckListBox1MouseDown(Sender: TObject; Button: TMouseButton;
Shift: TShiftState; X, Y: Integer);
private
{ Private declarations }
public
{ Public declarations }
end;
var
fmSettings: TfmSettings;
NumX, NumY : Integer;
implementation
{$R *.dfm}
procedure TfmSettings.CheckListBox1ClickCheck(Sender: TObject);
begin
fmShowData.DBGrid1.Columns[CheckListBox1.ItemIndex].Visible :=
not(fmShowData.DBGrid1.Columns[CheckListBox1.ItemIndex].Visible);
end;
procedure TfmSettings.CheckListBox1DragDrop(Sender, Source: TObject; X,
Y: Integer);
var
Num1, Num2, temp: Integer;
Point1, Point2: TPoint;
begin
Point1.X:=NumX;
Point1.Y:=NumY;
Point2.X:=X;
Point2.Y:=Y;
with Source as TCheckListBox do
begin
Num2:=CheckListBox1.ItemAtPos(Point1,True);
Num1:=CheckListBox1.ItemAtPos(Point2,True);
CheckListBox1.Items.Move(Num2, Num1);
if Num2>Num1 then
begin
temp:=Num2;
Num2:=Num1;
Num1:=temp;
end;
fmShowData.DBGrid1.Columns[Num1].Index:=Num2;
fmShowData.DBGrid1.Columns[Num2+1].Index:=Num1;
end;
end;
procedure TfmSettings.CheckListBox1DragOver(Sender, Source: TObject; X,
Y: Integer; State: TDragState; var Accept: Boolean);
begin
if Source=CheckListBox1 then Accept:=True;
end;
procedure TfmSettings.CheckListBox1MouseDown(Sender: TObject;
Button: TMouseButton; Shift: TShiftState; X, Y: Integer);
begin
NumY:=Y;
NumX:=X;
end;
procedure TfmSettings.FormActivate(Sender: TObject);
var
Index, i: Integer;
VTNodeField : PNodeField;
begin
VT.BeginUpdate();
//TableSpec:=TTableSpec(DBSchema.Tables.FindComponent(fmListOfTables.DisplayTable));
for Index := 0 to TTableSpec(DBSchema.Tables.FindComponent(fmListOfTables.DisplayTable)).Fields.ComponentCount-1 do
begin
if TTableSpec(DBSchema.Tables.FindComponent(fmListOfTables.DisplayTable)).Fields.Components[Index] is TFieldSpec then
begin
VTNodeField:=VT.GetNodeData(VT.AddChild(nil, nil));
VTNodeField^.NameField:=(TTableSpec(DBSchema.Tables.FindComponent(fmListOfTables.DisplayTable)).Fields.Components[Index] as TFieldSpec).name;
end;
end;
VT.EndUpdate();
for i := 0 to TTableSpec(DBSchema.Tables.FindComponent(fmListOfTables.DisplayTable)).Fields.ComponentCount-1 do
begin
if (TTableSpec(DBSchema.Tables.FindComponent(fmListOfTables.DisplayTable)).Fields.Components[i] is TFieldSpec) then
begin
CheckListBox1.Items.Add(TTableSpec(DBSchema.Tables.FindComponent(fmListOfTables.DisplayTable)).Fields.Components[i].Name);
CheckListBox1.Checked[i]:=true;
end;
end;
end;
procedure TfmSettings.FormClose(Sender: TObject; var Action: TCloseAction);
var i: integer;
begin
{ while VT.ComponentCount>0 do
begin
VT.DeleteNode(VT.Nodes.GetEnumerator.Current);
VT.DeleteChildren(VT.Nodes.GetEnumerator.Current);
VT.Nodes.GetEnumerator.MoveNext();
end; }
VT.Clear();
//fmTableData.DBGrid1.Columns
CheckListBox1.Clear;
end;
procedure TfmSettings.FormCreate(Sender: TObject);
var
Index: Integer;
VTNodeField : PNodeField;
//TableSpec : TTableSpec;
begin
{ VT.BeginUpdate();
//TableSpec:=TTableSpec(DBSchema.Tables.FindComponent(fmListOfTables.DisplayTable));
for Index := 0 to TTableSpec(DBSchema.Tables.FindComponent(fmListOfTables.DisplayTable)).ComponentCount-1 do
begin
if TTableSpec(DBSchema.Tables.FindComponent(fmListOfTables.DisplayTable)).Components[Index] is TFieldSpec then
begin
VTNodeField:=VT.GetNodeData(VT.AddChild(nil, nil));
VTNodeField.NameField:=(TTableSpec(DBSchema.Tables.FindComponent(fmListOfTables.DisplayTable)).Components[Index] as TFieldSpec).FieldName;
end;
end;
VT.EndUpdate();}
end;
procedure TfmSettings.VTFreeNode(Sender: TBaseVirtualTree; Node: PVirtualNode);
var
Data: PNodeField;
begin
Data:=Sender.GetNodeData(Node);
if Assigned(Data) then
begin
Finalize(Data^);
end;
end;
procedure TfmSettings.VTGetNodeDataSize(Sender: TBaseVirtualTree;
var NodeDataSize: Integer);
begin
NodeDataSize:=SizeOf(TNodeField);
end;
procedure TfmSettings.VTGetText(Sender: TBaseVirtualTree; Node: PVirtualNode;
Column: TColumnIndex; TextType: TVSTTextType; var CellText: string);
var
Index: Integer;
VTNodeField : PNodeField;
begin
VTNodeField:=Sender.GetNodeData(Node);
for Index := 0 to DBSchema.Tables.FindComponent(fmListOfTables.DisplayTable).ComponentCount-1 do
begin
if DBSchema.Tables.FindComponent(fmListOfTables.DisplayTable).Components[Index] is TFieldSpec then
begin
VTNodeField:=Sender.GetNodeData(Sender.AddChild(nil, nil));
VTNodeField^.NameField:=(DBSchema.Tables.FindComponent(fmListOfTables.DisplayTable).Components[Index] as TFieldSpec).name;
end;
end;
CellText:=VTNodeField^.NameField;
end;
procedure TfmSettings.VTNodeClick(Sender: TBaseVirtualTree;
const HitInfo: THitInfo);
var
NewVTNodeField : PNodeField;
NewNode : PVirtualNode;
begin
{NewNode:=VT.AddChild(VT.FocusedNode);
NewVTNodeField:=VT.GetNodeData(NewNode); }
end;
procedure TfmSettings.VTNodeDblClick(Sender: TBaseVirtualTree;
const HitInfo: THitInfo);
var
NewVTNodeField, CurrentNode: PNodeField;
NewNode : PVirtualNode;
//CurrentNode : PDataNode;
i, j : integer;
begin
{ NewNode:=VT.AddChild(VT.FocusedNode);
NewVTNodeField:=VT.GetNodeData(NewNode); }
CurrentNode:=VT.GetNodeData(VT.FocusedNode);
if IsPrimaryKey(fmListOfTables.DisplayTable, {VT.Text[VT.FocusedNode, 0]} CurrentNode^.NameField) then
begin
for i:= 0 to DBSchema.Tables.ComponentCount-1 do
for j:=0 to TTableSpec(DBSchema.Tables.Components[i]).Constraints.ComponentCount-1 do
begin
if (TConstraintSpec(TTableSpec(DBSchema.Tables.Components[i]).Constraints.Components[j]).Reference=fmListOfTables.DisplayTable) then
begin
NewNode:=VT.AddChild(VT.FocusedNode);
NewVTNodeField:=VT.GetNodeData(NewNode);
NewVTNodeField^.NameField:=(TTableSpec(DBSchema.Tables.Components[i])).Name;
end;
end;
end;
end;
function TfmSettings.IsPrimaryKey(InputTableName : string; InputFieldName: string):Boolean;
var
i : integer;
flag: boolean;
begin
flag:=False;
for i:=0 to TTableSpec(DBSchema.Tables.FindComponent(InputTableName)).Constraints.ComponentCount-1 do
begin
if ((TConstraintSpec(TTableSpec(DBSchema.Tables.FindComponent(InputTableName)).Constraints.Components[i]).ConstraintType='PRIMARY') and (TFieldSpec(TConstraintDetailSpec(TConstraintSpec(TTableSpec(DBSchema.Tables.FindComponent(InputTableName)).Constraints.Components[i]).DetailList).FieldSpec).FieldName=InputFieldName){(TTableSpec(DBSchema.Tables.FindComponent(InputTableName)).Fields.FindComponent(InputFieldName).Name=TConstraintDetailSpec(TConstraintSpec(TTableSpec(DBSchema.Tables.FindComponent(InputTableName)).Constraints.Components[i]).DetailList).FieldName)}) then
flag:=True;
Edit1.Text:=TConstraintSpec(TTableSpec(DBSchema.Tables.FindComponent(InputTableName)).Constraints.Components[i]).Name;
Edit2.Text:=AnsiToUtf8(TFieldSpec(TConstraintDetailSpec(TConstraintSpec(TTableSpec(DBSchema.Tables.FindComponent(InputTableName)).Constraints.Components[i]).DetailList).FieldSpec).FieldName);
Edit3.Text:=InputFieldName;
end;
Result:=flag;
end;
end.
Select the code of procedure that changes the order of the columns
procedure TfmSettings.CheckListBox1DragDrop(Sender, Source: TObject; X,
Y: Integer);
var
Num1, Num2, temp: Integer;
Point1, Point2: TPoint;
begin
Point1.X:=NumX;
Point1.Y:=NumY;
Point2.X:=X;
Point2.Y:=Y;
with Source as TCheckListBox do
begin
Num2:=CheckListBox1.ItemAtPos(Point1,True);
Num1:=CheckListBox1.ItemAtPos(Point2,True);
CheckListBox1.Items.Move(Num2, Num1);
if Num2>Num1 then
begin
temp:=Num2;
Num2:=Num1;
Num1:=temp;
end;
fmShowData.DBGrid1.Columns[Num1].Index:=Num2;
fmShowData.DBGrid1.Columns[Num2+1].Index:=Num1;
end;
end;

If then checklistbox is a representation of the fields the corresponding Action for
CheckListBox1.Items.Move(Num2, Num1);
would be
DBGrid1.Columns[num2].Index := DBGrid1.Columns[num1].Index;
nothing more or less.
// Removed comment thx to jachguate

Related

Access violation after TStringEditLink get destroyed (TVirtualStringTree) - Lazarus example

I've try to implement an editor for a VirtualStringTree based on example of Lazarius
Can you tell me why did I get an Access Violation after TStringEditLink gets destroyed?
It's weired that error appear only when i press ESCAPE or ENTER. If i click from one cell to another there is no error.
Like an observation, I sow that if I remove the FEdit.Free code from destructor TStringEditLink.Destroy the error disappear.
Do you have a solution for this?
Bellow the full code:
unit Unit2;
interface
uses
Winapi.Windows, Winapi.Messages, System.SysUtils, System.Variants, System.Classes, Vcl.Graphics,
Vcl.Controls, Vcl.Forms, Vcl.Dialogs, VirtualTrees, EditorLink, Vcl.StdCtrls,
Vcl.ExtCtrls, Vcl.Imaging.jpeg;
type
TTreeData = record
Fields: array of String;
end;
PTreeData = ^TTreeData;
const
SizeVirtualTree = SizeOf(TTreeData);
type
TForm2 = class(TForm)
VirtualTree: TVirtualStringTree;
procedure FormCreate(Sender: TObject);
procedure VirtualTreeClick(Sender: TObject);
procedure VirtualTreeCreateEditor(Sender: TBaseVirtualTree;
Node: PVirtualNode; Column: TColumnIndex; out EditLink: IVTEditLink);
procedure VirtualTreeEditing(Sender: TBaseVirtualTree; Node: PVirtualNode;
Column: TColumnIndex; var Allowed: Boolean);
procedure VirtualTreeFreeNode(Sender: TBaseVirtualTree; Node: PVirtualNode);
procedure VirtualTreeGetNodeDataSize(Sender: TBaseVirtualTree;
var NodeDataSize: Integer);
procedure VirtualTreeGetText(Sender: TBaseVirtualTree; Node: PVirtualNode;
Column: TColumnIndex; TextType: TVSTTextType; var CellText: string);
procedure VirtualTreeNewText(Sender: TBaseVirtualTree; Node: PVirtualNode;
Column: TColumnIndex; NewText: string);
private
{ Private declarations }
public
{ Public declarations }
end;
var
Form2: TForm2;
implementation
{$R *.dfm}
procedure TForm2.FormCreate(Sender: TObject);
var
Node: PVirtualNode;
LTreeData: PTreeData;
begin
VirtualTree.Clear;
VirtualTree.BeginUpdate;
//node 1
Node:= VirtualTree.AddChild(nil,nil);
VirtualTree.ValidateNode(Node,False);
LTreeData:= VirtualTree.GetNodeData(Node);
SetLength(LTreeData^.Fields,3);
LTreeData^.Fields[0]:= 'John';
LTreeData^.Fields[1]:= '2500';
LTreeData^.Fields[2]:= 'Production';
//node 2
Node:= VirtualTree.AddChild(nil,nil);
VirtualTree.ValidateNode(Node,False);
LTreeData:= VirtualTree.GetNodeData(Node);
SetLength(LTreeData^.Fields,3);
LTreeData^.Fields[0]:= 'Mary';
LTreeData^.Fields[1]:= '2100';
LTreeData^.Fields[2]:= 'HR';
VirtualTree.EndUpdate;
end;
procedure TForm2.VirtualTreeClick(Sender: TObject);
var
VT: TVirtualStringTree;
Click: THitInfo;
begin
VT:= Sender as TVirtualStringTree;
VT.GetHitTestInfoAt(Mouse.CursorPos.X-VT.ClientOrigin.X, Mouse.CursorPos.Y-VT.ClientOrigin.Y, True, Click);
VT.EditNode(Click.HitNode,Click.HitColumn);
end;
procedure TForm2.VirtualTreeCreateEditor(Sender: TBaseVirtualTree;
Node: PVirtualNode; Column: TColumnIndex; out EditLink: IVTEditLink);
begin
EditLink := TStringEditLink.Create;
end;
procedure TForm2.VirtualTreeEditing(Sender: TBaseVirtualTree;
Node: PVirtualNode; Column: TColumnIndex; var Allowed: Boolean);
begin
Allowed:= True;
end;
procedure TForm2.VirtualTreeFreeNode(Sender: TBaseVirtualTree;
Node: PVirtualNode);
var
LTreeData: PTreeData;
begin
LTreeData:= Sender.GetNodeData(Node);
Finalize(LTreeData^);
end;
procedure TForm2.VirtualTreeGetNodeDataSize(Sender: TBaseVirtualTree;
var NodeDataSize: Integer);
begin
NodeDataSize:= SizeVirtualTree;
end;
procedure TForm2.VirtualTreeGetText(Sender: TBaseVirtualTree;
Node: PVirtualNode; Column: TColumnIndex; TextType: TVSTTextType;
var CellText: string);
var
LTreeData: PTreeData;
begin
if Assigned(Node) and (Column > NoColumn) then
begin
LTreeData:= Sender.GetNodeData(Node);
CellText:= LTreeData^.Fields[Column];
end;
end;
procedure TForm2.VirtualTreeNewText(Sender: TBaseVirtualTree;
Node: PVirtualNode; Column: TColumnIndex; NewText: string);
var
LTreeData: PTreeData;
begin
LTreeData:= Sender.GetNodeData(Node);
LTreeData^.Fields[Column]:= NewText;
end;
end.
and the EditorLink unit
unit EditorLink;
interface
uses
Classes, SysUtils, Forms, Controls, Graphics, Dialogs,
VirtualTrees, Messages, Windows, StdCtrls, Vcl.ExtCtrls;
type
TStringEditLink = class(TInterfacedObject, IVTEditLink)
private
FEdit: TWinControl;
FTree: TVirtualStringTree;
FNode: PVirtualNode;
FColumn: Integer;
FStopping: Boolean;
protected
procedure EditKeyDown(Sender: TObject; var Key: Word; Shift: TShiftState);
public
destructor Destroy; override;
function BeginEdit: Boolean; stdcall;
function CancelEdit: Boolean; stdcall;
function EndEdit: Boolean; stdcall;
function GetBounds: TRect; stdcall;
function PrepareEdit(Tree: TBaseVirtualTree; Node: PVirtualNode; Column: TColumnIndex): Boolean; stdcall;
procedure ProcessMessage(var Message: TMessage); stdcall;
procedure SetBounds(R: TRect); stdcall;
end;
implementation
uses unit2;
destructor TStringEditLink.Destroy;
begin
FEdit.Free; //--> seems that due to this I get the access violation
inherited;
end;
procedure TStringEditLink.EditKeyDown(Sender: TObject; var Key: Word; Shift: TShiftState);
begin
case Key of
VK_ESCAPE:
begin
FTree.CancelEditNode;
Key := 0;
FTree.setfocus;
end;
VK_RETURN:
begin
PostMessage(FTree.Handle, WM_KEYDOWN, VK_DOWN, 0);
Key := 0;
FTree.EndEditNode;
FTree.setfocus;
end;
end; //case
end;
function TStringEditLink.BeginEdit: Boolean;
begin
Result := not FStopping;
if Result then
begin
FEdit.Show;
FEdit.SetFocus;
end;
end;
function TStringEditLink.CancelEdit: Boolean;
begin
Result := True;
FEdit.Hide;
end;
function TStringEditLink.EndEdit: Boolean;
var
s: String;
begin
Result := True;
s := TComboBox(FEdit).Text;
FTree.Text[FNode, FColumn] := s;
FTree.InvalidateNode(FNode);
FEdit.Hide;
FTree.SetFocus;
end;
function TStringEditLink.GetBounds: TRect;
begin
Result := FEdit.BoundsRect;
end;
function TStringEditLink.PrepareEdit(Tree: TBaseVirtualTree; Node: PVirtualNode; Column: TColumnIndex): Boolean;
var
FCellText: String;
FCellTextBounds: TRect;
FCellFont: TFont;
begin
Result := True;
FTree := Tree as TVirtualStringTree;
FNode := Node;
FColumn := Column;
FCellFont:= TFont.Create;
FTree.GetTextInfo(FNode, FColumn, FCellFont, FCellTextBounds, FCellText);
FEdit := TComboBox.Create(nil);
with FEdit as TComboBox do
begin
Visible := False;
Parent := Tree;
Items.Add('Google');
Items.Add('Yahoo');
Items.Add('Altavista');
OnKeyDown := EditKeyDown;
Text:= FCellText;
end;
end;
procedure TStringEditLink.ProcessMessage(var Message: TMessage);
begin
FEdit.WindowProc(Message);
end;
procedure TStringEditLink.SetBounds(R: TRect);
var
Dummy: Integer;
begin
FTree.Header.Columns.GetColumnBounds(FColumn, Dummy, R.Right);
FEdit.BoundsRect := R;
end;
end.
I have no Lazarus but it seems to behave the same on XE4.
In my VST installation, located in ./VirtualTreeviewV5.3.0/Demos/Advanced there is an Editors.pas file where I've found the destructor below. Notice the comment casues issue #357:
destructor TPropertyEditLink.Destroy;
begin
//FEdit.Free; casues issue #357. Fix:
if FEdit.HandleAllocated then
PostMessage(FEdit.Handle, CM_RELEASE, 0, 0);
inherited;
end;
Moreover, FEdit.Free is performed in the PrepareEdit method before its fresh creation:
function TStringEditLink.PrepareEdit(Tree: TBaseVirtualTree; Node: PVirtualNode; Column: TColumnIndex): Boolean;
var
FCellText: String;
FCellTextBounds: TRect;
FCellFont: TFont;
begin
Result := True;
FTree := Tree as TVirtualStringTree;
FNode := Node;
FColumn := Column;
FEdit.Free;
FEdit := nil;
FCellFont:= TFont.Create;
FTree.GetTextInfo(FNode, FColumn, FCellFont, FCellTextBounds, FCellText);
FEdit := TComboBox.Create(nil);
with FEdit as TComboBox do
. . .
This solves the VK_ESC and the VK_RETURN issues on my XE4 and XE7 installation.
The issue #357 seems to have not been fixed yet: see - Used fix proposed in issue #361 to fix issue #357 (AV in advanced demo - PropertiesDemo form in XE3+). I've found no evidence of the #361 fix.
Another issue happens to me when clicking on a unassigned node after an edit operation.
Checking if the Click.HitNode is not nil before start editing solves the above.
procedure TForm2.VirtualTreeClick(Sender: TObject);
var
VT: TVirtualStringTree;
Click: THitInfo;
begin
VT:= Sender as TVirtualStringTree;
VT.GetHitTestInfoAt(Mouse.CursorPos.X-VT.ClientOrigin.X, Mouse.CursorPos.Y-VT.ClientOrigin.Y, True, Click);
if Assigned(Click.HitNode) then
VT.EditNode(Click.HitNode,Click.HitColumn);
end;
Notice also you have a circular reference in the EditorLink unit:
uses Unit2;
This pseudo stack trace of your code illustrates the issue:
FEdit.EditKeyDown()
-- calls --
FTree.EndEditNode() { or FTree.CancelEditNode }
-- which calls --
TStringEditLink.Destroy()
-- which calls --
FEdit.Free()
The code in the event handler for FEdit.EditKeyDown() frees FEdit before the key down event handler code finishes running. Thus the access violation error.
We handled this by setting up a signal mechanism so the TStringEditLink could signal the main form when it was done, and the main form could run the code to destroy the TStringEditLink (since it is the one that created the TStringEditLink in the first place). We added a TTimer to the main form, and a property to receive the signal. The TTimer watches the property. The TStringEditLink component has a pointer to the form, so it can set the property.
unit Unit1;
interface
uses
Winapi.Windows, Winapi.Messages, System.SysUtils, System.Variants, System.Classes, Vcl.Graphics,
Vcl.Controls, Vcl.Forms, Vcl.Dialogs, Vcl.ExtCtrls, VirtualTrees;
type
TEditorAction = (eaCancel, eaAccept, eaNotSet);
TForm1 = class(TForm)
vstTree: TVirtualStringTree;
procedure vstTreeCreateEditor(Sender: TBaseVirtualTree; Node: PVirtualNode; Column: TColumnIndex; out EditLink: IVTEditLink);
procedure DoWatchTreeEditorTimer(Sender: TObject);
procedure FormCreate(Sender: TObject);
procedure FormDestroy(Sender: TObject);
private
FEndEditTimer: TTimer;
FEditorAction: TEditorAction;
procedure SetEditorAction(const Value: TEditorAction);
public
property EditorAction: TEditorAction read FEditorAction write SetEditorAction;
end;
TPropertyEdit = class(TInterfacedObject, IVTEditLink)
procedure EditKeyDown(Sender: TObject; var Key: Word; Shift: TShiftState);
private
FEdit: TWinControl;
FTree: TVirtualStringTree;
FNode: PVirtualNode;
FColumn: Integer;
public
FForm: TForm1;
destructor Destroy; override;
function BeginEdit: Boolean; stdcall;
function CancelEdit: Boolean; stdcall;
function EndEdit: Boolean; stdcall;
function GetBounds: TRect; stdcall;
function PrepareEdit(Tree: TBaseVirtualTree; Node: PVirtualNode; Column: TColumnIndex): Boolean; stdcall;
procedure ProcessMessage(var Message: TMessage); stdcall;
procedure SetBounds(R: TRect); stdcall;
end;
var
Form1: TForm1;
implementation
{$R *.dfm}
{ TForm1 }
procedure TForm1.FormCreate(Sender: TObject);
begin
FEndEditTimer := TTimer.Create(nil);
FEndEditTimer.Enabled := False;
FEndEditTimer.Interval := 100;
FEndEditTimer.OnTimer := DoWatchTreeEditorTimer;
end;
procedure TForm1.FormDestroy(Sender: TObject);
begin
FreeAndNil(FEndEditTimer);
end;
procedure TForm1.vstTreeCreateEditor(Sender: TBaseVirtualTree; Node: PVirtualNode; Column: TColumnIndex; out EditLink: IVTEditLink);
begin
EditLink := TPropertyEdit.Create;
TPropertyEdit(EditLink).FForm := Self; { lets us signal the form when the editor needs to be destroyed }
FEditorAction := eaNotSet;
end;
procedure TForm1.SetEditorAction(const Value: TEditorAction);
begin
if FEditorAction <> Value then
begin
FEditorAction := Value;
FEndEditTimer.Enabled := True;
end;
end;
procedure TForm1.DoWatchTreeEditorTimer(Sender: TObject);
begin
FEndEditTimer.Enabled := False;
Application.ProcessMessages;
case FEditorAction of
eaCancel:
begin
vstTree.CancelEditNode;
vstTree.SetFocus;
end;
eaAccept:
begin
vstTree.EndEditNode;
vstTree.SetFocus;
end;
end;
end;
{ TPropertyEdit }
function TPropertyEdit.BeginEdit: Boolean;
begin
Result := True;
FEdit.Show;
end;
function TPropertyEdit.CancelEdit: Boolean;
begin
Result := True;
FEdit.Hide;
FForm.FEditorAction := eaCancel;
end;
destructor TPropertyEdit.Destroy;
begin
if FEdit <> nil then
FreeAndNil(FEdit);
inherited;
end;
procedure TPropertyEdit.EditKeyDown(Sender: TObject; var Key: Word; Shift: TShiftState);
begin
case Key of
VK_ESCAPE:
begin
Key := Word(0); { some versions of Delphi throw random A/V errors if '0' is not cast as a Word() }
FForm.EditorAction := eaCancel;
end;
VK_RETURN:
begin
Key := Word(0); { some versions of Delphi throw random A/V errors if '0' is not cast as a Word() }
FForm.EditorAction := eaAccept
end;
end;
end;
function TPropertyEdit.EndEdit: Boolean;
begin
Result := True;
{ Do something with the value provided by the user }
FEdit.Hide;
FForm.EditorAction := eaAccept;
end;
function TPropertyEdit.GetBounds: TRect;
begin
Result := FEdit.BoundsRect;
end;
function TPropertyEdit.PrepareEdit(Tree: TBaseVirtualTree; Node: PVirtualNode; Column: TColumnIndex): Boolean;
begin
Result := True;
FTree := Tree as TVirtualStringTree;
FNode := Node;
FColumn := Column;
{ Setup the editor for user }
FEdit := TSomeWinControl.Create(nil);
FEdit.Properties := Values;
{ Capture keystrokes }
FEdit.OnKeyDown := EditKeyDown;
end;
procedure TPropertyEdit.ProcessMessage(var Message: TMessage);
begin
FEdit.WindowProc(Message);
end;
procedure TPropertyEdit.SetBounds(R: TRect);
var
Dummy: Integer;
begin
FTree.Header.Columns.GetColumnBounds(FColumn, Dummy, R.Right);
FEdit.BoundsRect := R;
end;
end.
Our code does a lot of additional things, so the above code is a copy/paste of the essential parts to demonstrate how to overcome the race condition. It is untested, but should get you pointed in the right direction.
One solution is also to free the previously created controls.
function TStringEditLink.PrepareEdit(Tree: TBaseVirtualTree; Node: PVirtualNode; Column: TColumnIndex): Boolean;
var
FCellText: String;
FCellTextBounds: TRect;
FCellFont: TFont;
i: Integer;
Item: TControl;
begin
Result := True;
FTree := Tree as TVirtualStringTree;
FNode := Node;
FColumn := Column;
FCellFont:= TFont.Create;
FTree.GetTextInfo(FNode, FColumn, FCellFont, FCellTextBounds, FCellText);
//----->> free previuous created control <<----------
for i := (FTree.ControlCount - 1) downto 0 do
begin
Item := FTree.controls[i];
if assigned(item) then
begin
if item is TComboBox then FreeAndNil(item);
end;
end;
//---------------------------------------------------
FEdit := TComboBox.Create(nil);
with FEdit as TComboBox do
begin
Visible := False;
Parent := Tree;
Items.Add('Google');
Items.Add('Yahoo');
Items.Add('Altavista');
OnKeyDown := EditKeyDown;
Text:= FCellText;
end;
end;
The solution I used at the end is listed bellow:
TBasePanel = class(TPanel)
private
procedure CMRelease(var Message: TMessage); message CM_RELEASE;
protected
public
procedure Release; virtual;
end;
TStringEditLink = class(TInterfacedObject, IVTEditLink)
private
FBasePanel: TBasePanel;
...
protected
procedure EditKeyDown(Sender: TObject; var Key: Word; Shift: TShiftState);
public
destructor Destroy; override;
function BeginEdit: Boolean; stdcall;
function CancelEdit: Boolean; stdcall;
function EndEdit: Boolean; stdcall;
function GetBounds: TRect; stdcall;
function PrepareEdit(Tree: TBaseVirtualTree; Node: PVirtualNode; Column: TColumnIndex): Boolean; stdcall;
procedure ProcessMessage(var Message: TMessage); stdcall;
procedure SetBounds(R: TRect); stdcall;
end;
implementation
procedure TBasePanel.CMRelease(var Message: TMessage);
begin
Free;
end;
procedure TBasePanel.Release;
begin
if HandleAllocated then
PostMessage(Handle, CM_RELEASE, 0, 0);
end;
destructor TStringEditLink.Destroy;
begin
if Assigned(FBasePanel) then FBasePanel.Release;
inherited;
end;
FBasePanel should be used as owner and as parent for as many component editors would like to be displayed in the same time.
In HeidiSql source code there is a good example to avoid this error.
The code a little changed is:
procedure TBaseEditorLink.TempWindowProc(var Message: TMessage);
begin
case Message.Msg of
WM_CHAR: //Catch hotkeys
if not (TWMChar(Message).CharCode = VK_TAB) then FOldWindowProc(Message);
WM_GETDLGCODE: //"WantTabs" mode for main control
Message.Result := Message.Result or DLGC_WANTARROWS or DLGC_WANTALLKEYS or DLGC_WANTTAB;
else
begin
try
FOldWindowProc(Message);
except
on E : EAccessViolation do; //EAccessViolation occurring in some cases
on E : Exception do raise;
end;
end;
end;
end;

set color selected line cell in TStringGrid with space key

i have report in TStringGrid and need that when press space key, change bg color of selected horizontal line cells.
how can do that
TSelColor = class
public
Color: TColor;
constructor Create(const aColor: TColor);
end;
TForm1 = class(TForm)
StringGrid1: TStringGrid;
procedure StringGrid1DrawCell(Sender: TObject; ACol, ARow: Integer;
Rect: TRect; State: TGridDrawState);
procedure StringGrid1KeyDown(Sender: TObject; var Key: Word;
Shift: TShiftState);
private
{ Private declarations }
public
kPressed: boolean;
end;
var
Form1: TForm1;
implementation
{$R *.dfm}
procedure TForm1.StringGrid1DrawCell(Sender: TObject; ACol, ARow: Integer;
Rect: TRect; State: TGridDrawState);
var
yourColor: TColor;
begin
yourColor:= clRed;
if gdFixed in State then
TStringGrid(Sender).Canvas.Brush.Color:= clBtnFace
else if gdSelected in State then
begin
TStringGrid(Sender).Canvas.Brush.Color:= clAqua;
if kPressed and not (TStringGrid(Sender).Objects[ACol, ARow] is TSelColor) then
TStringGrid(Sender).Objects[ACol, ARow]:= TSelColor.Create(yourColor)
else if kPressed and (TStringGrid(Sender).Objects[ACol, ARow] is TSelColor) then
TStringGrid(Sender).Objects[ACol, ARow]:= nil;
end
else
begin
TStringGrid(Sender).Canvas.Brush.Color:= clWindow;
if TStringGrid(Sender).Objects[ACol, ARow] is TSelColor then
TStringGrid(Sender).Canvas.Brush.Color:= TSelColor(TStringGrid(Sender).Objects[ACol, ARow]).Color;
end;
TStringGrid(Sender).Canvas.FillRect(Rect);
TStringGrid(Sender).Canvas.TextOut(Rect.Left + 2, Rect.Top + 2, TStringGrid(Sender).Cells[ACol, ARow]);
end;
procedure TForm1.StringGrid1KeyDown(Sender: TObject; var Key: Word;
Shift: TShiftState);
begin
if Key = 32 then
begin
kPressed:= true;
StringGrid1.Repaint;
kPressed:= false;
end;
end;
{ TSelColor }
constructor TSelColor.Create(const aColor: TColor);
begin
inherited Create;
Color:= aColor;
end;
StringGrid1
DefaultDrawning [false]
Options.goRowSelect [true]

User moving Shape at run time

I have a unit called MachineShapes, with a type TShape on it. I am trying to get it so when a user clicks on shape they can move it. I think iam close but got a little confused. Thanks for any help
MachineShapes
unit MachineShape;
interface
uses
Winapi.Windows, Winapi.Messages, System.SysUtils, System.Variants, System.Classes, Vcl.Graphics,
Vcl.Controls, extctrls,myDataModule,Vcl.Forms, Vcl.Dialogs, Vcl.StdCtrls;
type
TMachine = class(TShape)
private
{ Private declarations }
FOnMouseDown : TNotifyEvent;
FOnMouseUp: TNotifyEvent;
FonMouseMove: TNotifyEvent;
procedure ControlMouseDown(Sender: TObject;
Button: TMouseButton;
Shift: TShiftState;
X, Y: Integer);
procedure ControlMouseMove(Sender: TObject;
Shift: TShiftState;
X, Y: Integer);
procedure ControlMouseUp(Sender: TObject;
Button: TMouseButton;
Shift: TShiftState;
X, Y: Integer);
private
inReposition : boolean;
oldPos : TPoint;
Protected
procedure DoMouseDown; virtual;
procedure DoMouseUp; Virtual;
procedure DoMouseMove; virtual;
Published
property OnMouseDown: TNotifyEvent Read FOnMouseDown Write fOnMouseDown;
property OnMouseMove: TNotifyEvent Read FOnMouseMove write fOnMouseMove;
Property onMouseUp : TNotifyEvent Read FOnMouseUp write FOnMouseUp;
public
{ Public declarations }
end;
implementation
uses
deptlayout;
procedure TMachine.ControlMouseMove(Sender: TObject; Shift: TShiftState; X: Integer; Y: Integer);
const
minWidth = 20;
minHeight = 20;
var
newPos: TPoint;
frmPoint : TPoint;
begin
if inReposition then
begin
with TWinControl(Sender) do
begin
GetCursorPos(newPos);
Screen.Cursor := crSize;
Left := Left - oldPos.X + newPos.X;
Top := Top - oldPos.Y + newPos.Y;
oldPos := newPos;
end;
end;
end;
procedure TMachine.ControlMouseUp(Sender: TObject; Button: TMouseButton; Shift: TShiftState; X: Integer; Y: Integer);
begin
if inReposition then
begin
Screen.Cursor := crDefault;
ReleaseCapture;
inReposition := False;
end;
end;
procedure TMachine.ControlMouseDown(Sender: TObject; Button: TMouseButton; Shift: TShiftState; X: Integer; Y: Integer);
begin
inReposition:=True;
SetCapture(TWinControl(Sender).Handle);
GetCursorPos(oldPos);
end;
procedure tmachine.DoMouseDown;
begin
if assigned(fonmousedown) then
fonmousedown(self);
end;
procedure tmachine.DoMouseUp;
begin
if assigned(fonmouseup) then
fonmouseup(self);
end;
procedure tmachine.domousemove;
begin
if assigned(fonmousemove) then
fonmousemove(self);
end;
end.
How i call it..
procedure TFGetZoneDept.CreateShape(Sender: TObject);
var
machine : TMachine;
begin
//creates the shape
machine := MachineShape.TMachine.Create(fdeptlayout); //form to create shape on
machine.Parent := fdeptlayout; //form to add shape to
machine.OnMouseDown := machinemouseDown;
machine.OnMouseUp := machinemouseUp;
machine.OnMouseMove:= machinemouseMove;
end;
procedure TFGetZoneDept.MachineMouseDown(Sender: TObject);
var
machine: TMachine;
begin
machine := Sender as TMachine;
end;
procedure TFGetZoneDept.MachineMouseUp(Sender: TObject);
var
machine: TMachine;
begin
machine := Sender as TMachine;
end;
procedure TFGetZoneDept.machineMouseMove(Sender: TObject);
var
machine: TMachine;
begin
machine := sender as Tmachine;
end;
A shape is no Wincontrol and has no handle you could do something tike that....
unit Unit1;
interface
uses
Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms,
Dialogs, ExtCtrls;
type
TMachine=Class(TShape)
private
FX,FY:Integer;
Procedure MyMouseDown(var msg:TWMLButtonDown);message WM_LButtonDown;
Procedure MyMouseMove(var msg:TWMMouseMove);message WM_MouseMove;
End;
TForm1 = class(TForm)
procedure FormCreate(Sender: TObject);
private
{ Private-Deklarationen }
public
{ Public-Deklarationen }
FX,FY:Integer;
end;
var
Form1: TForm1;
implementation
{$R *.dfm}
{ TMachine }
procedure TMachine.MyMouseDown(var msg: TWMLButtonDown);
begin
inherited;
FX := msg.XPos;
FY := msg.YPos;
end;
procedure TMachine.MyMouseMove(var msg: TWMMouseMove);
begin
inherited;
if ssLeft in KeysToShiftState(msg.Keys) then
begin
Left := Left+ msg.XPos -FX;
Top := Top + msg.YPos -FY;
end;
end;
procedure TForm1.FormCreate(Sender: TObject);
begin
With TMachine.Create(self) do
begin
Parent := Self;
Width := 200;
Height := 200;
end;
end;
end.

Moving images while drag and drop

I have an TImage on a TPanel, and an other (empty) TPanels. I want to drag
the image from the first to the second panel using the drag and drop.
I actually want to see the image while it's moving from one panel to the
other (semi-transparent).
I think I should use TDragObject.GetDragImages but I can't figure out how to construct the whole magic.
procedure TForm1.Image1MouseDown(Sender: TObject; Button: TMouseButton;
Shift: TShiftState; X, Y: Integer);
begin
Image1.ControlStyle := Image1.ControlStyle + [csDisplayDragImage]; // ???
TImage(Sender).BeginDrag(False);
end;
procedure TForm1.Image1StartDrag(Sender: TObject; var DragObject: TDragObject);
begin
// ???
end;
procedure TForm1.Panel1DragOver(Sender, Source: TObject; X, Y: Integer;
State: TDragState; var Accept: Boolean);
begin
if (Source is TImage) then
Accept := TImage(Source).Parent <> Sender;
end;
procedure TForm1.Panel1DragDrop(Sender, Source: TObject; X, Y: Integer);
begin
if (Source is TImage) then
begin
TImage(Source).Parent := TPanel(Sender);
TImage(Source).Align := alClient;
end;
end;
Update - I found a useful article: Implementing Professional Drag & Drop In VCL/CLX Applications
unit Unit3;
interface
// 2012 Thomas Wassermann - demo
uses
Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms,
Dialogs, ExtCtrls;
type
TMyDragObject = class(TDragControlObject)
private
FImageList:TImageList;
FDragSource:TControl;
protected
function GetDragImages: TDragImageList; override;
public
Procedure StartDrag(G:TGraphic;p:TPoint;DragSource:TControl);
Constructor Create(AControl: TControl); override;
Destructor Destroy;override;
Property DragSource:TControl read FDragSource;
end;
TForm3 = class(TForm)
Panel1: TPanel;
Panel2: TPanel;
Image1: TImage;
procedure Image1StartDrag(Sender: TObject; var DragObject: TDragObject);
procedure FormCreate(Sender: TObject);
procedure Panel1DragOver(Sender, Source: TObject; X, Y: Integer; State: TDragState; var Accept: Boolean);
procedure FormDestroy(Sender: TObject);
procedure Panel1DragDrop(Sender, Source: TObject; X, Y: Integer);
private
{ Private-Deklarationen }
FDragObject:TMyDragObject;
public
{ Public-Deklarationen }
end;
var
Form3: TForm3;
implementation
constructor TMyDragObject.Create(AControl: TControl);
begin
inherited;
FImageList:=TImageList.Create(nil);
end;
destructor TMyDragObject.Destroy;
begin
FImageList.Free;
inherited;
end;
function TMyDragObject.GetDragImages: TDragImageList;
begin
Result := FImageList;
end;
{$R *.dfm}
procedure TMyDragObject.StartDrag(G: TGraphic;p:TPoint;DragSource:TControl);
var
bmp:TBitMap;
begin
FDragSource := DragSource;
bmp:=TBitMap.Create;
try
FImageList.Width := g.Width;
FImageList.Height := g.Height;
bmp.Width := g.Width;
bmp.Height := g.Height;
bmp.Canvas.Draw(0,0,g);
FImageList.Add(bmp,nil);
finally
bmp.Free;
end;
FImageList.SetDragImage(0,p.x,p.y)
end;
procedure TForm3.FormCreate(Sender: TObject);
var
i:Integer;
begin
ControlStyle := ControlStyle + [csDisplayDragImage];
for I := 0 to ControlCount -1 do
if Controls[i] is TPanel then
TPanel(Controls[i]).ControlStyle := TPanel(Controls[i]).ControlStyle + [csDisplayDragImage];
ReportMemoryLeaksOnShutDown := True;
end;
procedure TForm3.FormDestroy(Sender: TObject);
begin
if Assigned(FDragObject) then FDragObject.Free;
end;
procedure TForm3.Image1StartDrag(Sender: TObject; var DragObject: TDragObject);
var
p:TPoint;
begin
p:=TImage(Sender).ScreenToClient(mouse.cursorpos);
if Assigned(FDragObject) then FDragObject.Free;
FDragObject := TMyDragObject.Create(TImage(Sender));
FDragObject.StartDrag(TImage(Sender).Picture.Graphic,p,TImage(Sender));
DragObject := FDragObject;
end;
procedure TForm3.Panel1DragDrop(Sender, Source: TObject; X, Y: Integer);
begin
if FDragObject.DragSource is TImage then
TImage(FDragObject.DragSource).Parent := TPanel(Sender);
end;
procedure TForm3.Panel1DragOver(Sender, Source: TObject; X, Y: Integer; State: TDragState; var Accept: Boolean);
begin
Accept := Source is TMyDragObject;
end;
end.

How to add more objects to my form without adding the same amount of code

I have been making a small game for fun. In the game you are a small spaceship(an image) that shoots lazer beams(shape) at an object(panel). At this moment u can only fire one lazer beam at a time because there is only one lazer beam(shape) and there is only one object(panel) to shoot. So with the coding I have I would like to know how I can add more lazer beams and objects but especially lazer beams because I don't want to repeat the procedures for each lazer beam and for each panel.
Here is the code.
unit Unit1;
interface
uses
Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms,
Dialogs, ExtCtrls, StdCtrls, jpeg;
type
TForm1 = class(TForm)
Panel1: TPanel;
Timer1: TTimer;
Timer2: TTimer;
Button1: TButton;
Shape1: TShape;
Timer3: TTimer;
Image1: TImage;
procedure Timer2Timer(Sender: TObject);
procedure Button1Click(Sender: TObject);
procedure FormActivate(Sender: TObject);
procedure FormMouseWheelDown(Sender: TObject; Shift: TShiftState;
MousePos: TPoint; var Handled: Boolean);
procedure FormMouseWheelUp(Sender: TObject; Shift: TShiftState;
MousePos: TPoint; var Handled: Boolean);
procedure Timer3Timer(Sender: TObject);
procedure FormMouseDown(Sender: TObject; Button: TMouseButton;
Shift: TShiftState; X, Y: Integer);
private
{ Private declarations }
public
procedure StartPanelAnimation1;
procedure DoPanelAnimationStep1;
function PanelAnimationComplete1: Boolean;
procedure Startlazeranimation1;
procedure DolazeranimationStep1;
{ Public declarations }
end;
var
Form1: TForm1;
implementation
var key : char;
{$R *.dfm}
{ TForm1 }
{ TForm1 }
procedure TForm1.DoPanelAnimationStep1;
begin
Panel1.Top := Panel1.Top+1;
end;
function TForm1.PanelAnimationComplete1: Boolean;
begin
Result := Panel1.Top=512;
end;
procedure TForm1.StartPanelAnimation1;
begin
Panel1.Top := 0;
Timer1.Interval := 1;
Timer1.Enabled := True;
end;
procedure TForm1.Timer2Timer(Sender: TObject);
begin
DoPanelAnimationStep1;
if PanelAnimationComplete1 then
StartPanelAnimation1;
if (shape1.Top < panel1.Top) and (shape1.Left < panel1.Left+104) and (shape1.Left > panel1.Left) then
begin
startpanelanimation1;
sleep(10);
end;
end;
procedure TForm1.Button1Click(Sender: TObject);
begin
button1.Hide;
key := 'a';
timer2.Enabled := true;
StartPanelAnimation1;
end;
procedure TForm1.FormActivate(Sender: TObject);
begin
shape1.Visible := false;
timer3.Enabled := false;
timer2.Enabled := false;
end;
procedure TForm1.FormMouseWheelDown(Sender: TObject; Shift: TShiftState;
MousePos: TPoint; var Handled: Boolean);
begin
image1.Left := image1.Left-10;
end;
procedure TForm1.FormMouseWheelUp(Sender: TObject; Shift: TShiftState;
MousePos: TPoint; var Handled: Boolean);
begin
image1.Left := image1.Left+10;
end;
procedure TForm1.DolazeranimationStep1;
begin
shape1.Top := shape1.Top-10;
end;
procedure TForm1.Startlazeranimation1;
begin
shape1.Top := image1.Top;
shape1.Left := image1.Left+55;
shape1.Visible := true;
Timer3.Interval := 1;
Timer3.Enabled := True;
end;
procedure TForm1.Timer3Timer(Sender: TObject);
var k : integer;
begin
DolazeranimationStep1;
if (shape1.Top < panel1.Top) and (shape1.Left < panel1.Left+104) and (shape1.Left > panel1.Left) or (shape1.Top=clientheight) then
begin
timer3.Enabled := false;
shape1.Visible := false;
for k := 1 to 5 do
sleep(1);
begin
application.ProcessMessages;
end;
shape1.Top := 0;
shape1.Left := 0;
end;
end;
procedure TForm1.FormMouseDown(Sender: TObject; Button: TMouseButton;
Shift: TShiftState; X, Y: Integer);
begin
shape1.Show;
startlazeranimation1;
end;
end.
(The above is the old code)
I have successfully done what Stijn Sanders suggested. But now in this if
if (Shape1.top < panel1.Top) and (shape1.Left < panel1.Left+104) and (shape1.Left > panel1.Left) then
never tests true because shape1 never passes panel1 it, only the shape created on on click passes the panel.
So is there another way to test if the shape is at the pnael.
Not all components need to be created at design time. At run-time, for example using a TTimer and its event, you can call TShape.Create(Self); to have an extra shape. Keep the reference to the resulting value somewhere convenient, for example a (dynamic) array, and remember to set MyShape.Parent:=Self; or MyShape.Parent:=Panel1; so the system knows when and where to display this new control.
unit Unit1;
interface
uses
Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms,
Dialogs, ExtCtrls;
const
MaxRays=100;
RayStep=8;
type
TForm1 = class(TForm)
Timer1: TTimer;
procedure FormCreate(Sender: TObject);
procedure Timer1Timer(Sender: TObject);
procedure FormMouseDown(Sender: TObject; Button: TMouseButton;
Shift: TShiftState; X, Y: Integer);
private
{ Private declarations }
Rays:array[0..MaxRays-1] of TShape;
public
{ Public declarations }
end;
var
Form1: TForm1;
implementation
{$R *.dfm}
procedure TForm1.FormCreate(Sender: TObject);
var
i:integer;
begin
for i:=0 to MaxRays-1 do Rays[i]:=nil;
end;
procedure TForm1.FormMouseDown(Sender: TObject; Button: TMouseButton;
Shift: TShiftState; X, Y: Integer);
var
i:integer;
begin
i:=0;
while (i<MaxRays) and (Rays[i]<>nil) do inc(i);
if i<MaxRays then
begin
Rays[i]:=TShape.Create(Self);
Rays[i].Shape:=stEllipse;
Rays[i].Pen.Color:=clRed;
Rays[i].Pen.Style:=psSolid;
Rays[i].Brush.Color:=clYellow;
Rays[i].Brush.Style:=bsSolid;
Rays[i].SetBounds(X-4,Y-20,9,41);
Rays[i].Parent:=Self;
end;
end;
procedure TForm1.Timer1Timer(Sender: TObject);
var
i:integer;
begin
for i:=0 to MaxRays-1 do
if Rays[i]<>nil then
begin
Rays[i].Top:=Rays[i].Top-RayStep;
if Rays[i].Top<0 then FreeAndNil(Rays[i]);
end;
end;
end.
Create your objects dynamically at runtime and keep track of them in a list, then you can loop through the list when needed, eg:
unit Unit1;
interface
uses
Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms,
Dialogs, ExtCtrls, StdCtrls, jpeg;
type
TForm1 = class(TForm)
PanelTimer: TTimer;
Button1: TButton;
LazerTimer: TTimer;
Image1: TImage;
procedure PanelTimerTimer(Sender: TObject);
procedure Button1Click(Sender: TObject);
procedure FormCreate(Sender: TObject);
procedure FormDestroy(Sender: TObject);
procedure FormActivate(Sender: TObject);
procedure FormMouseWheelDown(Sender: TObject; Shift: TShiftState;
MousePos: TPoint; var Handled: Boolean);
procedure FormMouseWheelUp(Sender: TObject; Shift: TShiftState;
MousePos: TPoint; var Handled: Boolean);
procedure LazerTimerTimer(Sender: TObject);
procedure FormMouseDown(Sender: TObject; Button: TMouseButton;
Shift: TShiftState; X, Y: Integer);
private
{ Private declarations }
Lazers: TList;
Panels: TList;
procedure StartPanelAnimation;
procedure StartLazerAnimation;
public
{ Public declarations }
end;
var
Form1: TForm1;
implementation
{$R *.dfm}
{ TForm1 }
procedure TForm1.StartPanelAnimation;
var
Panel: TPanel;
begin
Panel := TPanel.Create(Self);
Panel.Parent := Self;
Panel.Top := 0;
// set other Panel properties as needed...
Panel.Visible := True;
Panels.Add(Panel);
if not PanelTimer.Enabled then
begin
PanelTimer.Interval := 1;
PanelTimer.Enabled := True;
end;
end;
procedure TForm1.PanelTimerTimer(Sender: TObject);
var
k: Integer;
Panel: TPanel;
begin
for k := 0 to Panels.Count-1 do
begin
Panel := TPanel(Panels[k]);
Panel.Top := Panel.Top + 1;
if Panel.Top = 512 then
Panel.Top := 0;
end;
end;
procedure TForm1.Button1Click(Sender: TObject);
begin
StartPanelAnimation1;
end;
procedure TForm1.FormCreate(Sender: TObject);
begin
Lazers := TList.Create;
Panels := TList.Create;
end;
procedure TForm1.FormDestroy(Sender: TObject);
begin
Lazers.Free;
Panels.Free;
end;
procedure TForm1.FormActivate(Sender: TObject);
begin
PanelTimer.Enabled := False;
LazerTimer.Enabled := False;
end;
procedure TForm1.FormMouseWheelDown(Sender: TObject; Shift: TShiftState;
MousePos: TPoint; var Handled: Boolean);
begin
Image1.Left := Image1.Left - 10;
end;
procedure TForm1.FormMouseWheelUp(Sender: TObject; Shift: TShiftState;
MousePos: TPoint; var Handled: Boolean);
begin
Image1.Left := Image1.Left + 10;
end;
procedure TForm1.StartLazerAnimation;
var
Lazer: TShape;
begin
Lazer := TShape.Create(Self);
Lazer.Parent := Self;
// set Lazer properties as needed...
Lazer.Top := Image1.Top;
Lazer.Left := Image1.Left + 55;
Lazer.Visible := True;
Lazers.Add(Lazer);
if not Lazer.Enabled then
begin
Lazer.Interval := 1;
Lazer.Enabled := True;
end;
end;
procedure TForm1.LazerTimerTimer(Sender: TObject);
var
k, m : integer;
Lazer: TShape;
Panel: TPanel;
PanelHit: Boolean;
begin
k := 0;
while k < Lazers.Count do
begin
Lazer := TShape(Lazers[k]);
Lazer.Top := Lazer.Top - 10;
for m := 0 to Panels.Count-1 do
begin
Panel := TPanel(Panels[m]);
PanelHit := (Lazer.Top > (Panel.Top+Panel.Height)) and (Lazer.Left > Panel.Left) and (Lazer.Left < (Panel.Left+Panel.Width));
if PanelHit then
begin
Panels.Remove(Panel);
Panel.Free;
if Panels.Count = 0 then
PanelTimer.Enabled := False;
Break;
end;
end;
if PanelHit or (Lazer.Top = 0) then
begin
Lazers.Remove(Lazer);
Lazer.Free;
if Lazers.Count = 0 then
LazerTimer.Enabled := False;
end else
Inc(k);
end;
end;
procedure TForm1.FormMouseDown(Sender: TObject; Button: TMouseButton;
Shift: TShiftState; X, Y: Integer);
begin
StartLazerAnimation;
end;
end.

Resources