2. Double-simple combination
2.1. T[x]edit Components with T[x]label
Please allow me to use [x] notation to represent different objects. Because there will be a lot of references to Tlabelededit and Ttntlabelededit, Tcustomlabelededit and Ttntcustomlabelededit and their similar classes.
2.2. Analyzing the T[x]labelededit component structure
Now to design a tlabelededit-like component, view Extctrls's tlabelededit definition as follows:
Tlabelededit = Class (Tcustomlabelededit)
Tlabelededit inherit from Tcustomlabelededit and open attributes, the Tcustomlabelededit section is defined as follows:
Tcustomlabelededit = Class (Tcustomedit)
Private
Feditlabel:tboundlabel;
Public
Property Editlabel:tboundlabel read Feditlabel;
As you can see, Tcustomlabelededit inherits from Tcustomedit and constructs a Tboundlabel object.
The Tboundlabel object is defined as follows:
Tboundlabel = Class (Tcustomlabel)
Tboundlabel and Tlabel are inherited from the Tcustomlabel, both of which are traced back to the source, is a brotherly relationship.
The source of Tlabelededit is Tcustomedit and Tcustomlabel, and Ttntlabelededit is the source of Ttntcustomedit and Ttntcustomlabel. The combination of T[x]customedit and T[x]customlabel formed the t[x]customlabelededit.
Here, T[x]customedit and T[x]customlabel are like husbands and wives, and the two and T[x]customlabelededit are like parent-child relationships.
The previous chapter designed Tgcxcustomedit and Tgcxcustomintedit can replace T[x]customedit, and now we need a new T[x]boundlabel object.
2.3. Design Tgcxcustomlabel and Tgcxboundlabel
2.3.1. Design Tgcxcustomlabel
For simplicity, we first simplify the Tgcxcustomlabel design and derive directly from the Ttntcustomlabel without making any changes.
Tgcxcustomlabel = Class (Ttntcustomlabel)
End
2.3.2. From Tboundlabel, Ttntboundlabel to Tgcxboundlabel
Part of the definition of Tboundlabel:
Tboundlabel = Class (Tcustomlabel)
Private
function Gettop:integer;
function Getleft:integer;
function Getwidth:integer;
function Getheight:integer;
Procedure SetHeight (const value:integer);
Procedure SetWidth (const value:integer);
Protected
Procedure Adjustbounds; Override
Public
Constructor Create (aowner:tcomponent); Override
Part of the definition of Ttntboundlabel:
Ttntboundlabel = Class (Ttntcustomlabel)
Private
function Gettop:integer;
function Getleft:integer;
function Getwidth:integer;
function Getheight:integer;
Procedure SetHeight (const value:integer);
Procedure SetWidth (const value:integer);
Protected
Procedure Adjustbounds; Override
Public
Constructor Create (aowner:tcomponent); Override
Then, the Tgcxboundlabel should be defined as follows:
Tgcxboundlabel = Class (Tgcxcustomlabel)
Private
function Gettop:integer;
function Getleft:integer;
function Getwidth:integer;
function Getheight:integer;
Procedure SetHeight (const value:integer);
Procedure SetWidth (const value:integer);
Protected
Procedure Adjustbounds; Override
Public
Constructor Create (aowner:tcomponent); Override
The code part basically plagiarism Ttntboundlabel, but adjustbounds method slightly difference, why? This requires reading the Tboundlabel.adjustbounds and Ttntboundlabel.adjustbounds code.
changes in 2.3.3.AdjustBounds
The Ttntboundlabel.adjustbounds code is as follows:
Procedure Ttntboundlabel.adjustbounds;
Begin
Inherited Adjustbounds;
If Owner is Ttntcustomlabelededit then
With Owner as Ttntcustomlabelededit do
Setlabelposition (labelposition);
End
As you can see, Ttntboundlabel checks whether its owner (owner) is Ttntcustomlabelededit and calls the owner's setlabelposition.
In this way, restricting the owner of the Ttntboundlabel must be ttntcustomlabelededit, limiting the scope of application of Ttntboundlabel, which is a flaw that violates the basic principles of OOP.
If you follow this design idea, when we want to bind T[x]boundlabel to other objects, we need to re-inherit from T[x]customlabel or T[x]boundlabel and rewrite the Adjustbounds method. This is a very bloated design idea that will lead to increased code and maintenance, which we do not want to see.
The Delphi class can only inherit from one base class, how do we change the situation? Yes, that's the interface.
The concept of the interface was first proposed by Microsoft from the idea of COM, and Delphi introduced and extended this part of the definition.
We can let the class have a base class, and have multiple interfaces, and use the Sysutils.supports function to determine if the class supports an interface.
Well, in order for Tgcxboundlabel to be able to serve more classes, our final code is modified as follows:
procedure Tgcxboundlabel.adjustbounds;
Begin
Inherited Adjustbounds;
If Supports (Owner, Iboundlabelowner) Then
With Owner as Iboundlabelowner do
Setlabelposition (getlabelposition);
End
This is originally the Setlabelposition method and LabelPosition property of the Ttntcustomlabelededit class, is modified to the Setlabelposition and Getlabelposition methods of the Iboundlabelowner interface.
2.3.4.IBoundLabelOwner Interface Definition
Because the member list memberlist of an interface definition can only include methods and properties. The domain is not allowed in the interface. Because there is no domain in the interface, both the read and write specifiers of the property must be methods.
Iboundlabelowner = interface
[' {0056aa66-ccd0-4d56-9555-2de908e89f8a} ']
function getlabelposition:tlabelposition;
Procedure Setlabelposition (const value:tlabelposition);
Property Labelposition:tlabelposition read Getlabelposition write setlabelposition;
End
This definition is simple, that is, two function method declarations, the interface is applied indirectly to Tgcxboundlabel.
2.3.5. Last important attribute bind
Defined as follows:
Tgcxboundlabel = Class (Tgcxcustomlabel)
Private
Fbind:twincontrol;
Protected
Procedure Setbind (Abind:twincontrol);
Public
Property Bind:twincontrol read Fbind;
The code is as follows:
Procedure Tgcxboundlabel.setbind (Abind:twincontrol);
Begin
Self.fbind: = Abind;
End
This attribute is not Tboundlabel and Ttntboundlabel, what does it do?
Referring to the Setlabelposition method of Tcustomlabelededit and Ttntcustomlabelededit, when calculating the position of the T[x]boundlabel object, the left, Top, Height, Width is based on the current object self, so there is some trouble. Why is it?
This is not an issue when T[x]boundlabel is constructed by T[x]customlabelededit and T[x]customlabelededit is the final component, and when T[x]customlabelededit is part of a new component, T [x] The position calculation of the Boundlabel will be very painful.
Refer to the T[x]customlabelededit.setparent method, since the T[x]customlabelededit object will copy its parent to t[x when it constructs T[x]boundlabel] Boundlabel's parent, which is why T[x]customlabelededit's height, width does not include the height, width of the t[x]boundlabel. T[x]customlabelededit is the T[x]boundlabel object that is managed and placed in its parent object.
Ttntcustomlabelededit and Tcustomlabelededit are treated like this here, and the code and its similarities.
So, we need a property to fix the location of Tgcxboundlabel, and the bind attribute is for this purpose. The description of the Setlabelposition method is described in the following Tgcxcustomlabelededit and Tgcxcustomintlabelededit, and for the description of the SetParent method, will be described in the following tgcxcustomvalueinfoedit.
2.4. Synthetic Tgcxcustom[x]labelededit
Tgcxcustomlabelededit, Tgcxcustomintlabelededit is modeled after the T[x]customlabelededit established. The former inherits from Tgcxcustomedit and is used for text input; the latter inherits from Tgcxcustomintedit and is used for numeric input.
Refer to the previous "changes in 2.1.2.3.AdjustBoundsand2.1.2.4IBoundLabelOwner Interface Definition"The appearances of Tgcxcustomlabelededit and Tgcxcustomintlabelededit are somewhat different, as defined below:
Tgcxcustomlabelededit = Class (Tgcxcustomedit, Iboundlabelowner)
Tgcxcustomintlabelededit = Class (Tgcxcustomintedit, Iboundlabelowner)
You can see that they have introduced an interface Iboundlabelowner.
The following definitions are basically consistent, the only thing that is slightly different from T[x]customlabelededit is the addition of the Getlabelposition method, which modifies the read definition portion of the LabelPosition property, as mentioned earlier, Not restated.
Private
{Private declarations}
Feditlabel:tgcxboundlabel;
Flabelposition:tlabelposition;
Flabelspacing:integer;
function getlabelposition:tlabelposition;
Procedure Setlabelposition (const value:tlabelposition);
Procedure setlabelspacing (const value:integer);
protected
{protected declarations}
procedure SetParent ( Aparent:twincontrol); Override
procedure Notification (acomponent:tcomponent;
operation:toperation); Override
Procedure SetName (const value:tcomponentname); override;
procedure cmvisiblechanged (var message:tmessage); Message cm_visiblechanged;
procedure cmenabledchanged (var message:tmessage); Message cm_enabledchanged;
procedure cmbidimodechanged (var message:tmessage); Message cm_bidimodechanged;
Public
{Public declarations}
Constructor Create (aowner:tcomponent); Override
Procedure SetBounds (Aleft:integer; Atop:integer;
Awidth:integer; Aheight:integer); Override
Procedure Setupinternallabel;
Property Editlabel:tgcxboundlabel read Feditlabel;
Property Labelposition:tlabelposition
Read Getlabelposition write setlabelposition default lpleft;
Property Labelspacing:integer
Read Flabelspacing write setlabelspacing default 3;
2.4.1.GetLabelPosition functions and Setlabelposition methods
Due to the introduction of the Iboundlabelowner interface, a new read function must be provided for the LabelPosition property, with the following code:
function TGcxCustomLabeledEdit.GetLabelPosition:TLabelPosition;
Begin
Result: = flabelposition;
End
The original setlabelposition code is very long and can only be calculated relative to itself (self) T[x]boundlabel position. Considering the difference between Tgcxboundlabel and T[x]boundlabel (the former will serve multiple classes), we will reorganize the original code and design a common method updatelabelposition. Finally, the Setlabelposition method is simplified as follows:
Procedure Tgcxcustomlabelededit.setlabelposition (
Const value:tlabelposition);
Begin
Flabelposition: = Value;
Updatelabelposition (self, Feditlabel, flabelposition, flabelspacing);
End
2.4.2. Public method Updatelabelposition
This method is derived from setlabelposition, the purpose of the extraction is to simplify the code of a single Tgcxboundlabel sub-object Tgcxcustom[x]labelededit, and the code maintenance effort.
Procedure Updatelabelposition (Aowner:twincontrol;
Aeditlabel:tcustomlabel;
Const newlabelposition:tlabelposition;
Const Alabelspacing:integer);
Var
P:tpoint;
Obj:twincontrol;
Begin
if Aeditlabel = nil then Exit;
obj: = Aowner;
if (Aeditlabel is Tgcxboundlabel) then
Begin
With (Aeditlabel as Tgcxboundlabel) do
If Assigned (Bind) Then
obj: = Bind;
End
If not Assigned (obj) Then
Exit;
with obj do
case newlabelposition of
lpabove:p: = Point (left, top-aeditlabel.height-alabelspacing);
lpbelow:p: = Point (left, Top + Height + alabelspacing);
Lpleft:p: = Point (left-aeditlabel.width-alabelspacing,
& nbsp; Top + (Height- Aeditlabel.height) Div 2));
Lpright:p: = Point (left + Width + alabelspacing,
Top + ((height-aeditlabel.height) Div 2));
end;
Aeditlabel.setbounds (p.x, P.Y, Aeditlabel.width , aeditlabel.height);
End;
in this code, with T[x]customlabelededit. Setlabelposition different places are as follows:
obj: = Aowner;
if (Aeditlabel is Tgcxboundlabel) then
begin
with (Aeditlabel as Tgcxboundlabel) d o
if Assigned (Bind) then
obj: = Bind;
END;
If not Assigned (obj) then
Exit;
With obj do
Because the type of Aeditlabel in this method is defined as Tcustomlabel, the code first checks whether Aeditlabel is Tgcxboundlabel, and then determines whether the Bind property is valid, if the above judgment is not true, The default Aowner object is selected and the position of the Aeditlabel is calculated based on the result of the decision.
2.5. A Perfect pair
After writing this paragraph, carefully check the code, found that Tgcxcustomlabelededit and Tgcxcustomintlabelededit code unexpectedly a touch. And Setlabelspacing, SetParent, Notification, SetName, cmvisiblechanged, cmenabledchanged, cmbidimodechanged, Create, SetBounds, Setupinternallabel and T[x]customlabelededit's code.
The original is so simple, if said T[x]customlabelededit is Zhou Botong's hands, Tgcxcustomlabelededit and tgcxcustomintlabelededit like small longnu and Yang, Yunlei and Zhang Maple's double swords.
On the gradual development of Delphi components (II.)--double-simple combination