VST实例(1)

vst是一个树形结构控件,是由若干节点“NODE”构成,每个节点可能有父节点(parent),兄弟节点,子节点,而所有的节点,构成节点树。

首先我们来看看节点的定义:

PVirtualNode = ^TVirtualNode;
TVirtualNode = packed record
  ChildCount: Cardinal;
  Index: Cardinal;
  NodeHeight: Word;
  States: TVirtualNodeStates;
  Align: Byte;
  CheckState: TCheckState;
  CheckType: TCheckType;
  Dummy: Byte;
  TotalCount: Cardinal;
  TotalHeight: Cardinal;
  FirstChild: PVirtualNode;
  LastChild: PVirtualNode;
  NextSibling: PVirtualNode;
  Parent: PVirtualNode;
  PrevSibling: PVirtualNode;
  Data: record;
end;

其中data:record就是我们要求VST帮我们管理的数据,所以我们首先应定义一个结构record,用于存储数据。而

TVirtualNode是VST自管理的结构,例如添加了多少子节点,其兄弟节点、父节点等等都是VST自行管理。
要让VST开始正常工作,
第一步:定义record
第二
步:让VST知道你的record的大小。

这里有两种途径:
1、在让VST工作前,例如onformcreate事件时,就告诉VST记录的大小;
2、在VST的onGetNodeDataSize事件中获得记录的大小(size)
第三步:添加节点
第四步:显示节点
下面我们以一个简单的程序来进行演示。我准备的霍尼韦尔的一个机载导航数据库,霍尼韦尔使用onenav来管理其机载导航数据库,数据库解码后在decode文件夹下会生成一个扩展名为bin的文件,实际上这个文件就是未加密的sqlite数据库文件,我们将使用里面的三个表:
airports 机场列表
CREATE TABLE Airports(
RecordIndex INT,
Key INT,
Ident TEXT,
RevCode INT,
ProceduresExist BOOL,
RunwaysExist BOOL,
SpeedLimitCoded BOOL,
TransAltValid BOOL,
TrueNorth BOOL,
Latitude REAL,
Longitude REAL,
MagVar INT,
Elevation INT,
SpeedLimit INT,
SpeedLimitAltitude INT,
TransAltitude INT,
GatesExist BOOL,
AlternatesExist BOOL,
MaxRunwayLength INT,
RevState INT,
CC TEXT,
SidExist TEXT,
StarExist TEXT,
ApproachExist TEXT,
EosidExist TEXT
);
Airports表结构

飞行程序头数据 procedureHeaders

CREATE TABLE ProcedureHeaders
(
RecordIndex INT,
Key INT,
Airport TEXT,
Ident TEXT,
BaseProcedure TEXT,
Runway TEXT,
TransAltitude INT,
RevCode INT,
ViaType INT,
ViaTypeString TEXT,
IsCustomized BOOL,
Extended BOOL,
RevState INT
);
飞行程序头数据 ProcedureHeaders

飞行程序详细数据 procedures

CREATE TABLE Procedures
(
RecordIndex INT,
Key INT,
ParentKey INT,
Airport TEXT,
Ident TEXT,
BaseProcedure TEXT,
Runway TEXT,
TransAltitude INT,
RevCode INT,
ViaType INT,
ViaTypeString TEXT,
IsCustomized BOOL,
Extended BOOL,
Fix TEXT,
FixLatitude REAL,
FixLongitude REAL,
FixType INT,
FixTypeString TEXT,
TurnDir INT,
TurnDirString TEXT,
PathTerm TEXT,
OverFly BOOL,
MissedApproach BOOL,
Course REAL,
Heading REAL,
Radial REAL,
Alt1Lable INT,
Alt2Lable INT,
Alt1 INT,
Alt2 INT,
CombinedAlt1 TEXT,
CombinedAlt2 TEXT,
Distance REAL,
Time REAL,
Navaid TEXT,
NavaidFrequency REAL,
NavaidLatitude REAL,
NavaidLongitude REAL,
NavaidDeclination REAL,
NDB TEXT,
NDBFrequency REAL,
NDBLatitude REAL,
NDBLongitude REAL,
ArcCtr TEXT,
ArcCtrLatitude REAL,
ArcCtrLongitude REAL,
FPA REAL,
RNP REAL,
GSCA REAL,
CondAlt REAL,
ARC BOOL,
ProcIndex BOOL,
RevState INT,
CAS REAL,
FAF BOOL
);
飞行程序详细数据 Procedures

