トップへ(mam-mam.net/)

TTreeViewで階層リスト(ツリー)を表示しドラッグ&ドロップで移動 ~Delphiソースコード集

検索:

TTreeViewで階層リスト(ツリー)を表示しドラッグ&ドロップで移動 ~Delphiソースコード集

TTreeViewを使うと階層リスト(ツリー)を表示することが出来ます。
以下のようにTTreeView×1個(TreeView1)とTButton×1個(Button1)があるフォームでButton1をクリックした時にTreeView1を操作するソースコードを記述します。
プロジェクトとユニットの保存(「すべて保存」ボタンをクリック)しておきます。(自動的に必要なユニットがUsesに記述されます)

Button1をクリックした時のイベント

Button1をクリックして選択し、左下ペインの「オブジェクトインスペクタ」にあるタブ「イベント」をクリックして切り替えます。 「OnClick」をダブルクリックするとクリックした時のイベントのひな形が生成されますのでソースコードを記述します。

Button1をクリックした時にTreeView1にアイテムを追加するソースコードの記述

以下ソースコードを入力します

unit Unit1;

interface

uses
  Winapi.Windows, Winapi.Messages, System.SysUtils, System.Variants,
  System.Classes, Vcl.Graphics,
  Vcl.Controls, Vcl.Forms, Vcl.Dialogs, Vcl.StdCtrls, Vcl.ComCtrls;

type
  TForm1 = class(TForm)
    TreeView1: TTreeView;
    Button1: TButton;
    procedure Button1Click(Sender: TObject);
  private
    { Private 宣言 }
  public
    { Public 宣言 }
  end;

var
  Form1: TForm1;

implementation

{$R *.dfm}

procedure TForm1.Button1Click(Sender: TObject);
var root1,root2,child:TTreeNode;
begin
  //全てのツリーノードを削除
  TreeView1.Items.Clear;

  //TreeView1.Items.AddChild(親ノード,子ノード)メソッドは
  //指定した親ノードの子ノードとしてその階層内の最後(一番下)に追加します

  //親ノードを「nil」で指定するとルートノードとして追加されます
  root2:=TreeView1.Items.AddChild(nil,'ルート2');

  //親ノード「root2」の子ノードを追加
  TreeView1.Items.AddChild(root2,'子ノード21');

  //親ノード「root2」の子ノードを追加
  child:=TreeView1.Items.AddChild(root2,'子ノード22');

  //「child」の子ノードを追加
  TreeView1.Items.AddChild(child,'子ノード222');
  //「child」の子ノードを追加
  TreeView1.Items.AddChild(child,'子ノード221');

  //親ノードを「nil」で指定するとルートノードとして追加されます
  root1:=TreeView1.Items.AddChild(nil,'ルート1');

  //「root1」の子ノードを追加
  TreeView1.Items.AddChild(root1,'子ノード11');

  //「root1」の子ノードを追加
  TreeView1.Items.AddChild(root1,'子ノード12');


  //TTreeNode.Expand(Recurse) は下位ノードを展開します
  //RecurseがFalseの場合は子ノードのみ展開し
  //rueの場合は全ての下位ノードを展開します
  root1.Expand(True);
  root2.Expand(True);
end;

end.

実行

実行してButton1をクリックするとTreeView1にノードが追加され展開されます

ソートする

以下ソースコードを1行追加するとノードがソートされます。

