Scoping issue with Set types - delphi

I have 3 units This is my main unit....
uses
Winapi.Windows, Winapi.Messages, System.SysUtils, System.Variants, System.Classes, Vcl.Graphics,
Vcl.Controls, Vcl.Forms, Vcl.Dialogs, Vcl.ExtCtrls, Vcl.StdCtrls;
type
TForm1 = class(TForm)
Button1: TButton;
Panel1: TPanel;
Panel2: TPanel;
Panel3: TPanel;
procedure FormShow(Sender: TObject);
private
{ Private declarations }
public
{ Public declarations }
end;
var
Form1: TForm1;
implementation
Uses Unit2;
{$R *.dfm}
procedure TForm1.FormShow(Sender: TObject);
begin
Panel1.Visible := oiNone in form2.Settings.OptVars;
Panel2.Visible := oiHint in form2.Settings.OptVars;
Panel3.Visible := oiStat in form2.Settings.OptVars;
end;
end.
This is the second unit - used by the first.
unit Unit2;
interface
uses
Winapi.Windows, Winapi.Messages, System.SysUtils, System.Variants, System.Classes, Vcl.Graphics,
Vcl.Controls, Vcl.Forms, Vcl.Dialogs, Vcl.StdCtrls, Unit3;
type
TForm2 = class(TForm)
CheckBox1: TCheckBox;
CheckBox2: TCheckBox;
CheckBox3: TCheckBox;
procedure FormCreate(Sender: TObject);
procedure FormDestroy(Sender: TObject);
private
{ Private declarations }
public
{ Public declarations }
Settings: TSettings;
end;
var
Form2: TForm2;
implementation
{$R *.dfm}
procedure TForm2.FormCreate(Sender: TObject);
begin
Settings := TSettings.Create;
Checkbox1.Checked := oiNone in Settings.OptVars;
CheckBox2.Checked := oiHint in Settings.OptVars;
CheckBox3.Checked := oiStat in Settings.OptVars;
end;
procedure TForm2.FormDestroy(Sender: TObject);
begin
Settings.Free;
end;
end.
and this is the unit which is used by the second unit and contains the Set type which is used by both other units.
unit Unit3;
interface
Uses
winAPI.windows,system.classes;
Type
TOptions = Set Of (oiNone, oiHint, oiStat);
TSettings = class
private
fMyOption: TOptions;
public
Constructor Create;
Destructor Destroy;
property OptVars: TOptions read fMyOption write fMyOption;
end;
implementation
constructor TSettings.Create;
begin
fMyOption := [oiNone, oiHint, oiStat];
end;
destructor TSettings.Destroy;
begin
end;
end.
In the second unit, the items oiNone,oiHint, oiStat are accessible and within scope.
In the first unit, the items oiNone, oiHint and oiStat are not accessible and within scope although the Settings.OpVars which is a TOption data type is accessible.
I cannot think of a better way to describe my problem. If you put these into a project you should see the issue.

Update
After the latest edit it is clear that my first hunch was correct. You weren't using the unit that declares the type. The names have changed, but with the current edit your problem is that the main unit does not use Unit3.
You need to reference the unit that declares the set type, and its enumerated type. Add UnusedItemsReg to your uses clause in the third unit.
If you have done that then the only other explanation I can imagine is that you compile with scoped enums enabled. But I'm clutching at straws now.
I would definitely recommend that you declare the enumerated type as a named type in its own right. Certainly if you use scoped enums then you'll need to do that. But sooner or later you'll want to have that type available.
type
TOption = (oiNone, oiHint, oiStat);
TOptions = set of TOption;
If you do use scoped enums then you'll refer to then like this:
TOption.oiNone

You need Unit3 in the (a) uses clause of the main unit. Otherwise it cannot see type TOptions. That is a requirement of Delphi for visibility. It does not use the implicit references that you seem to be seeking.

Related

How to solve the CYCLIC relation issue between 2 or more Units in Delphi?

