Delphi -- skills exploration:
{No. 1}
Sentences to create modal forms:
class PRocedure TMyForm.RunForm(AObj1, AObj2: TObject);
var
vForm: TMyForm;
begin
vForm := TMyForm.Create(application);
with vForm do
Try
InitForm(AObj1, AObj2);
ShowModal;
Finally
Free;
end;
end;
//*illustrate:
Functions declared through class are similar to static functions in VC; use statement: TMyForm.RunForm(vObj1, vObj2);
For other specific information, please refer to: class description in Delphi Help.
The purpose of emphasizing this idiom is to:
1. If this form is used in multiple places, you can ensure that this code is called uniformly;
2. If the function is modified, for example: processing based on the return value of ShowModal, then only modify this function.
3. The program has good encapsulation and is easy to maintain and work handover. *//
{No. 2}//Usage of Tag
Response to form toolbar button events
procedure TMyForm.RunOperate(ATag: Integer);
begin
Case ATag of
1: MyButton.Color := clRed;
2: MyButton.Color := clGreen;
3: MyButton.Color := clBlack;
end;
end;
procedure TMyForm.ToolBtnClick(Sender: TObject);
begin
RunOperate(TControl(Sender).Tag);
end;
If you are in a drop-down menu and need to perform similar functions,
procedure TMyForm.MenuItemClick(Sender: TObject);
begin
RunOperate(TMenuItem(Sender).Tag);
end;
//*illustrate:
1. Clear structure
2. Relevant information is concentrated, making it easier to check, modify and maintain.
3. Improve the adaptability and scalability of the program; for example, if the requirement is not implemented in the toolbar button, but implemented in different buttons, it will be easy to modify.
Suggestion: Each category is followed by only one line or a few lines of code. If there is a lot of code, use a process function instead.
What’s more interesting is that I often write as follows:
Case btnMyButton.Visible of
{show} True: ...
{Do not show} False: ...
end; *//
{ No. 3 }//Event pointer as parameter
//Use the event pointer for reading lists, etc.
type
TDataSetEvent = procedure (DataSet: TDataSet; AIndex, ACount: Integer) of Object;
//Classes derived from TADOQuery
procedure TMyADOQuery.EnumRecord(AWhereStr: String; APro: TDataSetEvent);
begin
Close;
SQL.Clear;
SQL.Add('Select * From Table1');
if AWhereStr <> '' then
SQL.Add('Where ' + AWhereStr);
Open;
While Not Eof Do
begin
if Assigned(APro) then APro(Self, RecNo, RecordCount);
Next;
end;
Close;
end;
//*illustrate:
This method comes from the API function in Window to enumerate all current child windows, EnumChildWindow
1. Principle: Try to separate data reading from data display, data processing, etc.; such as: MVC, etc. are all for this purpose.
2. The scalability of the program has been enhanced. If you originally wanted to display or process a certain column of information in a list, but later changed to using ComboBox, when modifying the program, you do not need to read the data reading part, you only need to modify the information display, etc. For another example, you are now required to use a progress bar to display the reading progress when reading records.
*//
{ No. 4 }//constant array
{In No.2, the following content is implemented
procedure TMyForm.RunOperate(ATag: Integer);
begin
Case ATag of
1: MyButton.Color := clRed;
2: MyButton.Color := clGreen;
3: MyButton.Color := clBlack;
end;
end;
}
//Then it would be more ideal to implement it using an array.
procedure TMyForm.RunOperate(ATag: Integer);
const
MyButtonColorMax := 3;
MyButtonColor: array [1..MyButtonColorMax] of TColor = (clRed, clGreen, clBlack);
begin
Case ATag of
1..MyButtonColorMax: MyButton.Color := MyButtonColor[ATag];
101:....
end;
end;
//*illustrate:
For use in array mode, just note that the upper or lower limit of the array is implemented using constants, and then try to use this constant for array loop reading in future uses.
*//
{No. 5}Message mechanism reduces class public functions
//How to minimize the definition of public functions in a form;
{For example: If you want to implement a property list form of the current form control, when you need to refresh the property form; change the value of a certain property; add new properties, etc.; there will be a lot of information that needs to be interacted with. If we use class public functions, we need to define many public functions. At the same time, if form type conversion is required, public functions can be used only after converting to the target form type. Therefore, there will be situations where two units need to include each other}
//Solution:
TfrmMyForm = class(TForm)
FfrmProperty: TForm;
end;
...
FfrmProperty := TfrmProperty.MyCreate(Application, Self);
...
//When the property window needs to be refreshed
FfrmProperty.Perform(WD_REFRESHPROPERTYLIST, 0, 0);
TfrmProperty = class(TForm)
private
FMyForm: TForm;
procedure WDREFRESHPROPERTYLIST(var Message: TMessage); message WD_REFRESHPROPERTYLIST;
public
constructor MyCreate(Owner: TComponent; AForm: TForm);
end;
constructor TfrmProperty.MyCreate(Owner: TComponent; AForm: TForm);
begin
inherited Create(Owner);
FMyForm := AForm;
end;
//* For the way of using messages, the definition of public functions of the form can be reduced. At the same time, it improves the scalability of the program. If you use his form instead, you can convert easily, because at most it is your form, which does not process the current message*)//
{No. 6}Use registration list to manage possible expansion modules
//Project: You are required to support multiple output display methods for a data set
...Examples will be given later
//* illustrate:
1. "Multiple output methods" indicates that the output methods may be frequently expanded in future applications, so the easy scalability of the output methods must be taken into consideration during program design.
2. Referring to the control registration (RegisterComponents) mechanism in VCL, you can find that a large number of registration mechanisms are used in VCL; the more classic one is the registration of the control property editor.
*//
{No.7}Use predefined control program version
//If you are doing a secondary development platform program, you must involve product version control and project version control issues
//Usually controlled using predefined methods
//The statement is relatively simple:
{$DEFINE JOYYUAN97}
{$IFDEF JOYYUAN97} {ELSE} {ENDIF}
{$UNDEF JOYYUAN97}
*illustrate:
1. Divide the predefinition into multiple separate files.
2. At the beginning of each unit but after Unit, use {$I ...} to include the file into the current unit.
3. Control the unit files that the current unit can contain based on predefined conditions
4. Try to separate a predefined file for the project. After including all the predefined files, include this file. In this file, some predefined files can be canceled according to the needs of the project. {$UNDEF JOYYUAN97}
*//
{No. 8}Use function pointers to reduce unit project inclusions
//I often think that reducing unit inclusion is the first step in making public units, so how to reduce unit inclusion as much as possible
//That is, more efforts should be made on how to reduce the coupling of program units.
{Scenario description:
TMyFormManager: Form management class
TMyForm: Data form base class
TMyFormaccess: Form information saving and reading class. Save the form information to a database or some other type of structure
analyze:
1. The form base class (TMyForm) and the form management class (TMyFormManager) need to be implemented in a unit uManagers.
2. The form specific implementation class (TMyImageForm) unit fMyImange needs to include the unit uManagers for form inheritance and form management.
3. The form data reading class (TMyFormAccess) unit uMyAccess needs to include the unit uManagers and the unit fMyImange
question:
If I want to save the form, it should be done in a button event of the form. When it comes to the form unit, it needs to contain the form data access class unit, and if it is placed in the form base class, the unit uManager must contain the unit uMyAccess.
Cell inclusion is a hazard when data access, i.e. data storage formats, change based on requirements and require scalability.
Solution: Use function pointer variables.
1. Define a function pointer variable in the unit uManagers to save data information.
2. Assign a value to this function pointer variable when the application is initialized.
3. When it is necessary to save the form information, determine if the pointer is not empty, and then execute the function to save the form information.
{No. 9}Constants, understand constants, use constants
There are many books that introduce the importance of constant definition, and I often think of it, but when I look at the VCL source code, I realize that I have ignored the use of constants by others.
1. The definition of a message we often use is: declare a constant and then use it at the appropriate time.
Commonly defined and used:
const
WD_MyMessage = WM_User + 101;
type
TMyForm = class(TForm)
...
procedure WDMyMessage(var message: TMessage); message WD_MyMessage; {response message position}
end;
However, if you rewrite the {response message position} statement as:
procedure WDMyMessage(var message: TMessage); message WM_User + 101;
Similarly, the compilation can be successful and the use is normal. Therefore, constant definitions are very commonly used in Window system processing and interfaces.
2. In Delphi, we have defined color variables, clRed, clGreen, etc., which are also defined constants to facilitate future use. Through this observation, I found that the definition of constants should be partially reusable in the project. Therefore, a standard constant unit can be defined so that the defined constants can be reused in each project.
{No. 10}A commonly used array in Delphi
There is a relatively complete implementation in Delphi for the definition and use of arrays of the TIdentMapEntryd type.
TIdentMapEntry = record
Value: Integer;
Name: String;
end;
1. Array definition: array[0..ArrMax] of TIdentMapEntry
Please refer to: Controls unit:
Cursors: array[0..21] of TIdentMapEntry = (
...
);
2. Two functions that evaluate each other: IntToIdent (find Name from Value) and IdentToInt (find Value from Name);
For specific applications, please refer to: IdentToCursor and CursorToIdent.
3. Application: a. Directly apply this tree group definition method and array manipulation function; b. Learn how to access and manipulate arrays in the function. c. Learn the standard information access function definition: function IntToIdent(Int: Longint; var Ident: string; const Map: array of TIdentMapEntry): Boolean; The specific returned information is returned by parameters. As for whether the access is valid, it is passed through the function The Boolean return value is judged.
{No. 11}From special cases to common discoveries
I discovered through tracing the definition and operation functions of Cursors:
1. As introduced in {No. 10}, generalize the definition and general operations of Cursors.
2. Provide functions for conversion between Int and Ident.
3. Provides a function for reading array list information cyclically: GetCursorValues; among them, the method of "event pointer as parameter" introduced in {No. 3} is used to read list information.
Supplement to {No. 6}:
example:
procedure RegisterComponents(const Page: string;
ComponentClasses: array of TComponentClass);
begin
if Assigned(RegisterComponentsProc) then
RegisterComponentsProc(Page, ComponentClasses)
else
raise EComponentError.CreateRes(@SRegisterError);
end;
Interpretation:
1. Use the registration method to record the types of controls that can be used, etc.
3. For RegisterComponentsProc, the method of "using function pointers to reduce the inclusion of unit projects" in {No. 8} is used to facilitate future program expansion, version upgrades, etc.
{No. 11}Only define one public function
//Project description: Now we need to implement a CAD drawing or Visio system, which requires good scalability and easy maintenance;
//And it requires low coupling, so that parts of the system or the expanded system can be packaged and used directly in future projects.
design:
1. Design a graphical object abstract class. In this class, define an abstract function CadPerform. The parameters of the function refer to function TControl.Perform(Msg: Cardinal; WParam, LParam: Longint): Longint;
2. In the graphics management class, implement the management of a graphics object list. What is stored in the list is the pointer of the abstract object.
3. When you want to control specific class objects, you only need to use the CanPerform function, and then pass in Msg according to the category of the current operation, and pass in the corresponding parameter information.
Implementation: TCad is the first-level control class inherited from the abstract class
function TCad.CadPerform(Msg: Cardinal; WParam, LParam: Longint): Longint;
begin
Case Msg of
My_Message1: Result := MyMessage1(WParam, LParam);
My_Message2: Result := MyMessage2(WParam, LParam);
end;
end;
For, TPoint inherits from TCad, the CadPerform function is implemented as follows.
function TPoint.CadPerform(Msg: Cardinal; WParam, LParam: Longint): Longint;
begin
Case Msg of
My_Message1: Result := MyMessage1(WParam, LParam); //The processing of this operation type in TCad is blocked
My_Message3: Result := MyMessage3(WParam, LParam);
else Result := inherited CadPerform(Msg, WParam, LParam);
end;
end;
*illustrate:
Because we operate graphics objects very frequently, we define a public and open interface function to achieve high encapsulation of the class and easy maintenance and expansion of the program.
*//
{No. 12}
The following are my programming requirements: (Some information has no language restrictions)
//Almost all of the following solutions can be found in the above methods.
1. Reduce the complexity of the program. a. Reduce the number of functions, use Case and Tag methods, and learn how to define Perform; b. Reduce unit nesting relationships, use message passing methods, and reduce the mutual inclusion of form units.
2. Reduce
{No. 13} Use broadcasting to implement notifications from management classes to management list objects
//For the {No. 12} project description, when the properties or status of the drawing form control change, it is often necessary to notify all graphic objects and make corresponding changes.
//If only one broadcast function is defined, parent-child notification can be realized, which will also improve the reusability, scalability, ease of maintenance, etc. of the program and make the class structure clear.
//For example: 1. In Visio and MapInfo, if the scale (zoom ratio) of the current form changes, all currently displayed graphics objects need to be redrawn with the new scale. 2. When the default form font of the current form is changed, the text fonts of graphic objects that use the form font by default to display text information should also be changed accordingly.
//Solution, refer to the processing mechanism in TWinControl that notifies all sub-Controls when attributes or status changes:
procedure TWinControl.NotifyControls(Msg: Word);
var
Message: TMessage;
begin
Message.Msg := Msg;
Message.WParam := 0;
Message.LParam := 0;
Message.Result := 0;
Broadcast(Message);//Broadcast the current change message
end;
in:
procedure TWinControl.Broadcast(var Message);
var
I: Integer;
begin
for I := 0 to ControlCount - 1 do
begin
Controls[I].WindowProc(TMessage(Message));
//Change to: with TMessage(Message) do Cads[I].CadPerform(msg, WParam, LParam);
if TMessage(Message).Result <> 0 then Exit;
end;
end;
However, when we deal with graphics objects, we may directly call the CanPerform public function of Cads.
{No. 14}Create your objects dynamically when needed
For example: http://www.delphibbs.com/keylife/iblog_show.asp?xid=824
//************Option 2: Create a property form when needed
uses
...
fProperty;
type
TfrmMyMap = class
...
procedure OnfrmMyMapDestroy(Sender: TObject);
procedure OnMapGeoSelected(AGeo: TGeometry);
private
FfrmProperty: TfrmProperty;
procedure ShowPropertyForm(aVisible: Boolean);
public
end;
procedure TfrmMyMap.ShowPropertyForm(aVisible: Boolean);
begin
if Not Assigned(FfrmProperty) then FfrmProperty := TfrmProperty.Create(Application);
FfrmProperty.Visible := aVisible;
end;
procedure TfrmMyMap.OnfrmMyMapDestroy(Sender: TObject);
begin
if Assigned(FfrmProperty) then FfrmProperty.Free;
end;
procedure TfrmMyMap.OnMapGeoSelected(AGeo: TGeometry);
begin
if Assigned(FfrmProperty) then FfrmProperty.MyRefresh(AGeo);
end;
Here it is stated:
1. Dynamically create your object FfrmProperty when needed
2. When the current object is released, determine the legality of your object, and then release the dynamically created object.
{No. 15}Create an interface or create a structure
//Project description: When I develop a table control, if I set the cell to a Com, if there is too much information in the table, the loading speed cannot be guaranteed, and there is even a possibility of a crash. The reason I use Com is so that in the future the processing and information of each cell can be expanded outside the control.
My solution is: create an instance for each control derived from Cell, and dynamically create several structure objects Record to record the information of each cell. If you need to operate the cell, assign the structure object pointer to Cell component, the test results are very satisfactory.
Therefore, if you need to use a large number of instances of a Com, try to manage and maintain one instance, and the data in it can be dynamically created and managed, which will have a good effect in terms of speed.
In addition, try to declare a pMyInterface = ^IMyInterface interface pointer. When passing or using parameters, use the interface pointer directly. This can reduce the number of calls to the counting function _AddInft, etc., and can also increase the speed if the operation is ordinary.