---- 1. Tips for using tree controls in Delphi
---- We all know that developers mainly use Delphi to develop database management software. Because of this, the use of tree controls is best linked to the database. Delphi provides a tree control TTreeView, which can be used to describe complex hierarchical relationships.
---- 1. Storage and loading of tree node information
---- Commonly used methods are to use the LoadFromFile and SavetoFile methods of the tree control to realize the interaction between the tree control and the file; or to use the Assign method to realize the interaction between the tree control and DBMemo, that is, the interaction with the database. The advantage of this method is that programming is relatively simple. The disadvantage is that the actual number of nodes of the tree control may be very large. For "big trees", the amount of data loaded and stored each time will increase, which will reduce the speed, increase system overhead, and cause Data redundancy. Another method is to only generate "visible" nodes in the tree. There is no file or database field dedicated to recording the entire tree node structure, and the tree node structure is dispersed in each record of the database.
---- The specific method is: create a database, the fields are determined according to the actual business, there must be one field whose information will be displayed on the node of the tree control, and there is also a field to save the unique identification number of the node. The identification number consists of two parts of equal length. The first part represents the parent node number of the current node, and the latter part represents the node number of the current node. This identification number is equivalent to a "linked list" that records the structure of the nodes on the tree. Advantages of this method: When users operate a "big tree", they generally do not expand all the nodes, but only use a limited part. At the same time, they can only expand layer by layer from the root of the tree. This method only generates "" on the tree. "Visible" nodes, therefore, the speed of storing and loading "big trees" is fast, the amount of data is small, and the system overhead and data redundancy are small. Disadvantages: Programming is more complicated, but this method can be combined to form a new tree control, which will greatly improve programming efficiency. It is worth noting that the ID number must be unique, so how to reasonably generate the ID in programming is particularly important.
---- 2. Database structure example
----Create a database. To simplify the procedure, I only create two database fields, defined as follows:
Field name type length
TextC10
LongIDC6
----The LongID field actually consists of two segments, each segment is 3 digits. LongID can only represent 1000 records. Define LongID as an index field and save it as c: esttree ree.dbf. Edit the DBF file, create a new record, set the Text field to TOP, and set the LongID field to "000" (three spaces before three "0"s).
---- 3. Create a demo program
---- Place TreeView1, Table1, PopupMenu1, Edit1, and Edit2 on Form1. The PopupMenu attribute of TreeView1 is set to PopupMenu1; the DataBaseName attribute of Table1 is set to c: esttree, the TableName attribute is set to tree.dbf, and the IndexFieldNames attribute is set to LongID; add single items Add1 and Del1 to PopupMenu1, and Caption is Add and Del respectively; Edit1 uses To enter the Text attribute value of the new node, Edit2 is used to enter the 3-digit ID number of the new node. Save as c:esttree reeunit.pas and c:esttree esttree.dPR. Add a line after the Type keyword in treeunit.pas: Pstr:^string;{Pstr is a string pointer} Add code for the OnCreate event of Form1:
procedure TForm1.FormCreate(Sender: TObject);
var p:Pstr;Node:TTreeNode;
begin
with Table1,Treeview1 do
begin
open;
first;
new(p);{Allocate memory for pointer p}
p^:=FieldByName(′LongID′).AsString;
Node:=Items.AddChildObject(nil,FieldByName
(′Text′).AsString,p);
if HasSubInDbf(Node) then Items
.AddChildObject(Node,′ ′,nil);{If there is a child node, add an empty child node}
end;
end;
---- HasSubInDbf is a custom function, the independent variable is Node, check whether the node Node has child nodes, return True if there are, otherwise return False, and add a prototype declaration to the class definition of TForm1 (the prototypes of other custom functions are also Declared in the class definition of TForm1 without further explanation), the function code is as follows:
function TForm1.HasSubInDbf(Node:TTreeNode):Boolean;
begin
with Table1 do
begin
Table1.FindNearest([copy(Pstr(Node.Data)^,4,3)+′000′]);
result:=copy(FieldByName(′LongID′).
AsString,1,3)=copy(Pstr(Node.Data)^,4,3);
{For example, the sum of the first three digits of the LongID field content of the current record in the database
If the last three digits of the Data of the node Node are the same, then the Node should have child nodes}
end;
end;
Add code for the OnDeletion event of the TreeView1 control. It should be pointed out that,
Not only can the OnDeletion event be triggered by calling the Delete method, but also before the tree control itself is released,
The OnDeletion event is also triggered, so it is "safe" to add dispose(node.data) here:
procedure TForm1.TreeView1Deletion
(Sender: TObject; Node: TTreeNode);
begin
Dispose(Node.Data);{Release node data memory}
end;
Add the following code to the OnClick event of the Add1 menu item:
procedure TForm1.Add1Click(Sender: TObject);
var p:pstr;Tmpstr:string;i:integer;
begin
try
StrToInt(Edit2.Text);
Tmpstr:=Edit2.Text;{Note: In practice, a better method must be used to generate ID}
except;
ShowMessage('Re-enter the content of Edit2');
abort;
end;
with TreeView1 do
begin
new(p);
p^:=copy(Pstr(Selected.Data)^,4,3)+TmpStr;
Items.AddChildObject(Selected,Edit1.Text,p);
end;
with Table1 do{Add record in database}
begin
Append;
FieldByName(′Text′).AsString:=Edit1.text;
FieldByName(′LongID′).AsString:=p^;
Post;
end;
TmpStr:=inttostr(strtoint(TmpStr)+1);
for i:=length(TmpStr) to 2 do TmpStr:=′0′+TmpStr;
Edit2.Text:=TmpStr;
end;
Add the following code to the OnClick event of the Del1 menu item:
procedure TForm1.Del1Click(Sender: TObject);
var DelList:TStringList;LongID,NSubLongID:string;
begin
DelList:=TStringList.create;
DelList.Sorted:=True;
DelList.Add(Pstr(TreeView1.Selected.Data)^);
while DelList.Count>0 do
begin
LongID:=DelList.Strings[0];
DelList.Delete(0);
Table1.SetKey;
Table1.FieldByName(′LongID′).AsString:=LongID;
if Table1.GotoKey then Table1.Delete;
if HasSubInDbf(TreeView1.Selected) then
begin
NSubLongID:=Table1.FieldByName(′LongID′).AsString;
while (copy(NSubLongID,1,3)=copy
(LongID,4,3))and(not Table1.Eof) do
begin
dellist.Add(NSubLongId);
Table1.Next;
NSubLongId:=Table1.FieldByName(′LongID′).AsString;
end;
end;
end;
DelList.Free;
TreeView1.Items.Delete(TreeView1.Selected);
end;
Add code for the OnExpanding event of TreeView1:
procedure TForm1.TreeView1Expanding
(Sender: TObject; Node: TTreeNode;
var AllowExpansion: Boolean);
var TmpNode:TTreeNode;NSubLongID:
String;p:Pstr;bm:TBookMark;
begin
with Table1,TreeView1 do
begin
Items.BeginUpdate;
SetKey;
FieldByName(′LongID′).AsString:=Pstr(Node.Data)^;
if not GotoKey then Items.Delete(Node)
else
begin
TmpNode:=Node.GetFirstChild;
if (TmpNode.Text=′ ′)and(TmpNode.Data=nil) then
begin
TmpNode.Delete;
if HasSubInDbf(Node) then
begin
NSubLongID:=FieldByName(′LongID′).AsString;
while (copy(NSubLongID,1,3)=copy(Pstr
(Node.Data)^,4,3))and(not Eof) do
begin
new(p);
p^:=FieldByName(′LongID′).AsString;
bm:=GetBookMark;
TmpNode:=Items.AddChildObject(Node,
FieldByName(′Text′).AsString,p);
if HasSubInDbf(TmpNode) then Items.
AddChildObject(TmpNode,′ ′,nil);
GotoBookMark(bm);
FreeBookMark(bm);
Next;
NSubLongId:=FieldByName(′LongID′).AsString;
end; end; end;
end;
Items.EndUpdate;
end;
end;
---- The above briefly talks about the basic method of tree display of the database. In addition, when editing the Text attribute of the node in the tree, the database is modified at the same time. When the same database is operated by multiple users at the same time, the consistency of the database and the tree, and the tree The copy and replication of the upper node will not be described in detail, and readers can improve it by themselves.
---- 2. Use of ip control
---- In network programs, we often encounter situations where users are required to enter IP addresses. However, Delphi does not provide us with a control that can be used to enter the IP string, so we have to use the Tedit control (single-line text box) to accept the IP string entered by the user. However, using Tedit to enter an IP string is not a good idea as it is very inconvenient to handle. In fact, there is a Windows control beside us specifically for entering IP strings. The IP control will reject illegal IP strings (only numbers between 0..255 can be entered in each part); it allows you to easily obtain the IP value (32-bit integer) corresponding to the IP string in the control. This This saves you the trouble of converting between IP strings and IP values; in addition, you can also limit the range of IPs that can be entered in the IP control. This section introduces how to use Windows IP controls in our Delphi program.
---- There are two very important dynamic link libraries in Windows: commctrl.dll and comctl32.dll, which are Windows' custom control libraries (Windows Common Controls). The custom control library contains many commonly used Windows controls, such as Statusbar, Coolbar, HotKey, etc.; in Delphi, most of these controls have been packaged as visual controls. After Microsoft launched Internet Explorer 3, some new controls were added to the custom control library, including the Windows IP control (IP Address edit control).
---- 1. Initialize Windows custom control library
---- Windows provides two API functions, InitCommonControls and InitCommonControlsEx, for initializing custom control libraries. From the names, it is not difficult to see the relationship between these two API functions: the latter is an enhancement of the former. If you want to use IP controls in your program, you must use InitCommonControlsEx to complete the initialization of custom control libraries and classes. The prototype of the function InitCommonControlsEx is as follows (Pascal syntax):
... ...
Create IP control
... ...
Use IP controls. In the program, we communicate with the IP control by sending messages to it.
The IP control can respond to the following six messages. These messages and their meanings are shown in the table below:
... ...
If you want to get the IP value corresponding to the IP string in the IP control, you should send
IPM_GETADDRESS message, and requires a 32-bit integer address as
The last parameter of SendMessage.
... ...
---- 2. Notification message of IP control
---- When the IP string is changed or the input focus is transferred, the IP control will send the notification message IPN_FIELDCHANGED to its parent window. In most cases, we can ignore this notification message. The following is an example of handling the notification message IPN_FIELDCHANGED:
procedure Tform1.WndProc(var Msg: TMessage);
var p:PNMHDR;
begin
inherited;
if Msg.Msg=WM_NOTIFY
then begin
p:=Pointer(Msg.lParam);
if p^.code=IPN_FIELDCHANGED
then begin
{…
Handling the IPN_FIELDCHANGED notification message of the IP control
…}
end;
end;
end;
---- 3. Methods and applications of dynamically generating controls
---- 1.Two methods to generate controls in Delphi
---- (1). Generate controls in Form design
---- When designing a Form, it is common to directly select the required control in the control toolbox, then set its properties and respond to events.
---- (2). Dynamically generate controls in the program
---- Sometimes, we need to dynamically generate controls when the program is running. This has two major advantages: first, it can increase the flexibility of the program; second, if the number of generated controls is related to the intermediate running results of the program, obviously method one It cannot be realized, and the dynamic generation method in the program must be used.
---- The method of dynamically generating controls in the program is divided into three steps. First, define the type of generated control, then use the Create function to generate the control, and finally assign values to the relevant properties of the control. Taking the TButton control as an example, the steps are as follows:
---- a. Define control type
var
Button1:TButton;
---- b. Generate controls
Button1:=TButton.Create(self);
Button1.Parent:=Self;
//Generally, set its parent control to Self. If the value of Parent is not set,
then the control will not be on the screen
//display it
---- c. Set other properties and define related event response functions, such as Caption, Left, Top, Height, Width, Visible, Enabled, Hint and onClick event response functions, etc.
---- 2. Application of dynamically generated control method
---- In the development of production scheduling and management systems, it is necessary to dynamically generate production scheduling charts, represented by Gantt charts, and it is very useful to use Shape controls to display the processing status of parts (the processing start time and end time of each process). Suitable. Using the Chart control, the processing equipment utilization is displayed in a three-dimensional histogram, which is very intuitive. Now we will explain the process of dynamically generating Shape control and Chart control in the program.
---- (1). Dynamically generate Shape control to display production scheduling plan chart (Gantt chart)
procedure TCreateMultiCharts.ProcCreateCharts;
var
i,j,Rows,Columns,RowSpace,ChartsHeight:Integer;
ShapeChart:array of array of TShape;
begin
Rows:=16; //The number of rows in the Shape control array
Columns:=8; //Shape control array column number
RowSpace:=20; // Shape control row spacing
ChartsHeight:=20; // Shape control height
SetLength(ShapeChart,Rows,Columns);
//Set the ShapeChart array size
for i:=0 to Rows do
for j:=0 to Columns do
begin
ShapeChart[i][j]:=TShape.Create(self);
with ShapeChart[i,j] do
begin
Parent:=Self; //This line is essential,
Otherwise, the Shape control will not be displayed on the screen.
Shape:=stRectangle; // Shape control shape is rectangle
Top:=45+i*(RowSpace+ChartsHeight);
Left:=Round(180+Q[i,j].StartTime);
//Because Q[i,j].StartTime is a real number, it needs to be rounded.
Width:=Round(Q[i,j].Value)
Height:=ChartsHeight;
Brush.Color:=RandomColor;
//Custom function, instructions are attached
Brush.Style:=bsSolid; //Set the filling method
Enabled:=True;
end;
end;
end;
---- Note: aQ is a record-type two-dimensional array, defined as follows:
type
TempData=Record
Value:Real;
StartTime:Real;
end;
Q:array of array of TempData
And the components of Q have been assigned values in another process.
---- b. In order to distinguish different parts, Shape is displayed in different colors. At this time, the function RandomColor is called. The function is:
function TCreateMultiCharts.RandomColor;
var
red,green,blue:byte;
begin
red:=random(255);
green:=random(255);
blue:=random(255);
result:=red or (green shl 8) or (blue shl 16);
end;
---- (2). Dynamically generate the ChartSeries component of the Charts control to display device utilization
procedure TFormMultiMachinesBurthen.
ShowMachineBurthenCharts;
var
i:Integer;
Burthen:Real;
SeriesClass:TChartSeriesClass;
NewSeries:array of TChartSeries;
begin
SetLength(NewSeries,CreateMultiCharts.Rows);
MachinesBurthenCharts.height:=200;
MachinesBurthenCharts.Width:=550;
for i:=0 to CreateMultiCharts.Rows do
begin
SeriesClass:=TBarSeries; //Set the shape to a three-dimensional bar chart
NewSeries[i]:=SeriesClass.Create(Self);
NewSeries[i].ParentChart:=MachinesBurthenCharts;
NewSeries[i].Clear;
Burthen:=MachineBurthen[i];
Burthen:=Round(Burthen*100)/100; //Only take two digits after the decimal point
NewSeries[i].add(Burthen,',NewSeries[i].SeriesColor);
end;
end;
---- Note: (a).MachineBurthen[i] is a real array, its value is the utilization of the corresponding device, which has been calculated in another function;
---- (b). MachinesBurthenCharts is a TChart control, described in the type section.
---- 3. Program running result display
---- (1). Dynamically generate a Shape control to display the parts scheduling plan (omitted)
---- (2). Dynamically generate the ChartSeries component of the Chart control and display the device utilization (omitted)