I'm using 2 Units in Delphi 2010: Unit1 has a Form with a TEdit and a Button, this Button will call a procedure from Unit2 (Unit2 doesn't have a form). That procedure will change the 'caption' property of the TEdit of Unit1, I tried to put Unit2 in Unit1 "Uses" to access the procedure, and put Unit1 in the "Uses" of Unit2 to to access the TEdit in Unit1, but this relation is cyclic.
I'm not sure what can I do to solve this, any suggestion?
There are many ways to do this, and which one is most appropriate depends crucially on the precise circumstances, so technically this question is too broad for Stack Overflow, I suspect.
Method 1
Still, let me just show you one very simple approach. This might not be a good approach, but it is close to what it seems you are trying to do.
Create a new VCL application.
Drop a TButton and a TEdit control on the main form (TForm1 in Unit1).
Create a new unit, Unit2:
unit Unit2;
interface
procedure SetCaption;
implementation
uses
Unit1;
procedure SetCaption;
begin
Unit1.Form1.Edit1.Text := 'Hello, World!';
end;
end.
On the main form, use this event handler for the button's OnClick event:
procedure TForm1.Button1Click(Sender: TObject);
begin
Unit2.SetCaption;
end;
after you have added Unit2 to the implementation section's uses clause. In full,
unit Unit1;
interface
uses
Winapi.Windows, Winapi.Messages, System.SysUtils, System.Variants, System.Classes, Vcl.Graphics,
Vcl.Controls, Vcl.Forms, Vcl.Dialogs, Vcl.StdCtrls;
type
TForm1 = class(TForm)
Button1: TButton;
Edit1: TEdit;
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
Unit2.SetCaption;
end;
end.
Notice how the units may use each other if referenced only in the implementation sections. Also notice we use the global Form1 variable in Unit1 to refer to the automatically created instance of TForm1. In many (most) cases, you don't want to use automatically created forms, but that is a different story (I could write a 100-page Delphi textbook here!).
Method 2
Also, let me reiterate the fact that there are many other ways to accomplish this result. For instance, this is arguably a better approach:
unit Unit1;
interface
uses
Winapi.Windows, Winapi.Messages, System.SysUtils, System.Variants, System.Classes, Vcl.Graphics,
Vcl.Controls, Vcl.Forms, Vcl.Dialogs, Vcl.StdCtrls;
type
TForm1 = class(TForm)
Button1: TButton;
Edit1: TEdit;
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
Unit2.SetCaption(Edit1);
end;
end.
unit Unit2;
interface
uses
StdCtrls;
procedure SetCaption(AEdit: TCustomEdit);
implementation
procedure SetCaption(AEdit: TCustomEdit);
begin
AEdit.Text := 'Hello, World!';
end;
end.
Method 3
Arguably even better:
unit Unit1;
interface
uses
Winapi.Windows, Winapi.Messages, System.SysUtils, System.Variants, System.Classes, Vcl.Graphics,
Vcl.Controls, Vcl.Forms, Vcl.Dialogs, Vcl.StdCtrls;
type
TForm1 = class(TForm)
Button1: TButton;
Edit1: TEdit;
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
Edit1.Text := Unit2.GetText;
end;
end.
unit Unit2;
interface
function GetText: string;
implementation
function GetText: string;
begin
Result := 'Hello, World!';
end;
end.

FormCreate procedure

