VST实例(1)
一、程序目标
首先谈谈我们的目标程序,这个程序是通过读取一个CSV文件,创建一个两层结构的目录树,第一层存放的是情报区名称,而第二层存放的是该情报区的机场信息。
每一层(level)的每一个节点(node)分为6栏(column),最终的运行结果如图所示:
程序实现了:
l 创建目录树
l 分栏显示不同的信息
l 不同的类型使用了不同的图标
l 不同的底色显示
l 在编辑框里输入字符,实时筛选。
l 同一栏中使用不同的显示。
l 可以调节行、列宽度
l 标题栏菜单
l 添加或修改内容
l ……
而程序设计阶段的界面如下
准备创建一个两层结构的关于机场列表的目录树,数据来源于一个CSV文件,但鉴于SQLITE强大的查询功能,所以首先要做的就是把CSV文件中的数据添加到SQLITE数据库中。
现在让我们一步步的开始程序的设计吧。
首先来介绍下CSV文件中的数据,CSV文件中存储了若干机场的基础数据,包括机场的ICAO四字码,IATA三字码,中文名称,所属情报区,该机场是否是单跑道机场以及该机场是主降机场还是备降机场。同时,还存储了情报区的ICAO四字码和中文名称,当然情报区不包含IATA三字码,也不存在跑道数,所以三字码部分是空白字符串,而跑道数是-1。
程序设计阶段的界面很简单,如图所示:
程序的功能基本上都靠代码来实现,imagelist(IL1)中添加了三个32*32的图标,只使用其中两个。
目录树使用的控件是TVirtualStringTree(VST),其它Firedac等控件的设置如下:
object Form2: TForm2 Left = 0 Top = 0 Caption = 'Form2' ClientHeight = 587 ClientWidth = 861 Color = clBtnFace Font.Charset = DEFAULT_CHARSET Font.Color = clWindowText Font.Height = -11 Font.Name = 'Tahoma' Font.Style = [] OldCreateOrder = False OnCreate = FormCreate PixelsPerInch = 96 TextHeight = 13 object vst: TVirtualStringTree Left = 0 Top = 50 Width = 861 Height = 537 Align = alClient Header.AutoSizeIndex = -1 Header.Options = [hoColumnResize, hoDrag, hoShowSortGlyphs, hoHeaderClickAutoSort, hoAutoColumnPopupMenu] Header.PopupMenu = vthdrpmn1 Header.SortColumn = 0 HintMode = hmHintAndDefault Images = il1 IncrementalSearch = isAll LineMode = lmBands LineStyle = lsSolid ParentShowHint = False PopupMenu = pm1 ShowHint = True TabOrder = 0 TreeOptions.AutoOptions = [toAutoDropExpand, toAutoExpand, toAutoScroll, toAutoScrollOnExpand, toAutoSort, toAutoSpanColumns, toAutoTristateTracking, toAutoHideButtons, toAutoChangeScale] TreeOptions.MiscOptions = [toAcceptOLEDrop, toCheckSupport, toEditable, toFullRepaintOnResize, toInitOnSave, toToggleOnDblClick, toWheelPanning, toFullRowDrag, toNodeHeightResize, toEditOnClick] TreeOptions.PaintOptions = [toShowBackground, toShowButtons, toShowDropmark, toShowHorzGridLines, toShowRoot, toShowVertGridLines, toThemeAware, toUseBlendedImages, toShowFilteredNodes] TreeOptions.SelectionOptions = [toFullRowSelect] OnBeforeItemErase = vstBeforeItemErase OnBeforeItemPaint = vstBeforeItemPaint OnCompareNodes = vstCompareNodes OnCreateEditor = vstCreateEditor OnDragAllowed = vstDragAllowed OnDragOver = vstDragOver OnDragDrop = vstDragDrop OnDrawText = vstDrawText OnEditing = vstEditing OnGetText = vstGetText OnPaintText = vstPaintText OnGetHintKind = vstGetHintKind OnGetImageIndex = vstGetImageIndex OnGetHint = vstGetHint OnGetNodeDataSize = vstGetNodeDataSize OnInitChildren = vstInitChildren OnInitNode = vstInitNode OnNewText = vstNewText OnNodeClick = vstNodeClick OnNodeDblClick = vstNodeDblClick Columns = < item Position = 0 Width = 857 end> end object pnl1: TPanel Left = 0 Top = 0 Width = 861 Height = 50 Align = alTop Caption = 'pnl1' ShowCaption = False TabOrder = 1 OnClick = pnl1Click object lbledt1: TLabeledEdit Left = 128 Top = 23 Width = 553 Height = 21 EditLabel.Width = 108 EditLabel.Height = 13 EditLabel.Caption = #35831#36755#20837#24182#22238#36710#26597#25214#65306 LabelPosition = lpLeft TabOrder = 0 OnChange = lbledt1Change OnEnter = lbledt1Enter end end object fdphysqltdrvrlnk1: TFDPhysSQLiteDriverLink Left = 792 Top = 400 end object con1: TFDConnection Params.Strings = ( 'DriverID=SQLite') Left = 792 Top = 336 end object dlgOpen1: TOpenDialog FileName = 'codes.csv' Left = 784 Top = 464 end object QRY1: TFDQuery Connection = con1 Left = 720 Top = 400 end object fdgxwtcrsr1: TFDGUIxWaitCursor Provider = 'Forms' Left = 720 Top = 344 end object QRY2: TFDQuery Connection = con1 Left = 712 Top = 464 end object il1: TImageList Height = 32 Width = 32 Left = 776 Top = 264 Bitmap = {......} end object vthdrpmn1: TVTHeaderPopupMenu Left = 792 Top = 57 end object pm1: TPopupMenu OnPopup = pm1Popup Left = 584 Top = 472 object N1: TMenuItem Caption = #20840#23637#24320 OnClick = N1Click end object N2: TMenuItem Caption = #20840#25910#25314 OnClick = N2Click end object N3: TMenuItem Caption = '-' end object CSV1: TMenuItem Caption = #21478#23384#20026'CSV'#25991#20214 OnClick = CSV1Click end object HTML1: TMenuItem Caption = #20854#23427#25991#26412 OnClick = HTML1Click end object N4: TMenuItem Caption = '-' end object N5: TMenuItem Caption = #23548#20986#21246#36873#26426#22330#20449#24687 OnClick = N5Click end end object dlgSave1: TSaveDialog Filter = #32593#39029#25991#20214'UTF-8 (*.htm; *.html)|*.htm;*.html|Unicode UTF-16 text file ' + '(*.uni)|*.uni|Rich text UTF-16 file (*.rtf)|*.rtf|CSV'#25991#20214' (*.csv)|' + '*.csv|'#25991#26412#25991#20214' (*.txt)|*.txt' Left = 704 Top = 280 end end
二、初始数据设置
首先创建一个record类型RCODES及其指针如下:
type RCODES=record ICAO:String; IATA:String; CODETYPE:String; rwy_style:String; names:string; apt_type:string; end; pcodes=^RCODES;
VST需要你首先告诉它你的数据的长度,这一点是必须的,我们需要为ongetnodedatasize事件写代码如下:
procedure TForm2.vstGetNodeDataSize(Sender: TBaseVirtualTree; var NodeDataSize: Integer); begin NodeDataSize:=SizeOf(rcodes); end;
也可以在创建窗体的时候进行设置:
VST. NodeDataSize:=SizeOf(rcodes);
数据来源于一个CSV文件,我们当然可以直接处理CSV文件的数据,但是这个文件的数据结构不是很清晰,我习惯性的把文件转换为SQLITE数据库进行处理,所以在创建窗体的时候首先有以下代码:
procedure TForm2.FormCreate(Sender: TObject); var sl1:TStringList;sl2:tarray<string>;ss1,ss2:string; i:Integer; //nd:PVirtualNode; begin con1.Params.Values['database']:='codes.db'; //如果没有SQLITE数据库,则找到CSV文件并创建数据库和表,表名为codes //创建的表中增加了一个新字段:RMK以备用。 if not FileExists('codes.db') then begin sl1:=TStringList.Create; if not dlgOpen1.Execute then close; sl1.LoadFromFile(dlgOpen1.FileName); ss1:=sl1[0]; sl2:=ss1.Split([',']); ss2:='create table codes('; for ss1 in sl2 do ss2:=ss2+ss1+' ftstring,'; ss2:=ss2+'rmk ftstring)'; con1.ExecSQL(ss2); for i:=1 to sl1.Count-1 do begin ss1:=sl1[i]; sl2:=ss1.Split([',']); ss2:='insert into codes values('; for ss1 in sl2 do ss2:=ss2+ss1.QuotedString+','; ss2:=ss2+'" ")'; Con1.ExecSQL(ss2); end; //创建的SL1最好释放 FreeAndNil(SL1); end; //设置节点的默认高度,这个默认值是18,我们在这里改为30 vst.DefaultNodeHeight:=30; //设置header标题栏 with vst.Header do begin //header的Options进行一些初始设置,分别是: //hoColumnResize 可以修改栏的尺寸 //hoShowSortGlyphs 排序时可以显示一个向上或向下的三角形 //hoHeaderClickAutoSort 可以通过点击标题栏进行排序,这个属性很重要 //标题栏的options比较多,有兴趣的朋友可以到VST.HEADER里面去逐个试验 Options:=[hoColumnResize, hoDrag, hoShowSortGlyphs, hoHeaderClickAutoSort, hoAutoColumnPopupMenu]; //让标题栏可见,并设置标题栏的基础属性 Options:=Options+[hoVisible]; //如果确实没有创建column,这一行可以不要 Columns.Clear; //设置标题栏的字体及高度 Font.Name:='宋体'; Font.Size:=14; Height:=33; //创建6个column,用于分别显示不同的信息 with Columns.Add do begin Width:=180; text:='四字码'; Hint:='机场ICAO四字码'; end; with Columns.Add do begin Width:=80; text:='三字码'; end; with Columns.Add do begin Width:=80; text:='类型'; end; with Columns.Add do begin Width:=180; text:='名称'; end; with Columns.Add do begin Width:=80; text:='跑道数'; end; with Columns.Add do begin Width:=80; text:='主/备'; end; end; with vst.TreeOptions do begin SelectionOptions:=SelectionOptions+[toMultiSelect]; end; with vst.TreeOptions do begin //设置autoiotions AutoOptions:=AutoOptions+[toAutoExpand,//点击带子节点的节点时,自动展开子节点 toAutoHideButtons];//当节点的所有子节点都不可见时,隐藏节点的“+”按钮 MiscOptions:=MiscOptions+[toCheckSupport,//支持复选框 toEditable,//可以编辑 toFullRowDrag,//拖动时整行拖动 toNodeHeightResize//可以修改节点高度 ]; SelectionOptions:=SelectionOptions+[toMultiSelect];//设置为允许多选 end; createvst; //vst.FullExpand(); //caption:=vst.AbsoluteIndex(vst.GetLast()).ToString(); //也可不调用createvst,写代码如下也可创建VST {QRY1.First; while not QRY1.Eof do begin nd:=vst.AddChild(nil); with pcodes(vst.GetNodeData(nd))^ do begin //根节点的获取数据语句 qry2.Open('相关的SQL语句'); while not QRY2.Eof do begin with pcodes(vst.GetNodeData(vst.AddChild(nd)))^ do begin //获取机场数据的语句 end; QRY2.Next; end; end; QRY1.Next; end; } end; procedure TForm2.createvst; begin QRY1.Open('select * from codes where site_code in '+ '(select distinct fir_code from codes where single_runway_ad>-1 )'+ ' and single_runway_ad=-1'); //firedac默认初始只读取50行数据,此函数强制让QRY1完全读取table数据 //如果不执行此行,则recordcount将最大只有50; QRY1.FetchAll; //设置根节点数为有机场的情报区数,因为根节点指向的是情报区。 vst.RootNodeCount:=qry1.RecordCount; QRY1.Close; end;
这段代码只在第一次运行时进行,就不再一一累述了。
CODES.CSV的内容如下:
除了标题栏,我们也要对VST本身进行一些设置。但不会在这里细讲,而在具体章节时才予以讲述,下面让我们一步一步的来完成我们的程序吧。