procedure TForm1.Button1Click(Sender: TObject);
var root1,root2,child:TTreeNode;
begin
  //全てのツリーノードを削除
  TreeView1.Items.Clear;

  //TreeView1.Items.AddChild(親ノード,子ノード)メソッドは
  //指定した親ノードの子ノードとしてその階層内の最後(一番下)に追加します

  //親ノードを「nil」で指定するとルートノードとして追加されます
  root2:=TreeView1.Items.AddChild(nil,'ルート2');

  //親ノード「root2」の子ノードを追加
  TreeView1.Items.AddChild(root2,'子ノード21');

  //親ノード「root2」の子ノードを追加
  child:=TreeView1.Items.AddChild(root2,'子ノード22');

  //「child」の子ノードを追加
  TreeView1.Items.AddChild(child,'子ノード222');
  //「child」の子ノードを追加
  TreeView1.Items.AddChild(child,'子ノード221');

  //親ノードを「nil」で指定するとルートノードとして追加されます
  root1:=TreeView1.Items.AddChild(nil,'ルート1');

  //「root1」の子ノードを追加
  TreeView1.Items.AddChild(root1,'子ノード11');

  //「root1」の子ノードを追加
  TreeView1.Items.AddChild(root1,'子ノード12');


  //TTreeNode.Expand(Recurse) は下位ノードを展開します
  //RecurseがFalseの場合は子ノードのみ展開し
  //rueの場合は全ての下位ノードを展開します
  root1.Expand(True);
  root2.Expand(True);

  //ソートする
  TreeView1.SortType:=TSortType.stText;

end;

end.

ノードをドラッグ&ドロップで移動できるようにする

TreeView1.DragModeプロパティを「TDragMode.dmAutomatic」に設定するとドラッグできるようになります。
イベントTreeView1.OnDragDropとTreeView1.DragOverとTreeView1.OnEndDragに応答すればドロップ処理できます。
ドロップ時にドロップ先の子ノードとしてノードを移動させます。
SHIFTキーを押しながらドロップするとドロップ先の兄弟ノードとしてノードを移動させます。
また、ノードのドラッグオーバー時にTreeView1の上下20ピクセルの位置にマウスを移動した時に自動でスクロールさせたいのでフォームにTTimerをドラッグ&ドロップします。
実行してButton1をクリックすると多くのノードが表示され、ノードのドラッグ&ドロップができます。

以下ソースコードを記述します。

unit Unit1;

interface

uses
  Winapi.Windows, Winapi.Messages, System.SysUtils, System.Variants,
  System.Classes, Vcl.Graphics,
  Vcl.Controls, Vcl.Forms, Vcl.Dialogs, Vcl.StdCtrls, Vcl.ComCtrls, Vcl.ExtCtrls;

type
  TForm1 = class(TForm)
    TreeView1: TTreeView;
    Button1: TButton;
    Timer1: TTimer;
    procedure Button1Click(Sender: TObject);
    procedure FormCreate(Sender: TObject);
    procedure TreeView1DragOver(Sender, Source: TObject; X, Y: Integer;
      State: TDragState; var Accept: Boolean);
    procedure TreeView1EndDrag(Sender, Target: TObject; X, Y: Integer);
    procedure TreeView1DragDrop(Sender, Source: TObject; X, Y: Integer);
    procedure Timer1Timer(Sender: TObject);
  private
    { Private 宣言 }
  public
    { Public 宣言 }
  end;

var
  Form1: TForm1;

implementation

{$R *.dfm}

procedure TForm1.Button1Click(Sender: TObject);
var root1,root2,child:TTreeNode;
    i:Integer;
begin
  //全てのツリーノードを削除
  TreeView1.Items.Clear;

  //AddChild(親ノード,子ノード)メソッドは
  //指定した親ノードの子ノードとして最後に追加します

  //親ノードを「nil」で指定するとルートノードとして追加されます
  root2:=TreeView1.Items.AddChild(nil,'ルート2');

  //親ノード「root2」の子ノードを追加
  TreeView1.Items.AddChild(root2,'子ノード21');

  //親ノード「root2」の子ノードを追加
  child:=TreeView1.Items.AddChild(root2,'子ノード22');

  //「child」の子ノードを追加
  TreeView1.Items.AddChild(child,'子ノード222');
  //「child」の子ノードを追加
  TreeView1.Items.AddChild(child,'子ノード221');

  //親ノードを「nil」で指定するとルートノードとして追加されます
  root1:=TreeView1.Items.AddChild(nil,'ルート1');

  //「root1」の子ノードを追加
  TreeView1.Items.AddChild(root1,'子ノード11');

  //「root1」の子ノードを追加
  TreeView1.Items.AddChild(root1,'子ノード12');

  //TTreeNode.Expand(Recurse) は下位ノードを展開します
  //RecurseがFalseの場合は子ノードのみ展開し
  //rueの場合は全ての下位ノードを展開します
  root1.Expand(True);
  root2.Expand(True);

  
  //たくさんノードを入れる
  for i := 0 to 99 do
  begin
    TreeView1.Items.AddChild(nil,IntToStr(i));
  end;
  
  //ソートする
  TreeView1.SortType:=TSortType.stText;