I have two units in my project as follow:
1 - Connexion unit:
unit Connexion;
interface
uses
Winapi.Windows, Winapi.Messages, System.SysUtils, System.Variants, System.Classes, Vcl.Graphics,
Vcl.Controls, Vcl.Forms, Vcl.Dialogs, Vcl.StdCtrls;
type
TFConn = class(TForm)
Button1: TButton;
procedure Button1Click(Sender: TObject);
private
{ Déclarations privées }
public
{ Déclarations publiques }
end;
var
FConn: TFConn;
implementation
{$R *.dfm}
uses MainForm;
procedure TFConn.Button1Click(Sender: TObject);
begin
if not Assigned(FMain) then
begin
FMain := TFMain.CreateNew(Application);
FMain.OnClose := FMain.FormClose;
FMain.ShowModal;
end;
end;
end.
2 - MainForm unit :
unit MainForm;
interface
uses
Winapi.Windows, Winapi.Messages, System.SysUtils, System.Variants, System.Classes, Vcl.Graphics,
Vcl.Controls, Vcl.Forms, Vcl.Dialogs, Vcl.StdCtrls;
type
TFMain = class(TForm)
Constructor FormCreate(Sender: TObject);overload;
procedure FormClose(Sender: TObject; var Action: TCloseAction);
end;
Var
FMain : TFMain;
implementation
Constructor TFMain.FormCreate(Sender: TObject);
var B : TButton;
begin
inherited;
B := TButton.Create(Self);
B.Parent := Self;
B.Caption := 'Button2';
end;
procedure tfmain.FormClose(Sender: TObject; var Action: TCloseAction);
begin
FMain := Nil;
end;
end.
The problem is that the procedure FormCreate in MainForm unit not working , I know that I miss something in the declaration , because the procedure should fire while the creation of the FMain form .
The application run without any error , but it should create B button on the FMain form.
How can I do that?
Constructor FormCreate(Sender: TObject);overload;
is wrong. It should be:
procedure FormCreate(Sender: TObject);
The other problem is that you must set the OnCreate event to refer to FormCreate. Do this in the events page of the object inspector.
That will also need you to have a dfm file for this form which you appear not to have. Once you restore the dfm file you can set the OnClose event handler in the same way. You'd need to switch to Create rather than CreateNew.
You can't set the OnCreate event handler in code because the form has already been created.
If there is a good reason not to have a dfm file and do everything in code then you could need to add a constructor. Do that by overriding the virtual constructor:
constructor Create(AOwner: TComponent); override;
Finally, very much of the code in the question looks dubious, shall I saw. Strange use of global variables. Odd naming. Setting important events like OnClose from outside the class. Testing against nil that suggests weak design. I think there are problems ahead.

How to instantiate different frame types?

I'm here again with frames.
I've this main form:
It's only a simple form created in order to understand the use of frames.
With the two buttons on the top of the form I would like to open this two frames:
Frame1
and Frame2
here is the code the simple code of the first frame:
unit AppFrame1;
interface
uses
Winapi.Windows, Winapi.Messages, System.SysUtils, System.Variants,
System.Classes, Vcl.Graphics, Vcl.Controls, Vcl.Forms, Vcl.Dialogs,
Vcl.StdCtrls;
type
TFrame1 = class(TFrame)
lblFrame1: TLabel;
private
{ Private declarations }
public
{ Public declarations }
end;
implementation
{$R *.dfm}
end.
and here is the code of the second frame:
unit AppFrame2;
interface
uses
Winapi.Windows, Winapi.Messages, System.SysUtils, System.Variants,
System.Classes, Vcl.Graphics, Vcl.Controls, Vcl.Forms, Vcl.Dialogs,
Vcl.StdCtrls;
type
TFrame2 = class(TFrame)
lblFrame2: TLabel;
private
{ Private declarations }
public
{ Public declarations }
end;
implementation
{$R *.dfm}
end.
So nothing special in the two frames.
in order to open the frames from the main form I've created an interface like this:
unit FramesManager;
interface
uses
Vcl.Forms, Vcl.StdCtrls, Vcl.ExtCtrls, Vcl.Controls;
type
TFrameClass = class(TFrame)
end;
IFrameManager = interface
['{A00E0D1B-3438-4DC4-9794-702E8302B567}']
procedure CreateGenericFrame(AParentPanel: TPanel; AFrameClass: TFrameClass);
procedure DestroyGenericFrame();
end;
TFrameManager = class(TInterfacedObject, IFrameManager)
private
FGenericFrame: TFrameClass;
procedure CreateGenericFrame(AParentPanel: TPanel; AFrameClass: TFrameClass);
procedure DestroyGenericFrame();
public
property Frame: TFrameClass read FGenericFrame write FGenericFrame;
end;
implementation
{ TFrameManagement }
procedure TFrameManager.CreateGenericFrame(AParentPanel: TPanel;
AFrameClass: TFrameClass);
begin
FGenericFrame := AFrameClass.Create(AParentPanel);
FGenericFrame.Parent := AParentPanel;
FGenericFrame.Align := alClient;
end;
procedure TFrameManager.DestroyGenericFrame;
begin
FGenericFrame.Free;
end;
end.
At this point my intension is to use the interface for create the two frames but I can't realize how to reach this task.
My main form code is this:
unit Main;
interface
uses
Winapi.Windows, Winapi.Messages, System.SysUtils, System.Variants,
System.Classes, Vcl.Graphics, Vcl.Controls, Vcl.Forms, Vcl.Dialogs,
Vcl.ExtCtrls, FramesManager, Vcl.StdCtrls, AppFrame1, AppFrame2;
type
TfrmMain = class(TForm)
pnlCommands: TPanel;
pnlFrames: TPanel;
btnCrtFrame1: TButton;
btnCrtFrame2: TButton;
procedure FormCreate(Sender: TObject);
procedure btnCrtFrame1Click(Sender: TObject);
procedure btnCrtFrame2Click(Sender: TObject);
private
FFrame: IFrameManager;
public
{ Public declarations }
end;
var
frmMain: TfrmMain;
implementation
{$R *.dfm}
procedure TfrmMain.FormCreate(Sender: TObject);
begin
FFrame := TFrameManager.Create;
end;
procedure TfrmMain.btnCrtFrame1Click(Sender: TObject);
begin
FFrame.CreateGenericFrame(pnlFrames, TFrame1);
end;
procedure TfrmMain.btnCrtFrame2Click(Sender: TObject);
begin
FFrame.CreateGenericFrame(pnlFrames, TFrame2);
end;
end.
When I try co compile the project I receive this errors:
[dcc32 Error] Main.pas(41): E2010 Incompatible types: 'TFrameClass' and 'class of TFrame1'
[dcc32 Error] Main.pas(46): E2010 Incompatible types: 'TFrameClass' and 'class of TFrame2'
So I would like to understand how to create the two frames from the main.
How can I assign the right object type to the TFrameClass?
I've tought about generics, but I have not idea on how to implement this kind of interface in order to open a "generic" frame that can be created form the main when the use choose to open it.
I hope I have explained clearly my problem, but I know that it may seem complicate to understand.
type
TFrameClass = class(TFrame)
end;
Your TFrameClass declaration is wrong. Right now it is declared as a descendant of TFrame which is just another class.
What you need is a class reference:
type
TFrameClass = class of TFrame;
Because both TFrame1 and TFrame2 descend from TFrame, both can be put into a TFrameClass variable.
But I am pretty sure this TFrameClass already exists somewhere in the VCL in which case you do not have to redeclare this type.
Subsequently, the type of FGenericFrame private field needs to become TFrame instead of TFrameClass.
(Also, this has nothing to do with Generics.)

