{ ----------------------------------- TreeView関係Unit 2003/11/13 TreeViewItemsToText/TextToTreeViewItems実装 2004/01/28 Get/SetTreeSelectItemIndexを追加 2004/04/18 TreeViewRuledLine/TreeNodeCountSubItemを追加 //----------------------------------- } unit TreeViewUnit; interface uses ComCtrls, Classes, SysUtils; function TreeViewItemsToText(TreeView: TTreeView): String; procedure TextToTreeViewItems(Text: String; TreeView: TTreeView); function GetTreeSelectItemIndex(TreeView: TTreeView): Integer; procedure SetTreeSelectItemIndex(TreeView: TTreeView; Value: Integer); function TreeViewRuledLine(TreeView: TTreeView; TreeNode: TTreeNode; HorzLineLength: Integer; ItemHeader, ItemFooter: String; BoldFlag: Boolean): String; function TreeNodeCountSubItem(TreeNode: TTreeNode; VisibleCount: Boolean): Integer; implementation {------------------------------- // TreeViewのItemと文字列の相互変換をする TreeViewItemsToText TextToTreeViewItems 機能: TreeView.SaveToFileや TreeView.LoadFromFileで使われる 文字列をメモリ上で取得します。 備考: Levelが深くなるとインデントにタブが入ります 履歴: 2003/11/12 作成 //------------------------------} function TreeViewItemsToText(TreeView: TTreeView): String; var ms: TMemoryStream; // i: Integer; ReadByte: Byte; begin ms := TMemoryStream.Create; try TreeView.SaveToStream(ms); SetLength(Result, ms.Size); ms.Position := 0; ms.ReadBuffer(Result[1], ms.Size); { ----------------------------------- ↑上記1行は下記のように置き換え可能 for i := 1 to ms.Size do begin ms.ReadBuffer(ReadByte, SizeOf(byte)); Result[i] := Char(ReadByte); end; //----------------------------------- } finally ms.Free end; end; procedure TextToTreeViewItems(Text: String; TreeView: TTreeView); var ms: TMemoryStream; begin ms := TMemoryStream.Create; try ms.WriteBuffer(Text[1], Length(Text)); ms.Position := 0; TreeView.LoadFromStream(ms); finally ms.Free end; end; //------------------------------ //選択しているTreeItemのIndexを返す function GetTreeSelectItemIndex(TreeView: TTreeView): Integer; begin Result := -1; if TreeView = nil then Exit; if TreeView.Selected <> nil then Result := TreeView.Selected.AbsoluteIndex; end; //Indexで選択TreeItemを変える procedure SetTreeSelectItemIndex(TreeView: TTreeView; Value: Integer); begin if TreeView = nil then Exit; if (0<=Value) and (Value<=TreeView.Items.Count-1) then TreeView.Selected := TreeView.Items[Value]; end; {------------------------------- // TreeNodeの全ての下位Itemの個数を数える 引数説明: VisibleCount:表示されているItemの個数だけを数える場合Trueにする 備考: 履歴: 2004/04/18 //------------------------------} function TreeNodeCountSubItem(TreeNode: TTreeNode; VisibleCount: Boolean): Integer; var TargetTreeNode: TTreeNode; begin Result := -1; if TreeNode = nil then Exit else Result := 0; TargetTreeNode := TreeNode; while (TargetTreeNode.GetNext <> nil) do begin TargetTreeNode := TargetTreeNode.GetNext; if TargetTreeNode.Level = TreeNode.Level then break; if VisibleCount then begin if TargetTreeNode.IsVisible then Inc(Result); end else Inc(Result); end; end; //------------------------------ {------------------------------- //TreeViewのテキスト化処理 機能: TreeViewを ┬◆継承元クラスA │├◆継承先クラスC ││└◆継承先クラスD │└◇継承先クラスE │ ├◇継承先クラスF │ └◇継承先クラスG ├◇継承元クラスB └◇継承元クラスH こんな文字列化する関数 戻り値: TreeNodeRuledLine: 1つのTreeNodeに対する罫線文字列 TreeViewRuledLine: 全体の罫線文字列 処理: ・TreeViewRuledLine関数でTreeNodeに対してループ ・TreeNodeに対して1行分の罫線文字列を作る ・罫線文字列はTreeNodeの親をたどって構成する 備考: 展開していないNodeは表示されません ShowRootにも対応しています。 履歴: 2001/07/10 2001/09/12 最初のアイテムが横棒になるように対応 ShowLinesに対応 ShowButtonsに対応 2001/10/01 横罫線延長とItemHeader/Futerに対応した 2004/04/18 選択TreeNodeからのテキスト化に対応した //------------------------------} const HorzNormalLine: WideString = '─'; function TreeNodeRuledLine(TreeNode: TTreeNode; ZenkakuSpaceSwitch: Boolean = False; IndentLevel: Integer = 0): String; {↓TreeNodeの複数階層の親を求める ParentCount=2なら2段階上の親のTreeNodeを返す関数 ParentCountは1以上とする} function TreeNodeParents(TreeNode: TTreeNode; ParentCount: Integer): TTreeNode; var i: Integer; begin Result := TreeNode; for i := 0 to ParentCount-1 do begin Result := Result.Parent; end; end; function SwitchSpace: String; begin if ZenkakuSpaceSwitch then Result := ' ' else Result := ' '; end; {↓罫線を描画する場合にShowLine=Falseなら全角スペースを戻す関数} function ShowLineStr(LineStr: String): String; begin if TTreeView(TreeNode.TreeView).ShowLines then Result := LineStr else Result := SwitchSpace; end; var i: Integer; begin Result := ''; for i := TreeNode.Level downto IndentLevel do begin if (i=TreeNode.Level) and (TTreeView(TreeNode.TreeView).ShowRoot=False) then begin //ShowRootに対応 //Root部分の描画は行なわない end else if (i=TreeNode.Level) and (TTreeView(TreeNode.TreeView).ShowLines=False) and (TTreeView(TreeNode.TreeView).ShowButtons=False) then begin {↑ShowRootがTrueでもShowLines&ShowButtonsが共にFalseなら} //Root部分の描画は行なわない end else if i=IndentLevel then begin if TTreeView(TreeNode.TreeView).ShowButtons=False then begin if (TreeNode.Level=0) and (TreeNode.Index=0) then begin //最初のアイテム {↓同一階層の次のItemがあれば『┬』なければ『─』} if (TreeNode.getNextSibling=nil) then Result := Result + ShowLineStr('─') else Result := Result + ShowLineStr('┬'); end else {↓同一階層の次のItemがあれば『├』なければ『└』} if (TreeNode.getNextSibling=nil) then Result := Result + ShowLineStr('└') else Result := Result + ShowLineStr('├'); end else {↓ShowButtonがTrueの場合} if TreeNode.Count=0 then begin {↓子ItemがなくてButtonを表示する必要がない場合} if (TreeNode.Level=0) and (TreeNode.Index=0) then begin //最初のアイテム if (TreeNode.getNextSibling=nil) then Result := Result + ShowLineStr('─') else Result := Result + ShowLineStr('┬'); end else if (TreeNode.getNextSibling=nil) then Result := Result + ShowLineStr('└') else Result := Result + ShowLineStr('├'); end else begin if TreeNode.Expanded then Result := Result + '−' else Result := Result + '+'; end; end else begin {↓i個分の親TreeNodeの同一階層で次のItemがあれば『│』なければスペース} if TreeNodeParents(TreeNode, i-IndentLevel).getNextSibling=nil then Result := Result + SwitchSpace else Result := Result + ShowLineStr('│'); end; end; end; //------------------------------- //WideCharを指定個数用意する function StringOfWideChar(Ch: WideChar; Count: Integer): string; var i: Integer; begin Result := ''; for i := 1 to Count do begin Result := Result + Ch; end; end; function TreeViewRuledLine(TreeView: TTreeView; TreeNode: TTreeNode; HorzLineLength: Integer; ItemHeader, ItemFooter: String; BoldFlag: Boolean): String; //------------------------------- //水平罫線を伸ばす function HorzLineReplace(S: WideString): WideString; const LineTbl: WideString = '─┬└├+−'; var i: Integer; begin Result := S; for i := 1 to Length(LineTbl) do begin Result := StringReplace(Result, LineTbl[i], LineTbl[i]+StringOfWideChar(HorzNormalLine[1],HorzLineLength), []); if Result <> S then Exit; end; end; //------------------------------- //細罫線を太罫線にする function BoldLineString(S: String): String; begin Result := S; Result := StringReplace(Result, '─', '━', [rfReplaceAll]); Result := StringReplace(Result, '┬', '┳', [rfReplaceAll]); Result := StringReplace(Result, '└', '┗', [rfReplaceAll]); Result := StringReplace(Result, '├', '┣', [rfReplaceAll]); Result := StringReplace(Result, '│', '┃', [rfReplaceAll]); end; var i, Index: Integer; LineStr: String; SL: TStringList; WS: WideString; LoopStart, LoopEnd, TreeIndentLevel: Integer; begin Result := ''; if TreeView = nil then Exit; if TreeNode = nil then begin LoopStart := 0; LoopEnd := TreeView.Items.Count-1; TreeIndentLevel := 0; end else begin LoopStart := TreeNode.AbsoluteIndex; LoopEnd := LoopStart + TreeNodeCountSubItem(TreeNode, False); TreeIndentLevel := TreeNode.Level+1; end; for i := LoopStart to LoopEnd do begin LineStr := ''; if TreeView.Items[i].IsVisible = False then continue; {↑見えないNodeはスキップ} LineStr := TreeNodeRuledLine(TreeView.Items[i], False, TreeIndentLevel); {↓横罫線を伸ばす} LineStr := HorzLineReplace(LineStr); {↓ItemにHeaderとFooterを付属させる} LineStr := LineStr + ItemHeader + TreeView.Items[i].Text + ItemFooter; Result := Result + LineStr + #13#10; end; {↓罫線をつなぎなおす処理 ─ ┬ └ → └ ─ ┬ ├ → ├ } SL := TStringList.Create; try SL.Text := Result; for i := 1 to SL.Count-1 do begin WS := SL[i-1]; Index := Pos('└', WideString(SL[i])); if (Index<>0) and (WS[Index]='─') then begin Delete(WS, Index, 1); Insert('┬', WS, Index); SL.Strings[i-1] := WS; end; Index := Pos('├', WideString(SL[i])); if (Index<>0) and (WS[Index]='─') then begin Delete(WS, Index, 1); Insert('┬', WS, Index); SL.Strings[i-1] := WS; end; end; Result := SL.Text; finally SL.Free; end; if BoldFlag then Result := BoldLineString(Result); Result := Trim(Result); end; //------------------------------ end.