数据库控件使用的就是delphi XE V的dbexpress。

程序准备完成后的VST是三成结构,第一层(level 0)是机场列表 ,第二层是各机场下的飞行程序列表,第三层是飞行程序各航段列表。

不过在第一部分只准备完成第一层和第二层。

第一步 定义结构(type record)

结构定义数据如下:

  //机场数据的结构
  rairport=record
    key:Integer;
    ident:string[26];
    procexist:Boolean;
    rwyexist:Boolean;
    latitude:Real;
    longitude:Real;
    magvar:Real;
    elevation:Integer;
  end;
  //程序头的结构
  rprocheader=record
    key:Integer;
    aptid:string[6];
    ident:string[30];
    RWY:string[10];
    transaltitude:Integer;
    iscust:Boolean;
  end;
  //航段数据的结构
  rsegment=record
    parentkey:Integer;
    fix:string[12];
    latitude:Real;
    longitude:Real;
    viatype:string[10];
    fixtype:string[12];
    pathterm:string[4];
    tunrdirstring:string[2];
    missedapp:Boolean;
    course:Real;
    alt1:string[12];
    alt2:string[12];
  end;
  //定义的航空数据
  airdata=(airport,proc,airway,segment,pnt);
  //定义的VST使用的结构
  rndbapt=record
    airtype:airdata;
    case airdata of
      airport:(apt:rairport);
      proc:(proc_h:rprocheader);
      pnt:(seg:rsegment;)

  end;
  PNDBAPT=^rndbapt;

程序的界面如下,一个VST1,没有任何数据,四个数据库控件

第二步 程序初始化

 SQLite的数据库连接放在了窗体的oncreate中,代码如下:

procedure TMAINF.FormCreate(Sender: TObject);
begin
  con1.Open;
  //让VST显示标题栏
  vst1.Header.Options:=vst1.Header.Options+[hoVisible];
  //建立三个栏目
  with vst1.Header.Columns.Add do
    begin
      Text:='名称';
      Width:=80;
    end;
  with vst1.Header.Columns.Add do
    begin
      Text:='';
      Width:=100;
    end;
  with vst1.Header.Columns.Add do
    begin
      Text:='';
      Width:=100;
    end;
  //获得机场数据
  GETAPTDATA;
end;

而初始化结构的数据大小直接在VST1的ongetnodedatasize事件中实现,代码如下:

//获得数据大小(size)
procedure TMAINF.vst1GetNodeDataSize(Sender: TBaseVirtualTree;
  var NodeDataSize: Integer);
begin
  NodeDataSize:=SizeOf(RNDBAPT);
end;

第三步  初始化数据

在第二步中的oncreate事件相应代码的最后有一句getaptdata,这是定义的获取数据的程序。

代码如下:

procedure TMAINF.GETAPTDATA;
begin
   QRY1.SQL.Text:='SELECT * FROM AIRPORTS WHERE REVCODE>=2 ORDER BY IDENT' ;
   QRY1.Open;
   qry2.SQL.Text:='select * from ProcedureHeaders order by airport';
   QRY2.Open;
   qry3.SQL.Text:='select airport,count(*) from ProcedureHeaders group by airport order by airport';
   qry3.Open;
   //直接设置根节点数据量,即机场数量
   vst1.RootNodeCount:=QRY1.RecordCount;
   //设置RootNodeCount,VST即开始调用ONINITNODE,调用完后才执行下面的语句
   QRY1.Close;
   ct:=0;
end;

VST中增加节点有两种方法,一种是直接设置rootnodecount和ChildCount,并在oninitnode和oninitchildren事件中载入数据,这种方法是VST所推荐的,速度也是最快的,本文使用的就是这种;

另一种是使用函数addchild(node:pvirtualnode):pvirtualnode增加节点,然后使用getnodedata(node)函数来载入数据。这种方法最灵活,但运行速度稍逊。

oninitnode事件发生于VST的初期,用于设置VST的节点数据和节点属性,事实上,VST的每一个节点的每一个栏都可以有不同的节点背景、节点高度,checkbox类型等属性。而每一行也可设定在具体那一栏显示图标等。言归正传,此例子程序在oninitnode事件中载入数据,代码如下:

procedure TMAINF.vst1InitNode(Sender: TBaseVirtualTree; ParentNode,
  Node: PVirtualNode; var InitialStates: TVirtualNodeInitStates);