Delphi fire frame events from datamodule

I've an application made by a main form, a frame and a datamodule.
When the main form is created I create also the frame wich contains the fields present in a table that are in the datamodule.
So the main form code is this:
unit main;
interface
uses
Winapi.Windows, Winapi.Messages, System.SysUtils, System.Variants,
System.Classes, Vcl.Graphics, Vcl.Controls, Vcl.Forms, Vcl.Dialogs,
FraDtl;
type
TfrmMain = class(TForm)
procedure FormCreate(Sender: TObject);
private
{ Private declarations }
FFraDtl: TfraDetail;
public
{ Public declarations }
end;
var
frmMain: TfrmMain;
implementation
{$R *.dfm}
procedure TfrmMain.FormCreate(Sender: TObject);
begin
FFraDtl := TfraDetail.Create(Self);
FFraDtl.Parent := Self;
end;
end.
The code of the frame is this:
unit FraDtl;
interface
uses
Winapi.Windows, Winapi.Messages, System.SysUtils, System.Variants,
System.Classes, Vcl.Graphics, Vcl.Controls, Vcl.Forms, Vcl.Dialogs,
database, cxGraphics, cxControls, cxLookAndFeels, cxLookAndFeelPainters,
cxContainer, cxEdit, cxTextEdit, cxDBEdit, Vcl.StdCtrls, dxSkinsCore,
cxNavigator, cxDBNavigator;
type
TfraDetail = class(TFrame)
lblCognome: TLabel;
edtCognome: TcxDBTextEdit;
lblNome: TLabel;
edtNome: TcxDBTextEdit;
cxDBNavigator1: TcxDBNavigator;
private
{ Private declarations }
public
{ Public declarations }
end;
implementation
{$R *.dfm}
end.
And the code of the datamodule is this:
unit database;
interface
uses
System.SysUtils, System.Classes, Data.DB, DBAccess, MSAccess, MemDS,
dxSkinsCore, cxClasses, cxLookAndFeels, dxSkinsForm;
type
TdmData = class(TDataModule)
Connection: TMSConnection;
tblAutori: TMSTable;
dsAutori: TMSDataSource;
fAut_IdAutore: TIntegerField;
fAut_Cognome: TStringField;
fAut_Nome: TStringField;
fAut_Nominativo: TStringField;
SkinController: TdxSkinController;
procedure DataModuleCreate(Sender: TObject);
procedure DataModuleDestroy(Sender: TObject);
procedure dsAutoriStateChange(Sender: TObject);
private
{ Private declarations }
public
{ Public declarations }
end;
var
dmData: TdmData;
implementation
{$R *.dfm}
procedure TdmData.DataModuleCreate(Sender: TObject);
begin
tblAutori.Open;
end;
procedure TdmData.DataModuleDestroy(Sender: TObject);
begin
tblAutori.Close;
end;
procedure TdmData.dsAutoriStateChange(Sender: TObject);
begin
// Some stuff
end;
end.
I would like to call a procedure present in the frame in the dsAutoriStateChange event is fired. There is a way to do this?
I would like to call a procedure present in the frame in the dsAutoriStateChange event is fired.
You can do this using code like this:
unit database;
....
implementation
uses FraDtl;
procedure TdmData.dsAutoriStateChange(Sender: TObject);
begin
FraDtl.SomeProcedure;
end;
Or if you want to access the actual instantiated frame you access the main unit.
unit database;
....
implementation
uses main; //circular reference allowed in implementation
procedure TdmData.dsAutoriStateChange(Sender: TObject);
begin
frmMain.FrameDetail.DoSomething(Sender);
end;
In that case you need to add an public property to frmMain exposing the frame.
TfrmMain = class(TForm)
...
public
property FrameDetail: TFraDetail read FFraDtl;
Warning
These kinds of hard coupling between data module and form are considered bad programming practice.
Loose coupling is good
Hard coupling is bad
It would be better to use an event handler for a datasource inside the mainform, or if that's not possible to add additional properties to TdmData that get updated by the main form as needed.
The event handler dsAutoriStateChange can then access these properties instead of having to poke around in the private parts of the frame.

