I wonder if there is some way to create TShape controls programmatically during runtime. For example, insteed of putting 100 shapes, hide them and when the program runs, show them, 100 shapes could be created over some time (5 shapes created in 5 seconds, 10 in 10 seconds, 15 in 15 seconds, and so on).
You should not draw and animate by using controls. Instead, you should draw manually using plain GDI or some other API. For an example, see this example or this example from one of your questions.
Anyhow, a simple answer to your question: Put a TTimer on your form and set its Interval to 250, and write:
unit Unit1;
interface
uses
Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms,
Dialogs, ExtCtrls;
type
TForm1 = class(TForm)
Timer1: TTimer;
procedure Timer1Timer(Sender: TObject);
private
{ Private declarations }
FShapes: array of TShape;
public
{ Public declarations }
end;
var
Form1: TForm1;
implementation
{$R *.dfm}
procedure TForm1.Timer1Timer(Sender: TObject);
begin
SetLength(FShapes, Length(FShapes) + 1); // Ugly!
FShapes[high(FShapes)] := TShape.Create(Self);
FShapes[high(FShapes)].Parent := Self;
FShapes[high(FShapes)].Width := Random(100);
FShapes[high(FShapes)].Height := Random(100);
FShapes[high(FShapes)].Left := Random(Width - FShapes[high(FShapes)].Width);
FShapes[high(FShapes)].Top := Random(Height - FShapes[high(FShapes)].Height);
FShapes[high(FShapes)].Brush.Color := RGB(Random(255), Random(255), Random(255));
FShapes[high(FShapes)].Shape := TShapeType(random(ord(high(TShapeType))))
end;
end.
Related
I have created a simple Delphi form with a button that, when pressed, creates a label object in run time. I have created an on double click event for the label that shows a message to the screen. The problem is that after creating the label, I have to double click on the form before the double click event works on the label. Obviously this is not ideal as I would like to be able to double click on the label and trigger the event without having to first double click the form.
Here is the code for my form:
unit Unit4;
interface
uses
Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms,
Dialogs, StdCtrls;
type
TForm4 = class(TForm)
Button1: TButton;
procedure Button1Click(Sender: TObject);
procedure FormDblClick(Sender: TObject);
procedure MyLabelDblClick(Sender:TObject);
private
{ Private declarations }
LabelObject: TLabel;
public
{ Public declarations }
end;
var
Form4: TForm4;
implementation
{$R *.dfm}
procedure TForm4.Button1Click(Sender: TObject);
begin
LabelObject := TLabel.Create(Self);
LabelObject.Left := 100;
LabelObject.Top := 100;
LabelObject.Width := 200;
LabelObject.Height := 20;
LabelObject.Visible := True;
LabelObject.Parent := Self;
LabelObject.Caption := 'My Run Time Label';
LabelObject.Cursor := crHandPoint;
end;
procedure TForm4.FormDblClick(Sender: TObject);
begin
LabelObject.OnDblClick := MyLabelDblClick;
end;
procedure TForm4.MyLabelDblClick(Sender: TObject);
begin
showmessage('You double clicked My Run Time Label');
end;
end.
Thanks in advance for any help with this matter.
The problem is that after creating the label, I have to double click on the form before the double click event works on the label.
Assign LabelObject.OnDblClick when creating the label, i.e. inside the Button1Click event.
I started learning classes and objects programming today. There is code in the handbook that I must copy to run and save. I need to create a class(TLine) and use that class for instantiating an object.
Problem : No output is displayed in my RichEdit component. I copied the code exactly from the book to delphi, but no output is displayed.
How the output should look: "**********"
My class:
unit Lines_U;
interface
uses
Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms,
Dialogs;
Type
TLine = Class
Public
fSize : integer;
fPattern : char;
public
Constructor Create;
Procedure Draw(Var line: string);
end;
implementation
{ TLine }
Constructor TLine.Create;
begin
fSize := 10;
fPattern := '*';
end;
Procedure TLine.Draw(Var line: string);
Var
loop : integer;
begin
for loop := 1 to fSize do
begin
line := line + fPattern;
end;
end;
end.
Code for instantiating the Object of the TLine Class:
unit UseLine_U;
interface
uses
Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms,
Dialogs, Lines_U, StdCtrls, ComCtrls;
type
TForm1 = class(TForm)
redOut: TRichEdit;
Procedure FormCreate(Sender: TObject);
private
{ Private declarations }
public
{ Public declarations }
end;
var
Form1: TForm1;
line : TLine;
implementation
{$R *.dfm}
Procedure TForm1.FormCreate(Sender: TObject);
Var tempLine : string;
begin
line := TLine.Create;
line.Draw(tempLine);
redOut.Lines.Add(tempLine);
end;
end.
The reason your code is not running is that your event handler Form1.FormCreate is not linked to the OnCreate event. Restore the link in the object inspector.
About event handlers
Never write event handlers (all those procedures starting with On...) manually. Always use the Object inspector to create them.
If you double click on an event, Delphi will create a code template for you that you can fill with data.
Make sure your event handlers are filled in the object inspector. If not they will not work (as you've seen).
If you want to remove an event handler do not remove it in the object inspector, but reduce the code inside the event handling procedure back to the empty template.
Delphi will see that it is empty and remove it on the next compile.
About your code
Other than the missing link there is nothing wrong with your code. It runs just fine.
There are a few style issues though, these have no bearing on the operation, but are important none the less.
Here's how I would rewrite your code.
unit Lines_U;
interface
//only import units that you actually use.
type //please type reserved words in all lowercase, this is Pascal not VB.
TLine = class
private //make data members private.
fSize : integer;
fPattern : char;
public
constructor Create;
procedure Draw(var line: string);
property Size: integer read fSize write fSize; //Use properties to expose data members.
property Pattern: char read fPattern write fPattern;
end;
implementation
{ TLine }
constructor TLine.Create;
begin
inherited; //make the inherited call in your constructor explicit.
fSize := 10;
fPattern := '*';
end;
procedure TLine.Draw(var line: string);
//var
//loop : integer; //use consistent indentation
begin
//Changing a string ten times in a row is inefficient.
//try to do your changes all at once.
//for loop := 1 to fSize do begin
// line := line + fPattern;
//end;
Line:= Line + StringOfChar(fPattern, fSize);
end;
end.
Your form:
unit UseLine_U;
interface
uses
Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms,
Dialogs, StdCtrls, ComCtrls, Lines_U;
//put your own unit last, to prevent name clashes with built in classes and functions.
type
TForm1 = class(TForm)
//note that the {nothing} line is really **published**.
//And data members should be private
//Line : TLine; //Line should be private.
RedOut: TRichEdit;
procedure FormCreate(Sender: TObject);
private
//Prefix all private data with `F` for Field.
FLine: TLine; //Line should be a item in the form, not a global var.
public
property Line: TLine read FLine; //read only access to line.
end;
var
Form1: TForm1;
implementation
{$R *.dfm}
procedure TForm1.FormCreate(Sender: TObject);
var
tempLine : string;
i: integer;
begin
//tempLine:= ''; //local variables should be initialized.
//However strings are always initialized to '', because they are managed types.
//everything else will contain random data unless you fill it!
FLine := TLine.Create;
Line.Draw(tempLine);
i:= 0; //init i, otherwise it will be random!
while i < 5 do begin //always use `begin-end` in loops, never a naked `do`
RedOut.Lines.Add(tempLine);
i:= i + 1;
end; {while} //I like to annotate my loop `end`s, but that's just me.
FreeAndNil(FLine); //Dispose of TLine when you're done with it.
end;
end.
I can think of other things, but I don't want to overload you.
How to create (when I want to show it) and destroy (when I want to hide it) frames on the main TForm? Frames' align = alClient.
I tried this:
The form:
unit main;
interface
uses
Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms, Dialogs, StdCtrls, uFrame1, uFrame2;
type
TFormMain = class(TForm)
procedure FormCreate(Sender: TObject);
private
{ Private declarations }
public
{ Public declarations }
f1: TFrame1;
f2: TFrame2;
end;
var
FormMain: TFormMain;
implementation
{$R *.dfm}
procedure TFormMain.FormCreate(Sender: TObject);
begin
f1 := TFrame1.Create(Self);
f1.Parent := Self;
end;
end.
First frame:
unit uFrame1;
interface
uses
Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms, Dialogs, StdCtrls;
type
TFrame1 = class(TFrame)
btn1: TButton;
procedure btn1Click(Sender: TObject);
private
{ Private declarations }
public
{ Public declarations }
end;
implementation
{$R *.dfm}
uses main, uFrame2;
procedure TFrame1.btn1Click(Sender: TObject);
begin
Self.Free;
FormMain.f2 := TFrame2.Create(FormMain);
FormMain.f2.Parent := FormMain;
end;
end.
Second frame:
unit uFrame2;
interface
uses
Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms, Dialogs, StdCtrls;
type
TFrame2 = class(TFrame)
lbl1: TLabel;
btn1: TButton;
procedure btn1Click(Sender: TObject);
private
{ Private declarations }
public
{ Public declarations }
end;
implementation
{$R *.dfm}
uses main, uFrame1;
procedure TFrame2.btn1Click(Sender: TObject);
begin
Self.Free;
FormMain.f1 := TFrame1.Create(FormMain);
FormMain.f1.Parent := FormMain;
end;
end.
but it crashes with access vialataions when I click button on FrameStart or Frame1 (TForm FormCreate works fine i.e. it creates and shows FrameStart).
Delphi 7.
You can't call Self.Free in those event handlers. When the event handler returns, the VCL code that executes next still uses a reference to an object that you just freed. And that's where the access violation comes from. If you had been running with FastMM in full debug mode then you would have been shown a helpful diagnostic message.
These frames will have to kill themselves in a more roundabout manner. Post a CM_RELEASE message to the frame asking it to call Free on the frame. You post the message, rather than sending it, so that all the in flight messages are processed first. You'll need to add a message handler to the frame to respond to the message.
You've got some of it.
The basic idea behind this sort of stuff.
add a private property to your mainform to hold the frame.
in the button click handler assuming you only want one at a time do
if assigned(fMyFrame) then
begin
fMyFrame.Free;
fMyFrame := nil;
end;
fMyFrame := TSomeFrame.Create(self);
fMyFrame.Parent := self;
fMyFrame.blah...
When you just want to get rid of it as opposed to replacing it
if assigned(fMyFrame) then
begin
fMyFrame.Free;
fMyFrame := nil;
end;
If you want your frame to raise another frame, repeat the above in there.
If you want the frame you raise in a frame to be a sibling, e.g. have the same Parent, then don't use Form1 var.
fMyNextFrame.Parent = self.Parent;
There's a huge number of ways you could improve on this once you get it working, it's a classic scenario for interfaces and or inheritance, but figure this bit out first.
mySomething := TMySomething.Create();
you can now do something with something.
After you called free, it's not can't, it's don't and don't let anything else either.
Don't do self.free, it's like playing with matches in barrel of petrol. It will hurt....
Using delphi 7 TRichEdit component, RTF data is being imported from a msword document through copy and paste, but if data is contained in a box, it is not displaying correctly i.e.
Please assist
Try to use the following, it should subclass the TRichEdit class to version 4.1. However I don't know if Delphi 7 supports interposed classes, so just try to paste the following code and try to build the project.If it compiles then if you put a TRichEdit component and run the project you should get RichEdit 4.1.
unit Unit1;
interface
uses
Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms,
Dialogs, StdCtrls, ComCtrls, RichEdit;
type
TRichEdit = class(ComCtrls.TRichEdit)
protected
procedure CreateParams(var Params: TCreateParams); override;
end;
type
TForm1 = class(TForm)
private
{ Private declarations }
public
{ Public declarations }
end;
var
Form1: TForm1;
FRichEditModule: THandle;
implementation
{$R *.dfm}
{ TRichEdit }
procedure TRichEdit.CreateParams(var Params: TCreateParams);
const
RichEditClassName = 'RICHEDIT50A';
RichEditModuleName = 'MSFTEDIT.DLL';
HideScrollBarsStyle: array[Boolean] of DWORD = (ES_DISABLENOSCROLL, 0);
HideSelectionsStyle: array[Boolean] of DWORD = (ES_NOHIDESEL, 0);
begin
if FRichEditModule = 0 then
begin
FRichEditModule := LoadLibrary(RichEditModuleName);
if FRichEditModule <= HINSTANCE_ERROR then
FRichEditModule := 0;
end;
inherited CreateParams(Params);
CreateSubClass(Params, RichEditClassName);
Params.Style := Params.Style or HideScrollBarsStyle[HideScrollBars] or
HideSelectionsStyle[HideSelection];
Params.WindowClass.style := Params.WindowClass.style and
not (CS_HREDRAW or CS_VREDRAW);
end;
initialization
finalization
if FRichEditModule <> 0 then
FreeLibrary(FRichEditModule);
end.
Finally got it to work,
It was as simple as adding the Riched20.dll (Latest version) to the application folder
This is my code:
unit Unit1;
interface
uses
Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms,
Dialogs, StdCtrls, ComCtrls;
...
implementation
{$R *.dfm}
uses Unit2;
...
procedure TForm1.Button4Click(Sender: TObject);
begin
Frame2.Show;
end;
I got this compiler error:
Undeclared identifier: 'Frame2'
Then I tried to declare it:
Frame2: TFrame2;
Edit:
Further explenation form comment.
Ok I will be precise. I use anwser ardnew Frame2: TFrame; and I get ** access violation** and with out it I get Undeclared identifier: 'Frame2' now I'm more precise?
You did not show the contents of Unit2, so we can only speculate. It sounds like there is no Frame2 global variable declared in Unit2.pas. That would account for the undeclared identifier error. Declare the variable yourself, and instantiate an instance of the TFrame2 class before you can then Show() it, eg:
unit Unit1;
interface
uses
Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms,
Dialogs, StdCtrls, ComCtrls;
...
implementation
{$R *.dfm}
uses
Unit2;
var
Frame2: TFrame2 = nil;
...
procedure TForm1.Button4Click(Sender: TObject);
begin
if not Assigned(Frame2) then
begin
Frame2 := TFrame2.Create(Self);
Frame2.Parent := Self;
end;
Frame2.Show;
end;
You need to create the frame somehow, you can't access it without.
This example presumes you are creating 2 different "TFrame2" instances at once just temporarily, then closing (and freeing) them when done (in a try..finally block). There are many other ways of creating and freeing, but the general concept is if you create it, you have to free it...
procedure TForm1.Button4Click(Sender: TObject);
var
F1, F2: TFrame2;
begin
//You have to first create the instances of "TFrame2"...
F1:= TFrame2.Create(Self);
F2:= TFrame2.Create(Self);
try
F1.Left:= 0;
F2.Left:= Self.Width - F2.Width;
F1.Parent := Self;
F2.Parent := Self;
F1.Show;
F2.Show;
Application.ProcessMessages;
ShowMessage('There should be 2 instances of "TFrame2" showing on your main form');
finally
//And you have to free them when you're done...
F1.Free;
F2.Free;
end;
end;
Or if this "TFrame2" is elsewhere...
procedure TForm1.Create(Sender: TObject);
begin
//Create it first
Frame2:= TFrame2.Create(Self);
Frame2.Parent := Self;
Frame2.Left:= 0;
Frame2.Show;
end;
procedure TForm1.Destroy(Sender: TObject);
begin
if assigned(Frame2) then begin
Frame2.Free;
Frame2:= nil;
end;
end;
Be careful though, because you might already be creating this "TFrame2"... Go to Project > Options > Forms and look to see if "Frame2" is auto-create or not.
I guess it should be declared as
Frame2: TFrame;