Delphi: Why A class variable affects the result of the method? - delphi

This is a full simplification of my code, but even so it doesn't work when class var ID_COUNTER is there, in this code I don't use the class var, but in my real code yes, but just the existence of this class variable
makes the result of 's' different. This is the most weird I have ever seen.
Here is a simplification, but still doesn't Works, one Unit in 75 lines.
unit Umain;
interface
uses
Winapi.Windows, Winapi.Messages, System.SysUtils, System.Variants, System.Classes, Vcl.Graphics,
Vcl.Controls, Vcl.Forms, Vcl.Dialogs, Vcl.StdCtrls,XMLIntf,XmlDoc,IOUtils,XMLDom,System.Generics.Collections;
type
TStore = class
public
class var ID_COUNTER: Integer;
MainNode: IDomNode;
constructor create(node:IDomNode);
function getNode():IDomNode;
end;
TForm1 = class(TForm)
Button1: TButton;
procedure Button1Click(Sender: TObject);
private
{ Private declarations }
public
FMain: TStore;
function Recursive(node:IDomNode):TStore;
end;
var
Form1: TForm1;
implementation
{$R *.dfm}
procedure TForm1.Button1Click(Sender: TObject);
var
Doc:IXMLDocument;
content: WideString;
html: IDomNode;
s: String;
begin
Doc := TXMLDocument.Create(Application);
Doc.LoadFromFile('C:\temp\example.xml');
Doc.Active := true;
html := Doc.DOMDocument.getElementsByTagName('html').item[0];
FMain := Recursive(html);
s := FMain.getNode().nodeName;
end;
function TForm1.Recursive(node: IDOMNode):TStore;
var
i: Integer;
store: TStore;
nodeName,nodeValue:String;
begin
store := TStore.create(node);
if(not node.hasChildNodes)then
Exit(store);
for i := 0 to node.childNodes.length-1 do
begin
Recursive(node.childNodes.item[i]);
end;
Exit(store);
end;
constructor TStore.create(node: IDOMNode);
begin
self.MainNode := node;
end;
function TStore.getNode:IDomNode;
begin
Result := self.MainNode;
end;
end.
Some notes:
example.xml is only a simple HTML document.
Everything is broken when ID_COUNTER exists, if it is commented, everything is Ok. It happens here and in my real and wide Project.