Get TAdvEdit.Text directly from procedure/function

Hello i get error E2197: [DCC Error] proj1.pas(34): E2197 Constant object cannot be passed as var parameter:
unit proj1;
interface
uses
Winapi.Windows, Winapi.Messages, System.SysUtils, System.Variants, System.Classes, Vcl.Graphics,
Vcl.Controls, Vcl.Forms, Vcl.Dialogs, Vcl.StdCtrls, AdvEdit;
type
TForm1 = class(TForm)
AdvEdit1: TAdvEdit;
Button1: TButton;
procedure Button1Click(Sender: TObject);
private
{ Private declarations }
public
{ Public declarations }
end;
var
Form1: TForm1;
implementation
{$R *.dfm}
procedure SetEditText(const instr: string; out outstr: string);
begin
outstr := instr;
end;
procedure TForm1.Button1Click(Sender: TObject);
begin
SetEditText('Pippo', AdvEdit1.Text);
end;
end.
Of course, i can solve writing:
procedure TForm1.Button1Click(Sender: TObject);
var sText: string
begin
SetEditText('Pippo', sText);
AdvEdit1.Text := sText;
end;
But when i have many AdvEdit, then it is hard. Then i ask, is possible solve the problem in some mode giving directly TAdvEdit.Text as parameter in mine procedure?
Thanks very much.
I presume that Text is a property. And you cannot pass a property to a var or out parameter. You can only pass variables to parameters of those kinds.
You'll need to find a different way to write your code. You've come up with one such idea, but it seems needlessly complex to me. I cannot see anything simpler than:
AdvEdit1.Text := 'Pippo';
How could there be any code simpler than this? You need to specify at a bare minimum the following:
The target control.
That we are dealing with the Text property.
The fact that we are assigning.
The new value.
The code above does that and nothing more.

Resources