begin
  case Sender.GetNodeLevel(NODE)  of
    0:begin
        with PNDBAPT(Sender.GetNodeData(NODE))^ do
         begin
           airtype:=airport;
           with APT,QRY1 do
            begin
              key:=FieldByName('key').AsInteger;
              IDENT:=FieldByName('IDENT').AsString;
              procexist:=FieldByName('ProceduresExist').AsString='1';
              rwyexist:=FieldByName('RunwaysExist').AsString='1';
              latitude:=FieldByName('latitude').AsFloat;
              longitude:=FieldByName('longitude').AsFloat;
              magvar:=FieldByName('magvar').AsFloat;
              elevation:=FieldByName('elevation').AsInteger;
              //如果有程序,则允许子节点
              if procexist then
                begin
                  InitialStates:=InitialStates+[ivsHasChildren]+[ivsExpanded] ;

                end;


            end;
         end;
        QRY1.Next;
      end;
    1:begin
        with PNDBAPT(Sender.GetNodeData(NODE))^ do
         begin
           airtype:=proc;
           //ShowMessage(QRY2.SQL.Text);
           with proc_h,QRY2 do
            begin
              key:=FieldByName('key').AsInteger;
              aptid:=QRY2.FieldByName('Airport').AsString;
              ident:=QRY2.FieldByName('ident').AsString;
              RWY:=QRY2.FieldByName('runway').AsString;

                transaltitude:=FieldByName('TransAltitude').AsInteger;

                iscust:=FieldByName('IsCustomized').AsInteger=1;
                proc_type:=FieldByName('viatypestring').AsString;

             end;
         end;
        QRY2.Next;
      end;
    2:;
  end;
end;

而oninitchildren的代码如下:

procedure TMAINF.vst1InitChildren(Sender: TBaseVirtualTree; Node: PVirtualNode;
  var ChildCount: Cardinal);
begin
  with PNDBAPT(Sender.GetNodeData(node))^ do
  begin
    case Sender.GetNodeLevel(node) of
      0:begin

               ChildCount:=QRY3.Fields[1].AsInteger;
               qry3.Next;

        end;
      1:

    end;
  end;
end;

第四步 显示数据

在ongettext事件定义显示的文本。

代码如下:

procedure TMAINF.vst1GetText(Sender: TBaseVirtualTree; Node: PVirtualNode;
  Column: TColumnIndex; TextType: TVSTTextType; var CellText: string);
begin
  with PNDBAPT(Sender.GetNodeData(node))^ do
  begin
    case airtype of
      airport:begin
                case Column of
                  0:CellText:=apt.ident;
                  1:begin
                      //纬度数据
                      CellText:=latitudetostr(apt.latitude);
                    end;
                  2:begin
                      CellText:=longitudetostr(apt.longitude);
                    end;
                end;
              end ;
      proc: begin
                case Column of
                  0:CellText:=proc_h.ident;
                  1:begin
                      //对应的跑道
                      CellText:=proc_h.RWY;
                    end;
                  2:begin
                      //程序类型,进近或进离场、复飞等
                      CellText:=proc_h.proc_type;
                    end;
                end;
              end;
      segment: ;
    end;
  end;
end;

而latitudetostr和longitudetostr是两个自定义的函数,用于将浮点数类型的坐标转换为字符串坐标。代码如下:

//纬度转换为字符串
function latitudetostr(lat:Real):string;
var c,d,f,m:string;
begin
  if lat>=0 then c:='N' else c:='S';
  lat:=Abs(lat);
  d:=IntToStr(Trunc(lat));
  lat:=Frac(lat)*60;
  f:=IntToStr(Trunc(lat));
  lat:=Frac(lat)*60;
  m:=IntToStr(Trunc(lat));
  Result:=Format('%s%s°%s′%s″',[C,D,F,M]);


end;
//经度转换为字符串
function longitudetostr(lat:Real):string;
var c,d,f,m:string;
begin
  if lat>=0 then c:='E' else c:='W';
  lat:=Abs(lat);
  d:=IntToStr(Trunc(lat));
  lat:=Frac(lat)*60;
  f:=IntToStr(Trunc(lat));
  lat:=Frac(lat)*60;
  m:=IntToStr(Trunc(lat));
  Result:=Format('%s%s°%s′%s″',[C,D,F,M]);


end;

最后程序运行结果如下:

原文地址:https://www.cnblogs.com/luohq001/p/12987164.html