The problem is that, syntactically, class var introduces a class field block rather than a single class field, meaning that if you use class var, all following field declarations in the same visibility section will be class variables too. So now, MainNode becomes a class variable too and that probably causes the problems you encounter. Reformatting your code shows this a little more clearly:
public
class var
ID_COUNT: Integer;
MainNode: IDomNode;
constructor Create(... etc.
Your options are:
move ID_COUNT one line down:
public
MainNode: IDomNode;
class var ID_COUNTER: Integer;
constructor Create(... etc.
create a special section for MainNode:
public
class var ID_COUNTER: Integer;
public
MainNode: IDomNode;
constructor Create(... etc.
preface MainNode with the var keyword (which, likewise, introduces a block, specifically an instance field block within the current visibility section):
public
class var
ID_COUNTER: Integer;
// any other class variables
var
MainNode: IDomNode;
// any other instance variables
constructor Create(... etc.

Related

Delphi error while returning TList

I have made a very simple application but I have an issue that I really cannot understand. Look at this basic code:
unit Unit1;
interface
uses
Winapi.Windows, Winapi.Messages, System.SysUtils, System.Variants, System.Classes, Vcl.Graphics,
Vcl.Controls, Vcl.Forms, Vcl.Dialogs, generics.collections, Vcl.StdCtrls;
type
TForm1 = class(TForm)
Button1: TButton;
procedure Button1Click(Sender: TObject);
procedure FormCreate(Sender: TObject);
procedure FormDestroy(Sender: TObject);
private
{ Private declarations }
test: TList<integer>;
aList: TList<integer>;
public
{ Public declarations }
function testGenerics: TList<integer>;
end;
var
Form1: TForm1;
implementation
{$R *.dfm}
procedure TForm1.Button1Click(Sender: TObject);
begin
test := testGenerics;
test.Sort;
showmessage(test[0].tostring);
end;
procedure TForm1.FormCreate(Sender: TObject);
begin
test := TList<integer>.Create;
aList := TList<integer>.Create;
end;
procedure TForm1.FormDestroy(Sender: TObject);
begin
aList.Free;
test.Free;
end;
function TForm1.testGenerics: TList<integer>;
begin
aList.Add(4);
result := aList;
end;
end.
Basically when the Form opens I am going to create test and aList and then when I press the button the function testGenerics is called. Why do I have the Invalid pointer operation error?
I really cannot understand since I am creating and destroying the objects (I guess) properly. This code instead works fine:
function TForm1.testGenerics: TList<integer>;
begin
Result := TList<integer>.Create;
Result.Add(4);
end;
In this case I am returning an instance of TList<integer> but also in the case above I am returning an instance of aList (which is a TList).
If I'm correct in the first case test := testGenerics is like test := aList (because I am returning aList in fact) so I am going to give test the same reference as aList. Am I correct?
In the first example, whenever you call testGenerics(), you are re-assigning test to point at the aList object. You are losing track of the original test object created in the OnCreate event, so it is leaked. And then in the OnDestroy event, when you call test.Free, it crashes because you already freed the aList object beforehand, so you are trying to free the same object a second time, which is an invalid operation.
In the second example, you are still leaking the original test object (and every TList you allocate and assign to test, except for the last one), but you are not re-assigning test to point at the aList object anymore, so there is no crash in the OnDestroy event because both variables are pointing at separate objects.
What are you trying to accomplish in the first place? Returning objects in this manner is not good practice. Nor does it make sense to call Sort() on 1-element lists.
If you are trying to populate test with multiple values over time, you should pass test as an input parameter to testGenerics() (or just let testGenerics() access test directly via Self), don't use the return value at all.
And in any case, get rid of your aList private member, as you are not doing anything with it anyway.
Try this:
unit Unit1;
interface
uses
Winapi.Windows, Winapi.Messages, System.SysUtils, System.Variants, System.Classes, Vcl.Graphics,
Vcl.Controls, Vcl.Forms, Vcl.Dialogs, generics.collections, Vcl.StdCtrls;
type
TForm1 = class(TForm)
Button1: TButton;
procedure Button1Click(Sender: TObject);
procedure FormCreate(Sender: TObject);
procedure FormDestroy(Sender: TObject);
private
{ Private declarations }
test: TList<integer>;
public
{ Public declarations }
procedure testGenerics(aList: TList<integer>);
end;
var
Form1: TForm1;
implementation
{$R *.dfm}
procedure TForm1.Button1Click(Sender: TObject);
begin
testGenerics(test);
test.Sort;
ShowMessage(test[0].tostring);
end;
procedure TForm1.FormCreate(Sender: TObject);
begin
test := TList<integer>.Create;
end;
procedure TForm1.FormDestroy(Sender: TObject);
begin
test.Free;
end;
procedure TForm1.testGenerics(aList: TList<integer>);
begin
// FYI, a better way to exercise Sort()
// would be to use RandomRange() instead
// of a hard-coded number...
aList.Add(4);
end;
end.

Dynamically assigning anonymous generic functions in pascal

I have the following class hierarchy
I would like to be able to dynamically assign anonymous methods which operate on objects of both types TB and TC.
So here is a simple contrived example:
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;
Button2: TButton;
procedure Button1Click(Sender: TObject);
procedure Button2Click(Sender: TObject);
private
{ Private declarations }
public
{ Public declarations }
end;
TNotifyEventWrapper = class
private
FProc: TProc<TObject>;
public
constructor Create(Proc: TProc<TObject>);
published
procedure Event(Sender: TObject);
end;
IA = interface
procedure Foo;
end;
TA = class(TInterfacedObject)
procedure Foo;
end;
TB = class(TA, IA)
procedure Foo;
end;
TC = class(TA, IA)
procedure Foo;
end;
TControl = class
strict private
public
class var NEW : TNotifyEventWrapper;
class var Foo : TNotifyEvent;
class function GetWrapper<T:TA, IA, constructor>(D: T): TNotifyEventWrapper;
class procedure AssignFooHandler<T:TA, IA, constructor>;
end;
var
Form1: TForm1;
implementation
{$R *.dfm}
procedure TC.Foo;
begin
ShowMessage('TC.Foo');
end;
class function TControl.GetWrapper<T>(D: T): TNotifyEventWrapper;
begin
Result :=
TNotifyEventWrapper.Create
(
procedure (S : TObject)
begin
T(D).Foo;
end
);
end;
class procedure TControl.AssignFooHandler<T>;
var
X : T;
begin
X := T.Create;
try
TControl.NEW := TControl.GetWrapper<T>(X);
TControl.Foo := TControl.NEW.Event;
finally
FreeAndNil(X);
end;
end;
procedure TA.Foo;
begin
ShowMessage('TA.Foo');
end;
procedure TB.Foo;
begin
ShowMessage('TB.Foo');
end;
constructor TNotifyEventWrapper.Create(Proc: TProc<TObject>);
begin
inherited Create;
FProc := Proc;
end;
procedure TNotifyEventWrapper.Event(Sender: TObject);
begin
FProc(Sender);
end;
procedure TForm1.Button1Click(Sender: TObject);
begin
TControl.Foo(Sender);
end;
procedure TForm1.Button2Click(Sender: TObject);
begin
TControl.AssignFooHandler<TC>; //TB
end;
end.
I would like to be able to call
TControl.AssignFooHandler<TC>;
And have the TControl.Foo(Sender); method invoke TC.Foo
Also I want TControl.AssignFooHandler<TB>; to result in TControl.Foo(Sender); invoking TB.Foo
Unfortunately, when I run this, it always invokes the base class method TA.Foo.
I'm not sure how to get around this.
Your Generic is constrained to descendants of TA and IA. TA.Foo is not declared as virtual, and T(B|C).Foo() are not declared to override it. That is why TA.Foo() is being called every time. You need to make TA.Foo() virtual and T(B|C).Foo override it, then T(B/C).Foo will get called as expected.
Also, you are freeing the T(A/B/C) object that you are passing to TControl.GetWrapper() before TControl.Foo() ever gets a chance to invoke the Foo() method of that object. In this particular example, it is OK since none of the Foo() methods access any object member fields, but once you start doing that in actual production code, it is likely to crash. You need to keep the T(A/B/C) object alive until you are done using the TNotifyEventWrapper object.

generic compare function for 2 class types

here come my definition of Vertex class and graph class using generic programming features of Delphi :
unit Unit1;
interface
uses
Winapi.Windows, Winapi.Messages, System.SysUtils, System.Variants,
System.Classes, Vcl.Graphics,
Vcl.Controls, Vcl.Forms, Vcl.Dialogs,
System.Math, System.Generics.Collections,
System.Generics.Defaults, Vcl.StdCtrls;
type
tvertex = class(TObject)
name: string;
function markme: tvertex;
function Compare(const v: TVertex): Integer;
constructor create;
destructor free;
end;
tvertex<T> = class(tvertex)
Userdata: T;
end;
TGraph <T : class > = class (Tobject)
vertexlist : TObjectList<T>;
procedure CompareLists(
var _V1: TObjectList<T>;
var _V2: TObjectList<T>);
end;
TForm1 = class(TForm)
Edit1: TEdit;
private
{ Private-Deklarationen }
public
{ Public-Deklarationen }
end;
var
Form1: TForm1;
// helper function
function createVertexComparer(): IComparer <TVertex >;
implementation
// helper functions
function createVertexComparer(): IComparer<TVertex>;
begin
Result := TDelegatedComparer<TVertex>.Create(
function(const Left, Right: TVertex): Integer
begin
Result := Left.Compare(Right);
end);
end;
{$R *.dfm}
{ tvertex }
function tvertex.Compare(const v: TVertex): Integer;
begin
// ...
end;
constructor tvertex.create;
begin
// ...
end;
destructor tvertex.free;
begin
// ...
end;
function tvertex.markme: tvertex;
begin
// ...
end;
procedure TGraph<T>.CompareLists(
var _V1: TObjectList<T>;
var _V2: TObjectList<T>);
begin
_V1 := TObjectList<T>.Create(createVertexComparer(), False); /// line which does not compile ....
end;
end.
How to modify the code that he is willing to accept TVertex and TVertex<T> class types as arguments ....
I would say that the main problem that you have is that you declared the graph class like this:
type
TGraph<T: class>
...
end;
And this means that the compiler will accept any class as T. Consequently the graph class knows nothing about T, beyond that it is a class.
It's hard to be sure, but I think that you intend T to be a vertex. So you need to constraint the graph class appropriately.
type
TGraph<T: TVertex>
...
end;
And then you have another problem with this function:
function createVertexComparer(): IComparer<TVertex>;
You pass the result of that to
TObjectList<T>.Create
But that expects a parameter of type IComparer<T> and you are supplying IComparer<TVertex>. That's the type mismatch that the compiler reports.
You'll need to make createVertexComparer be a method of TGraph<T> so that it can be generic. Its implementation would be:
function TGraph<T>.createVertexComparer(): IComparer<T>;
begin
Result := TDelegatedComparer<T>.Create(
function(const Left, Right: T): Integer
begin
Result := Left.Compare(Right);
end);
end;
Also, do note that
destructor free;
is a disaster waiting to happen. You must use
destructor Destroy; override;
In fact, the rest of your code troubles me. For instance:
procedure TGraph<T>.CompareLists(var _V1, _V2: TObjectList<T>);
begin
_V1 := TObjectList<T>.Create(createVertexComparer(), False);
end;
The method's name does not match what it does. It only returns one value, so why have two var parameters? It's very hard to discern intent when viewing code like this.

Delphi Class Parameter

I am trying to compose my own ThreadManager unit in Delphi and I have this so far:
unit uThreadManager;
interface
uses
Classes,
Windows;
type
TCustomTThread = class (TThread)
public
TaskData : Pointer;
end;
type
TWorkerThread = class(TObject)
private
TaskDataList : TList;
TaskDataListCrit : TRTLCriticalSection;
function ReadTotalTasks : Integer;
public
constructor Create;
destructor Destroy; override;
property TotalTasks : Integer read ReadTotalTasks;
function AddTask(Thread: TCustomTThread; Data: Pointer) : Integer;
procedure Delete (Index : Integer);
end;
implementation
type
PTaskData = ^TTaskData;
TTaskData = record
Thread : TCustomTThread;
TaskPointer : Pointer;
end;
procedure TWorkerThread.Delete(Index: Integer);
var
TaskData : PTaskData;
begin
EnterCriticalSection(TaskDataListCrit);
TaskData := TaskDataList.Items[Index];
TaskDataList.Delete(Index);
LeaveCriticalSection(TaskDataListCrit);
TaskData^.Thread.Free;
Dispose(TaskData);
end;
function TWorkerThread.ReadTotalTasks;
begin
EnterCriticalSection(TaskDataListCrit);
result := TaskDataList.Count;
LeaveCriticalSection(TaskDataListCrit);
end;
destructor TWorkerThread.Destroy;
begin
DeleteCriticalSection(TaskDataListCrit);
TaskDataList.Free;
inherited;
end;
constructor TWorkerThread.Create;
begin
inherited;
InitializeCriticalSection(TaskDataListCrit);
TaskDataList := TList.Create;
end;
function TWorkerThread.AddTask(Thread: TCustomTThread; Data: Pointer) : Integer;
var
NewTask : PTaskData;
begin
EnterCriticalSection(TaskDataListCrit);
New(NewTask);
// I would like to create a new instance of TCustomTThread here!
//NewTask^.Thread := ...
NewTask^.TaskPointer := Data;
result := TaskDataList.Add (NewTask);
LeaveCriticalSection(TaskDataListCrit);
end;
end.
I came across the problem with the parameter from my AddTask procedure...
Here is an example on what I am trying to do:
unit Unit2;
interface
uses
Winapi.Windows, Winapi.Messages, System.SysUtils, System.Variants, System.Classes, Vcl.Graphics,
Vcl.Controls, Vcl.Forms, Vcl.Dialogs, Vcl.StdCtrls, uThreadManager;
type
TForm2 = class(TForm)
Button1: TButton;
procedure Button1Click(Sender: TObject);
private
{ Private declarations }
public
{ Public declarations }
end;
var
Form2: TForm2;
type
TTheCustomThread = class (TCustomTThread)
public
procedure Execute; override;
end;
implementation
{$R *.dfm}
procedure TTheCustomThread.Execute;
begin
// My Code
end;
procedure TForm2.Button1Click(Sender: TObject);
var
NewWorkerThread : TWorkerThread;
begin
NewWorkerThread := TWorkerThread.Create;
NewWorkerThread.AddTask(TTheCustomThread, NIL);
end;
end.
this code gives me the error:
[dcc32 Error] Unit2.pas(42): E2010 Incompatible types:
'TCustomTThread' and 'class of TTheCustomThread'
I could fix this by declaring a new TTheCustomThread var in the stack but I would like to avoid this cause I won't need it at all later and AddTask will create a new instance of TTheCustomThread. I could use TClass and then typecast to TCustomThread but I was wondering if there's anything else to make this work.
Thank you for your help.
Your function AddTask is defined as so:
function AddTask(Thread: TCustomTThread; Data: Pointer) : Integer;
The first parameter that you pass is of type TCustomTThread. That is an instance of TCustomTThread.
You call the function like this:
AddTask(TTheCustomThread, nil);
Here you pass the class rather than an instance. Hence the compiler error.
Now, it seems that what you want to do is pass the class. Inside AddTask you wish to receive a class and then create a new instance. Declare a class type like so:
type
TTheCustomThreadClass = class of TTheCustomThread;
Change AddTask to receive that type:
function AddTask(ThreadClass: TCustomTThreadClass; Data: Pointer) : Integer;
And inside the implementation, create an instance like this:
NewTask^.Thread := ThreadClass.Create;
Very likely you will want to declare the constructor of TTheCustomThread to be virtual to allow derived classes the freedom to define constructors that can be executed from your factory creation mechanism.

Delphi 7: an abstract class through VFI

Is it possible in Delphi 7 to create an abstract class that can be inherited through the Visual Form Inheritance technique? If so, please, provide an example.
The task is this. I want to create a form that will serve as a base form for two other forms that will inherit all the properties of that form. The two inheriting forms will be used for adding new stuff to the database (creating a product item, for example) and editing that stuff. So, I guess the base form should be thought of as an abstract class that should have okay and cancel buttons and things like that which all inheriting classes will share. Well, it is obviously an abstract class, because there is no other use of the form other than being a form to base other forms on.
Here's a simple diagram to make the point clearer:
First of all we need to define what abstract class means. There appear to me to be two competing definitions:
An abstract class is one that cannot be instantiated. This is the most commonly used definition.
An abstract class is one that contains more than one abstract method.
Since Delphi no language mechanism for enforcing definition 1, it would appear that definition 2 is the definition that applies to this question.
And the answer to the question is that classes that contain abstract methods can be used with Visual Form Inheritance.
Modern versions of Delphi do allow you to decorate classes with the abstract keyword. However, this has no effect. You can still instantiate such a class. It is my understanding that the abstract keyword was added for the benefit of the Delphi .net compiler.
Again, in modern versions of Delphi, you can configure the compiler to treat instantiation of classes with abstract methods as a compilation error. That's probably the closest you can get in Delphi to definition 1.
However, even that does not fully adhere to definition 1 since those classes can be instantiated through RTTI or virtual constructors. And the mechanism by which a designed component is instantiated is a perfect example.
Take this class for example:
type
TForm1 = class(TForm)
public
procedure Boo; virtual; abstract;
end;
Even if you set the option for W2000 Constructing instance containing abstract method to Error, you can still let the framework instantiate the class. It's only if you write TForm1.Create that the compiler objects.
You use the term abstract, but reading your question, I seriously doubt you really mean it the way abstract within Delphi is defined. I think you mean the term abstract in the general plain spoken way: you want to design a form with parts that have to be altered or added by descendants. Abstract methods in Delphi mean class routines without implementation. It does not matter though, because it is perfectly possible to design a base form, with or without abstract methods.
You can create a setup as shown in your picture/diagram as follows:
Design a form TBaseForm with 2 Edits, 2 Labels, 2 Buttons and 1 ActionList,
Add 3 actions to the ActionList: Create, Save and Cancel,
Assign the Cancel action to CancelButton.Action in advance,
Save the form,
Design a new form, inherited from TBaseForm, with the menu command: File > New > Other > [Project Name] > BaseForm
You will have a new form that has the edits, labels, buttons and actions,
Assign the Save action to the other button's action property,
Give it a "Edit Item" caption,
Save the form, and repeat it for the "Create New Item"-form.
The base form may have abstract methods, if you want to. When you create a TBaseForm instance at runtime, the compiler will give a warning constructing instance of 'TBaseForm' containing abstract method 'TBaseForm.MethodName'. It remains a warning, until you invoke the method at runtime which will produce an abstract error. Creating a descendant form which implements that method, then there will be no warning. Creating forms with abstract methods in the designer does not produce warnings. Runtime errors then may still occur though.
No, you cannot create an "abstract" base form in Delphi Visual Form Inheritance in the strict Delphi sense of the word "abstract".
However, from your description it doesn't sound like you actually need a strictly abstract base form. You don't mention a requirement for defining abstract methods at all.
It sounds like you just need a base form from which you create multiple differently specialized descendants that can share UI and implementation with the base form.
That's what VFI is for, so yes, you can do that.
I have Succeed to create a Demo with a BaseForm has Abstract Methodes and is work as charm ...
my IDE is Rad Studio RIO
my BaseForm code:
unit UBaseForm;
interface
uses
Winapi.Windows,
Winapi.Messages,
System.SysUtils,
System.Variants,
System.Classes,
Vcl.Graphics,
Vcl.Controls,
Vcl.Forms,
Vcl.Dialogs,
Vcl.ExtCtrls, Vcl.StdCtrls;
type
TBaseForm = class(TForm)
Lbl_IndexPage: TLabel;
procedure Abstracted_Event(Sender: TObject); virtual; abstract;
procedure Abstracted_Proc; virtual; abstract;
function Abstracted_Func: string; virtual; abstract;
private
{ Private declarations }
public
{ Public declarations }
end;
procedure Get_SubForm(var Ref; AFormClass: TFormClass;
aOwner: TComponent; aParent: TWinControl);
var
BaseForm: TBaseForm;
implementation
{$R *.dfm}
procedure Get_SubForm(var Ref; AFormClass: TFormClass;
aOwner: TComponent; aParent: TWinControl);
var
Instance: TBaseForm;
begin
if not Assigned(TBaseForm(Ref)) then
begin
Instance := TBaseForm(AFormClass.NewInstance);
TBaseForm(Ref) := Instance;
Instance.Create(aOwner);
end
else Instance := TBaseForm(Ref);
Instance.Parent := aParent;
Instance.Align := alClient;
Instance.BorderStyle := bsNone;
Instance.OnShow := Instance.Abstracted_Event;
Instance.Show;
end;
end.
in my APP i have three Forms can inherit all the properties of that BaseForm Above Even the Methodes ...
unit UFirstPage;
interface
uses
Winapi.Windows,
Winapi.Messages,
System.SysUtils,
System.Variants,
System.Classes,
Vcl.Graphics,
Vcl.Controls,
Vcl.Forms,
Vcl.Dialogs,
UBaseForm,
Vcl.StdCtrls, Vcl.ExtCtrls;
type
TFrmFirstPage = class(TBaseForm)
Pnl_1: TPanel;
Edt_Abst_Msg_Event: TEdit;
Pnl_2: TPanel;
Pnl_3: TPanel;
Btn_Get_Abstract_Func: TButton;
Btn_Get_Abstract_Proc: TButton;
procedure Btn_Get_Abstract_FuncClick(Sender: TObject);
procedure Btn_Get_Abstract_ProcClick(Sender: TObject);
published
procedure Abstracted_Event(Sender: TObject); override;
procedure Abstracted_Proc; override;
function Abstracted_Func: string; override;
private
{ Private declarations }
public
{ Public declarations }
end;
var
FrmFirstPage: TFrmFirstPage;
implementation
{$R *.dfm}
{ TFrmFirstPage }
{$REGION ' Overridden Abstract Methodes ..'}
procedure TFrmFirstPage.Abstracted_Event(Sender: TObject);
begin
inherited;
Pnl_1.Color := clBlue; Pnl_2.Color := clGray; Pnl_3.Color := clRed;
Edt_Abst_Msg_Event.Text := 'All this Properties can changed using [Abstracted_Event] | (Owner Form is: ['+ Self.ClassName +'])';
end;
function TFrmFirstPage.Abstracted_Func: string;
begin
Result := 'I''m Just an Override of Abstracted_Func ['+ Self.ClassName +']';
end;
procedure TFrmFirstPage.Abstracted_Proc;
begin
inherited;
ShowMessage('I''m Just an Override of Abstracted_Proc ['+ Self.ClassName +']');
end;
{$ENDREGION}
procedure TFrmFirstPage.Btn_Get_Abstract_FuncClick(Sender: TObject);
begin
ShowMessage(Abstracted_Func);
end;
procedure TFrmFirstPage.Btn_Get_Abstract_ProcClick(Sender: TObject);
begin
Abstracted_Proc;
end;
end.
my Second Form:
unit USecondPage;
interface
uses
Winapi.Windows,
Winapi.Messages,
System.SysUtils,
System.Variants,
System.Classes,
Vcl.Graphics,
Vcl.Controls,
Vcl.Forms,
Vcl.Dialogs,
UBaseForm,
Vcl.StdCtrls;
type
TFrmSecondPage = class(TBaseForm)
Lbl_Abst_Msg_Event: TLabel;
Lbl_1: TLabel;
Lbl_2: TLabel;
Lbl_3: TLabel;
Btn_Do_Abst_Proc: TButton;
Btn_Get_Abst_Func: TButton;
procedure Btn_Do_Abst_ProcClick(Sender: TObject);
procedure Btn_Get_Abst_FuncClick(Sender: TObject);
published
procedure Abstracted_Event(Sender: TObject); override;
procedure Abstracted_Proc; override;
function Abstracted_Func: string; override;
private
{ Private declarations }
public
{ Public declarations }
end;
var
FrmSecondPage: TFrmSecondPage;
implementation
{$R *.dfm}
{ TFrmSecondPage }
{$REGION ' Overridden Abstract Methodes ..'}
procedure TFrmSecondPage.Abstracted_Event(Sender: TObject);
begin
inherited;
Lbl_1.Font.Color := clBlue; Lbl_2.Font.Color := clGray; Lbl_3.Font.Color := clRed;
Lbl_Abst_Msg_Event.Caption := 'All this Properties can changed using [Abstracted_Event] | (Owner Form is: ['+ Self.ClassName +'])';
end;
function TFrmSecondPage.Abstracted_Func: string;
begin
Result := 'I''m Just an Override of Abstracted_Func ['+ Self.ClassName +']';
end;
procedure TFrmSecondPage.Abstracted_Proc;
begin
inherited;
ShowMessage('I''m Just an Override of Abstracted_Proc ['+ Self.ClassName +']');
end;
{$ENDREGION}
procedure TFrmSecondPage.Btn_Do_Abst_ProcClick(Sender: TObject);
begin
Abstracted_Proc;
end;
procedure TFrmSecondPage.Btn_Get_Abst_FuncClick(Sender: TObject);
begin
ShowMessage(Abstracted_Func);
end;
end.
my third Form:
unit UThirdPage;
interface
uses
Winapi.Windows,
Winapi.Messages,
System.SysUtils,
System.Variants,
System.Classes,
Vcl.Graphics,
Vcl.Controls,
Vcl.Forms,
Vcl.Dialogs,
UBaseForm,
Vcl.StdCtrls;
type
TFrmThirdPage = class(TBaseForm)
published
procedure Abstracted_Event(Sender: TObject); override;
procedure Abstracted_Proc; override;
function Abstracted_Func: string; override;
private
{ Private declarations }
public
{ Public declarations }
end;
var
FrmThirdPage: TFrmThirdPage;
implementation
{$R *.dfm}
{ TFrmThirdPage }
{$REGION ' Overridden Abstract Methodes ..'}
procedure TFrmThirdPage.Abstracted_Event(Sender: TObject);
begin
inherited;
// your Code Goes Here ..
// call this methode or fill it with code Not a Mandatory :)
// The Mandatory thing is to implement this Methodes Exactly where BASEFORM HAS & without Missing any one of them from the Base Class...
// Enjoy ...
end;
function TFrmThirdPage.Abstracted_Func: string;
begin
// your Code Goes Here ..
// call this methode or fill it with code Not a Mandatory :)
// Enjoy ...
end;
procedure TFrmThirdPage.Abstracted_Proc;
begin
inherited;
// your Code Goes Here ..
// call this methode or fill it with code Not a Mandatory :)
// Enjoy ...
end;
{$ENDREGION}
end.
my Main Form:
unit UMain;
interface
uses
Winapi.Windows,
Winapi.Messages,
System.SysUtils,
System.Variants,
System.Classes,
Vcl.Graphics,
Vcl.Controls,
Vcl.Forms,
Vcl.Dialogs,
Vcl.StdCtrls,
Vcl.ExtCtrls,
// My Abstracted Views ..
UFirstPage,
USecondPage,
UThirdPage;
type
TFrmMain = class(TForm)
Pnl_ToolBar: TPanel;
Pnl_StatusBar: TPanel;
Btn_Previous: TButton;
Btn_Next: TButton;
Notebook_SubForms: TNotebook;
Pnl_First_PAGE: TPanel;
Pnl_Second_PAGE: TPanel;
Pnl_Third_PAGE: TPanel;
procedure FormCreate(Sender: TObject);
procedure Notebook_SubFormsPageChanged(Sender: TObject);
procedure Btn_NextClick(Sender: TObject);
procedure Btn_PreviousClick(Sender: TObject);
private
{ Private declarations }
public
{ Public declarations }
end;
var
FrmMain: TFrmMain;
implementation
uses
// My Abstracted Base Template ..
UBaseForm;
{$R *.dfm}
procedure TFrmMain.Btn_NextClick(Sender: TObject);
begin
case Notebook_SubForms.PageIndex of
0:begin
Notebook_SubForms.PageIndex := 1;
end;
1:begin
Notebook_SubForms.PageIndex := 2;
end;
2:begin
Notebook_SubForms.PageIndex := 0;
end;
end;
end;
procedure TFrmMain.Btn_PreviousClick(Sender: TObject);
begin
case Notebook_SubForms.PageIndex of
0:begin
Notebook_SubForms.PageIndex := 2;
end;
1:begin
Notebook_SubForms.PageIndex := 0;
end;
2:begin
Notebook_SubForms.PageIndex := 1;
end;
end;
end;
procedure TFrmMain.FormCreate(Sender: TObject);
begin
Get_SubForm(FrmFirstPage, TFrmFirstPage, Self, Pnl_First_PAGE);
end;
procedure TFrmMain.Notebook_SubFormsPageChanged(Sender: TObject);
begin
case Notebook_SubForms.PageIndex of
0:begin
Get_SubForm(FrmFirstPage, TFrmFirstPage, Self, Pnl_First_PAGE);
if Assigned(FrmSecondPage) then FreeAndNil(FrmSecondPage);
if Assigned(FrmThirdPage) then FreeAndNil(FrmThirdPage);
end;
1:begin
Get_SubForm(FrmSecondPage, TFrmSecondPage, Self, Pnl_Second_PAGE);
if Assigned(FrmFirstPage) then FreeAndNil(FrmFirstPage);
if Assigned(FrmThirdPage) then FreeAndNil(FrmThirdPage);
end;
2:begin
Get_SubForm(FrmThirdPage, TFrmThirdPage, Self, Pnl_Third_PAGE);
if Assigned(FrmSecondPage) then FreeAndNil(FrmSecondPage);
if Assigned(FrmSecondPage) then FreeAndNil(FrmSecondPage);
end;
end;
end;
end.
When Compile 0 error and 0 Warning..
Result:
link to download the whole demo from my Github Repo here.
the Demo can work Also in Delphi 7 (i test it) :)

Resources