end;

procedure TForm1.FormCreate(Sender: TObject);
begin
  //ドラッグを有効にする
  TreeView1.DragMode:=TDragMode.dmAutomatic;

  //スクロール用タイマー
  Timer1.Enabled:=False;
  Timer1.Interval:=50;
end;

procedure TForm1.Timer1Timer(Sender: TObject);
begin
  if TreeView1.Tag<0 then
    //上に1行スクロール
    SendMessage(TreeView1.Handle,WM_VSCROLL,SB_LINEUP,0)
  else if TreeView1.Tag>0 then
    //下に1行スクロール
    SendMessage(TreeView1.Handle,WM_VSCROLL,SB_LINEDOWN,0);
end;

procedure TForm1.TreeView1DragDrop(Sender, Source: TObject; X, Y: Integer);
var o:TObject;
    k:SmallInt;
begin
  //ドロップされたときの処理
  o:=TreeView1.GetNodeAt(X,Y);
  if o is TTreeNode then
  begin
    k:=GetKeyState(VK_SHIFT);
    if k<0 then
    begin
      //シフトキーが押されてドロップ(子ノードとして移動する)
      TTreeView(Sender).Selected.MoveTo(
        TTreeView(Sender).DropTarget,  //ドロップ先オブジェクト
        TNodeAttachMode.naAddFirst//ドロップ先の最初の子ノードとして移動
      );
    end
    else
    begin
      //シフトキーが押されずにドロップ(兄弟ノードとして移動する)
      TTreeView(Sender).Selected.MoveTo(
        TTreeView(Sender).DropTarget,  //ドロップ先オブジェクト
        TNodeAttachMode.naAddChildFirst//ドロップ先の最初の兄弟ノードとして移動
      );
    end;
  end;
end;

procedure TForm1.TreeView1DragOver(Sender, Source: TObject; X, Y: Integer;
  State: TDragState; var Accept: Boolean);
var o:TObject;
begin
  //ドラッグオーバー時
  Accept:=False;
  if (Source is TTreeView) then
  begin
    //ドラッグオーバー時のマウスの座標の下がTTreeNodeか確認
    o:=TreeView1.GetNodeAt(X,Y);
    if o is TTreeNode then
      Accept:=True;  //ドロップの許可
  end;

  //スクロール処理用のタイマーを有効にする
  if Y<20 then
  begin
    //TreeView1の上部にマウスがあると上にスクロールしたい
    TreeView1.Tag := -1;
    Timer1.Enabled := True;
  end
  else if Y>=(TTreeView(Sender).ClientHeight-20) then
  begin
    //TreeView1の下部にマウスがあると下にスクロールしたい
    TreeView1.Tag := 1;
    Timer1.Enabled := True;
  end
  else
  begin
    TreeView1.Tag := 0;
    Timer1.Enabled := False;
  end;
end;

procedure TForm1.TreeView1EndDrag(Sender, Target: TObject; X, Y: Integer);
begin
  //ドラッグ&ドロップ処理終了、又はドラッグ中に「ESC」キーを押してキャンセルされたとき
  Timer1.Enabled:=False;
  TreeView1.Tag:=0;
end;

end.