I've downloaded Delphi XE7 and having some problems with accessing another Units...
I need to call procedures from another units, so I'll give a very basic illustration, simple program...
This is code from main Unit1 with form and button1:
unit Unit1;
interface
uses
Windows, Messages, SysUtils, Variants, Classes, Graphics,
Controls, Forms, Dialogs, StdCtrls, Unit2;
type
TForm1 = class(TForm)
Button1: TButton;
procedure Button1Click(Sender: TObject);
private
{ Private declarations }
public
{ Public declarations }
end;
var
Form1: TForm1;
implementation
{$R *.dfm}
procedure TForm1.Button1Click(Sender: TObject);
begin
ShowMessage('Hello');
end;
end.
And this is the code from Unit2:
unit Unit2;
interface
implementation
uses Unit1;
end.
Now, how is it possible to make procedure Button1Click like in Unit2 to showmessage let's say HelloFromUnit2 when button1 on form1 is clicked? Unit2 is codeUnit without anything..
Use the build in procedure for calling the Click handler
Leave form 1 the way it is:
unit Unit2;
interface
implementation
uses
Unit1;
procedure Click;
begin
if Assigned(Form1) then
Form1.Button1.Click;
end;
end.
Add a procedure declaration to the public section of TForm1, like this
type
TForm1 = class(TForm)
Button1: TButton;
procedure Button1Click(Sender: TObject);
private
public
Procedure SayHello;
end;
...
procedure TForm1.SayHello;
begin
ShowMessage('Hello');
end;
end.
Then in Unit2 you would call this procedure. You would have to ensure that Form2 has already been instantiated - or create a new instance for your call.
Do not use this mechanism for event handlers!
The header of your post doesn't match the question in the text
"Call Button1Click in Form1/Unit1 from Unit2" vs.
"Now, how is it possible to make procedure Button1Click like in Unit2 to showmessage let's say HelloFromUnit2 when button1 on form1 is clicked?"
I answer the question in the text (as I understand it). If this is not what you intended, you might want to rephrase the question in the text.
Add, to Form1.Button1Click, a call to a new procedure in unit2
procedure TForm1.Button1Click(Sender: TObject);
begin
ShowMessage('Hello');
SayHelloFromUnit2; // <---- add this
end;
In unit2 add the following to the interface section:
procedure SayHelloFromUnit2;
and to the implementation section
uses Vcl.Dialogs;
procedure SayHelloFromUnit2;
begin
ShowMessage('Hello from unit2');
end;
Related
I have a simple project in Delphi:
program Project1;
uses
Forms,
Unit2 in 'Unit2.pas',
Unit1 in 'Unit1.pas' {Form1};
{$R *.res}
begin
Application.Initialize;
Application.CreateForm(TForm1, Form1);
Application.Run;
end.
Unit1:
unit Unit1;
interface
uses
Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms,
Dialogs, StdCtrls;
type
TForm1 = class(TForm)
Edit1: TEdit;
Button1: TButton;
procedure Button1Click(Sender: TObject);
private
{ Private declarations }
public
{ Public declarations }
end;
var
Form1: TForm1;
implementation
uses Unit2;
{$R *.dfm}
function encodeData(var Data:array of Byte; var Size: Integer): Integer;
var
i: Intger
begin
...
for i := 1 to Size do
begin
Data[i] := Data[i] + unit2.SomeArray[i]
end;
...
Result := 0;
Exit;
end
...
The second unit:
unit Unit2;
interface
implementation
const
SomeArray:Array [0..65000] of LongWord = (
...
);
end.
When I'm trying to build this project I get errors like this:
[Error] Unit1.pas(41): Undeclared identifier: 'SomeArray'
What's wrong with this code? I checked Delphi wiki and other questions and didn't find solution for this issue...
You need to define SomeArray in the interface section of the unit. Currently, you have it in the implementation section, which is purposely hidden from other units. Only things defined/declared in the interface are visible to other units.
In the documentation you linked, it's described:
The implementation section of a unit begins with the reserved word implementation and continues until the beginning of the initialization section or, if there is no initialization section, until the end of the unit. The implementation section defines procedures and functions that are declared in the interface section. Within the implementation section, these procedures and functions may be defined and called in any order. You can omit parameter lists from public procedure and function headings when you define them in the implementation section; but if you include a parameter list, it must match the declaration in the interface section exactly.
In addition to definitions of public procedures and functions, the implementation section can declare constants, types (including classes), variables, procedures, and functions that are private to the unit. That is, unlike the interface section, entities declared in the implementation section are inaccessible to other units.
(Emphasis mine)
I have an object that is created on Form1 and I would like to be able to access one of its fields on Form2. I have tried to google it and nobody can give an answer that I can understand. Please excuse me but I am a novice.
Form1
unit Unit1;
interface
uses
Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms,
Dialogs, StdCtrls;
type
Ttest=class
public
sName:string;
end;
type
TForm1 = class(TForm)
Button1: TButton;
procedure Button1Click(Sender: TObject);
end;
var
Form1: TForm1;
implementation
uses Unit2;
{$R *.dfm}
procedure TForm1.Button1Click(Sender: TObject);
var
myObj:Ttest;
begin
myObj.Create;
myObj.sName := 'Name';
Form2.Show;
end;
end.
Form2
unit Unit2;
interface
uses
Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms,
Dialogs, StdCtrls;
type
TForm2 = class(TForm)
Button2: TButton;
procedure Button2Click(Sender: TObject);
end;
var
Form2: TForm2;
implementation
uses Unit1;
{$R *.dfm}
procedure TForm2.Button2Click(Sender: TObject);
begin
ShowMessage(myObj.sName);//This is not working
end;
end.
You have two forms that both use an object. You should define the object in a separate unit and list it in the Uses clause in the Interface section of both forms. Try using something already defined in a main library, like TStringlist, so you don't get confused with this part.
From what you're showing here, you're attempting to create an instance of that object in one form and do something with it in another form. That's a common thing to do: you may have one unit that asks for a filename and loads a file into a TStringList, then hands that over to another form or unit to deal with.
The way you're doing it, however, can be improved to reduce coupling between the two forms.
What you want to do is define a property like this in TForm2:
TForm2 = class( TForm )
. . .
private
Ftestobj : TTest; // or TStringlist
public
property testobj : TTest read Ftestobj write Ftestobj;
Then in TForm1.OnButtonClick do something like this:
form2.testobj := myobj;
form2.Show;
And then this becomes:
procedure TForm2.Button2Click(Sender: TObject);
begin
ShowMessage(Ftestobj.sName);
end;
I did a whole session in CodeRage 9 on this topic recently, in fact. It's entitled, "Have you embraced your inner plumber yet?" and it's all about moving data in and out of forms like this. (I call it plumbing code.)
Search for "coderage 9" and watch the video. At the end is a link where you can download my example code. That should keep you busy for a while. :)
This question already has an answer here:
Resource not found error when using TForm as base for a component
(1 answer)
Closed 6 years ago.
I have put together this code for creating a dynamic form
unit Unit1;
interface
uses
Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms,
Dialogs, StdCtrls;
type
TForm1 = class(TForm)
Button1: TButton;
procedure Button1Click(Sender: TObject);
private
{ Private declarations }
public
{ Public declarations }
end;
type
TForm2 = class(TForm)
private
{ Private declarations }
public
{ Public declarations }
end;
var
Form2: TForm2;
Form1: TForm1;
implementation
{$R *.dfm}
procedure TForm1.Button1Click(Sender: TObject);
var
a:TForm2;
begin
a:=TForm2.Create(nil);
end;
end.
I get an error saying resource tform2 cannot be found. What must i do?
Thanks
You are calling the TForm.Create() constructor that loads the TForm contents from a DFM, but your project does not have a DFM for TForm2, which is why you are getting the resource error. To skip that, you need to use the TForm.CreateNew() constructor instead.
procedure TForm1.Button1Click(Sender: TObject);
var
a: TForm2;
begin
a := TForm2.CreateNew(nil, 0);
...
end;
In Delphi you must declare only one form per unit, also each form needs a dfm file, that file store the form definition and components properties. In your code you have this error because the application can't found the dfm file for the TForm2 form. So to fix the problem just create a new form (TForm2) in a separate unit and then add the unit a name to the unit where you need to call the TForm2.
i have 2 Form (Form1 and Form2) in the my project, Form1 is Auto-create forms, but Form2 is Available forms.
how i can to create Form2 and unload Form1?
I received a "Access validation" Error in this code.
Here is Form1 code:
1. uses Unit2;
//*********
2. procedure TForm1.FormCreate(Sender: TObject);
3. var a:TForm2;
4. begin
5. a := TForm2.Create(self);
6. a.Show;
7. self.free; // Or self.destory;
8. end;
Thanks.
I modified that "Serg" code to this :
unit Unit1;
interface
uses
Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms,
Dialogs, StdCtrls;
type
TForm1 = class(TForm)
Button1: TButton;
procedure Button1Click(Sender: TObject);
private
{ Private declarations }
public
{ Public declarations }
end;
var
Form1: TForm1;
implementation
uses Unit2;
{$R *.dfm}
procedure TForm1.Button1Click(Sender: TObject);
begin
Application.CreateForm(TForm2, Form2);
Release;
end;
end.
///
program Project1;
uses
Forms,
Unit1 in 'Unit1.pas' {Form1},
Unit2 in 'Unit2.pas' {Form2};
{$R *.res}
begin
Application.Initialize;
Application.MainFormOnTaskbar := True;
Form1:= TForm1.Create(Application);
Application.Run;
end.
but this project start and then exit automatically, Why?
i want to show Form1 and when we click Button1 then show Form2 and free(Release) Form1. how i can to this?
When you destroy a form, it's better to use Release.
Release is almost the same as free, but it waits for pending messages to avoid crashes.
You should never use Destroy. Free/Release calls the destructor.
Self is the current object (in your code Form1, so self.Free kills the current form. Which results in the access violation. Form1 is auto created, it is also auto destroyed so you shouldn't destroy it yourself. If you don't want it, hide it.
And you should keep a reference to the newly created form if you want to handle it later.
Your modified code should be like:
uses Unit2;
TForm1 = class (TForm)
private
FChild : TForm2;
end;
procedure TForm1.FormCreate(Sender: TObject);
begin
FChild := TForm2.Create(nil);
Hide; // Hides form 1
FChild.Show;
end;
procedure TForm2.FormDestroy(Sender: TObject);
begin
FChild.Release;
end;
But Why do you want to create another form in the form create of the first form. Why not remove the first form entirely and only use the second one (auto created)?
You are trying to do something strange.
You cannot free main form without closing application, so your Form1 should not be autocreated form, both Form1 and Form2 should be created manually.
First, you should edit your project source like this to create Form1 manually:
program Project9;
uses
Forms,
Unit1 in 'Unit1.pas' {Form1},
Unit2 in 'Unit2.pas' {Form2};
{$R *.res}
begin
Application.Initialize;
Application.MainFormOnTaskbar := True;
Form1:= TForm1.Create(Application);
Application.Run;
end.
Form1.OnCreate should be written as
procedure TForm1.FormCreate(Sender: TObject);
begin
Application.CreateForm(TForm2, Form2);
Release;
end;
that will make Form2 the main form of your application. As already answered you should use Release method to free the form.
If Form1 is 'autocreate', it's owned by the application object - you shouldn't free it in your code. If Form1 owns Form2, application cleans up both.
I'd do it like this, but not sure it meets your requirements:
procedure TForm1.FormCreate(Sender: TObject);
var a:TForm2;
begin
a := TForm2.Create(nil);
try
a.Show;
finally
freeandNil(a);
end;
end;
begin
Application.Initialize;
Application.CreateForm(TForm1, Form1);
Application.Run;
end.
...
procedure TForm1.FormClose(Sender: TObject; var CanClose: Boolean);
begin
if MessageDlg ('Are you want to exit?', mtConfirmation,
[mbYes, mbNo], 0) = mrNo then
CanClose := False;
end;
So, that is all...
If all Form1 should do is initialize something but not being shown, consider using a datamodule instead. These cannot be shown but can still be autocreated.
I did it by creating OLE object with Delphi in 2000/NT/XP as following:
Voice := CreateOLEObject('SAPI.SpVoice');
Voice.speak(...)
But this does not work in Vista, how can I make my program simply speak some text in Vista?
I just tried (D2009 on Vista Home Premium) with the following code and it works!
unit Unit1;
interface
uses
Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms,
Dialogs, StdCtrls, ComObj;
type
TForm1 = class(TForm)
Button1: TButton;
procedure Button1Click(Sender: TObject);
private
{ Private declarations }
public
{ Public declarations }
end;
var
Form1: TForm1;
implementation
{$R *.dfm}
procedure TForm1.Button1Click(Sender: TObject);
var
Voice: Variant;
begin
Voice := CreateOLEObject('SAPI.SpVoice');
Voice.speak('Hello World');
end;
end.
FYI, there is a nice paper on using speech in Delphi programming by Brian Long...
(Very) Late Update:
For why it might not work in Vista and give an EZeroDivide exception outside the IDE, see this other SO question: Delphi SAPI Text-To-Speech