今回はTreeViewコントロールの使い方です。
まずデザイナでForm1にTreeViewコントロールとボタンを貼り付けます。
そのあとTreeViewコントロールのプロパティを設定します。これは必須ではないけれどもこのように設定したほうがわかりやすいかもしれません。treeView1.HideSelection = false;と設定しておかないとTreeViewコントロール以外の部分をクリックしてしまった場合、どの項目が選択されているのかがわかりにくくなります。
1 2 3 4 5 6 7 8 9 10 |
public partial class Form1 : Form { public Form1() { InitializeComponent(); treeView1.LabelEdit = true; // 項目名の部分をクリックしたら名前を変更できる treeView1.HideSelection = false; // 選択されている項目がつねにわかるようにする } } |
Contents
TreeViewコントロールへの項目の追加
項目の追加ですが、
選択されてるアイテムの一番上の子として追加
選択されてるアイテムの一番下の子として追加
選択されてるアイテムのひとつ上に追加
選択されてるアイテムのひとつ下に追加
の4つのなかから選べるようにします。ただ最初はTreeViewコントロールのなかに項目はひとつもないので、その場合は自作したCreateRootNodeメソッドで最初の項目を追加して、それが選択されている状態にします。
項目の名前は「新しいノード (番号)」にして何番目に追加されたものかがわかるようにしています。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 |
public partial class Form1 : Form { int nodeNumber = 1; void CreateRootNode() { if (treeView1.Nodes.Count == 0) { string nodeText = "新しいノード " + nodeNumber; TreeNode node = new TreeNode(nodeText); treeView1.Nodes.Insert(0, node); treeView1.SelectedNode = node; } } } |
選択されてるアイテムの一番上の子として追加
新しい項目はTreeNodeのコンストラクタを呼び出して生成します。TreeNodeのコンストラクタは複数ありますが、アイコンをつかわず名前だけでよいのであれば new TreeNode(“項目の名前”)がよいと思います。
選択されてるアイテムの一番上の子として追加するときは、選択されているTreeNodeがあるか調べます。ない場合は上記のメソッドで最初の項目を追加します。選択されているTreeNodeが存在する場合はそのNodesの一番最初に新しく生成したTreeNodeのインスタンスを挿入します。
新しいTreeNodeを挿入したら、それが選択されている状態にします。そしてnodeNumberをインクリメントして次に追加する項目の名前を生成できるようにしておきます。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 |
public partial class Form1 : Form { private void ButtonAddItemAsFirst_Click(object sender, EventArgs e) { string nodeText = "新しいノード " + nodeNumber; // 項目の名前になる文字列を生成する TreeNode node = new TreeNode(nodeText); // TreeNodeを生成する if (treeView1.SelectedNode != null) // 選択されている項目があればその一番最初に挿入する treeView1.SelectedNode.Nodes.Insert(0, node); else CreateRootNode(); // 選択されている項目がないなら最初の項目として追加する treeView1.SelectedNode = node; // 選択状態にする nodeNumber++; } } |
選択されてるアイテムの一番下の子として追加
選択されてるアイテムの一番下の子として追加するときも、ほぼ同じです。選択されているTreeNodeが存在する場合はそのNodesの一番最後に新しく生成されたTreeNodeのインスタンスを追加します。
1 2 3 4 5 6 7 8 9 10 11 12 13 |
public partial class Form1 : Form { string nodeText = "新しいノード " + nodeNumber; TreeNode node = new TreeNode(nodeText); if (treeView1.SelectedNode != null) treeView1.SelectedNode.Nodes.Add(node); // 一番最後に追加するのだからInsertではなくAddを使う else CreateRootNode(); treeView1.SelectedNode = node; nodeNumber++; } |
選択されてるアイテムのひとつ上に追加
選択されてるアイテムのひとつ上に追加するときは選択されているTreeNodeがあるか調べます。ない場合はCreateRootNodeメソッドで最初の項目を追加します。
選択されているTreeNodeが存在する場合はさらにその親にあたるTreeNodeがあるかを調べます。ある場合は選択されているTreeNodeが親の上から何番目の子なのかを調べます。これはIndexOfメソッドを使えばわかります。
新しく生成したTreeNodeを親のNodesにInsertメソッドをつかって挿入します。このときの第一引数はIndexOfメソッドが返した値をそのまま使います。
選択されているTreeNodeの親が存在しない場合はTreeView.Nodesに対してInsertメソッドをつかって挿入します。このときの第一引数の求め方は親が存在する場合と同じです。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 |
public partial class Form1 : Form { private void ButtonAddItemAsPrev_Click(object sender, EventArgs e) { string nodeText = "新しいノード " + nodeNumber; TreeNode node = new TreeNode(nodeText); if (treeView1.SelectedNode != null) { TreeNode parentNode = treeView1.SelectedNode.Parent; if (parentNode != null) { int index = parentNode.Nodes.IndexOf(treeView1.SelectedNode); parentNode.Nodes.Insert(index, node); } else { int index = treeView1.Nodes.IndexOf(treeView1.SelectedNode); treeView1.Nodes.Insert(index, node); } } else { CreateRootNode(); } treeView1.SelectedNode = node; nodeNumber++; } } |
選択されてるアイテムのひとつ下に追加
選択されてるアイテムのひとつ下に追加するときも選択されてるアイテムのひとつ上に追加するときと基本は同じです。違いは親のNodesまたはTreeView.Nodesに新しいTreeNodeを挿入するためのInsertメソッドの引数です。IndexOfメソッドが返した値に1加えた数を使わなければなりません。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 |
public partial class Form1 : Form { private void ButtonAddItemAsNext_Click(object sender, EventArgs e) { string nodeText = "新しいノード " + nodeNumber; TreeNode node = new TreeNode(nodeText); if (treeView1.SelectedNode != null) { TreeNode parentNode = treeView1.SelectedNode.Parent; if (parentNode != null) { int index = parentNode.Nodes.IndexOf(treeView1.SelectedNode); parentNode.Nodes.Insert(index + 1, node); } else { int index = treeView1.Nodes.IndexOf(treeView1.SelectedNode); treeView1.Nodes.Insert(index + 1, node); } } else { CreateRootNode(); } treeView1.SelectedNode = node; nodeNumber++; } } |
削除するとき
選択されている項目を削除するのは簡単です。選択されているTreeNodeのRemoveメソッドを呼び出せば削除されます。この場合、選択されている項目だけでなくその子孫全体が削除されてしまいます。
1 2 3 4 5 6 7 8 9 10 |
public partial class Form1 : Form { private void ButtonDeleteItems_Click(object sender, EventArgs e) { if (treeView1.SelectedNode != null) { treeView1.SelectedNode.Remove(); } } } |
選択されている項目を移動させる
選択されている項目を移動させる場合、その多くはドラッグアンドドロップでおこないます。
最初にドラッグアンドドロップに対応させる処理が必要です。TreeView.AllowDrop = trueにすると同時にドラッグが開始されたとき、ドラッグされているとき、ドロップされたときのイベントハンドラを追加します。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 |
public partial class Form1 : Form { public Form1() { InitializeComponent(); treeView1.LabelEdit = true; treeView1.HideSelection = false; // 以下が必要 treeView1.AllowDrop = true; // ドラッグアンドドロップを許可 treeView1.ItemDrag += TreeView1_ItemDrag; // ドラッグが開始から終了まで treeView1.DragOver += TreeView1_DragOver; // ドラッグされているとき treeView1.DragDrop += TreeView1_DragDrop; // ドロップされたとき } } |
TreeView.ItemDragイベントはドラッグアンドドロップが開始された時に呼び出され、ドロップが完了したときに終了します。ItemDragEventArgs.Itemを調べればどのTreeNodeがドラッグされはじめたのかがわかります。
1 2 3 4 5 6 7 8 |
public partial class Form1 : Form { private void TreeView1_ItemDrag(object? sender, ItemDragEventArgs e) { treeView1.DoDragDrop((TreeNode?)e.Item, DragDropEffects.Move); Console.WriteLine("ドラッグアンドドロップ完了"); } } |
移動させる場合、移動先が移動元の子孫であると困ります。そこで本当に移動できるのかを調べるメソッドを作成しています。移動元と移動先のどちらかがnullであったり、移動先の親をたどっていくとそこに移動元のTreeNodeがある場合は移動できないのでfalseを返しています。最上位のTreeNodeにたどり着き、親は存在しないところまでたどり着いた場合は、移動先が移動元の子孫ではないので移動可能ということになります。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 |
public partial class Form1 : Form { bool CanMove(TreeNode dragSource, TreeNode dropTarget) { if(dragSource == null || dropTarget == null) return false; TreeNode? parent = dropTarget; while (true) { parent = parent.Parent; if (parent == dragSource) return false; if (parent == null) return true; } } } |
ドラッグされているときの処理を示します。ドロップを受け入れるのはTreeNodeだけです。DragEventArgs.Data.GetDataPresent(typeof(TreeNode))がtrueならTreeNodeがドラッグされていることになります。さらに TreeNode dragSource = (TreeNode)DragEventArgs.Data.GetData(typeof(TreeNode))とすればどのTreeNodeがドラッグされているかがわかります。
TreeNodeがドラッグされているときでも移動可能でない場合はドロップは受け入れません。移動可能であるときはDragDropEffects.Moveとし、それ以外の時はe.Effect = DragDropEffects.Noneとします。
またドロップ可能なTreeNodeのうえにマウスポインタが存在する場合はそのTreeNodeを選択状態にします。移動できない場合はドラッグされているTreeNodeを選択します。
マウスポインタがどのTreeNodeのうえに存在するかはどうやって調べればよいのでしょうか? マウスポインタがどこにあるかはDragEventArgs.XとDragEventArgs.Yを調べればわかります。ただしこの座標はスクリーン座標であり、TreeViewコントロール上のクライアント座標ではありません。そこでTreeView.PointToClientメソッドでクライアント座標で変換します。あとはTreeView.HitTestメソッドを呼び出せばわかります。
TreeView.HitTestメソッドはTreeViewHitTestInfoを返します。これを調べるとこの座標に存在するのはどのTreeNodeかだけでなくTreeViewHitTestInfo.Locationがどうなっているかで、TreeNodeのどの部分なのか(Label部分かアイコンを使用している場合はアイコン部分か?など)もわかります。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 |
public partial class Form1 : Form { private void TreeView1_DragOver(object? sender, DragEventArgs e) { if (e.Data.GetDataPresent(typeof(TreeNode))) { // ドラッグ元のTreeNodeを取得する TreeNode dragSource = (TreeNode)e.Data.GetData(typeof(TreeNode)); // new Point(e.X, e.Y); // e.X, e.Yはスクリーン座標 Point point = treeView1.PointToClient(new Point(e.X, e.Y)); TreeViewHitTestInfo info = treeView1.HitTest(point); if (info.Node != null) { // 移動可能なのか? if (dragSource != null && CanMove(dragSource, info.Node)) { // ドロップ先がわかるように該当する項目を選択状態にする treeView1.SelectedNode = info.Node; e.Effect = DragDropEffects.Move; return; } } // 移動可能ではない部分にマウスポインタがあるときは // マウスポインタをドラッグアンドドロップ不可にして ドラッグ元のTreeNodeを選択する treeView1.SelectedNode = dragSource; e.Effect = DragDropEffects.None; } } } |
ドロップされたときの処理を示します。
ドラッグされているTreeNodeとドロップのターゲットになるTreeNodeを取得する部分は同じです。あとはドラッグされているTreeNodeをTreeViewからいったん取り除き、これをドロップのターゲットになるTreeNodeの一番下の子として追加します。そして移動先のTreeNodeを選択状態にします。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 |
public partial class Form1 : Form { private void TreeView1_DragDrop(object? sender, DragEventArgs e) { // TreeView1_DragOverでe.Effect = DragDropEffects.None;だった場合はこの処理はおこなわれない if (e.Data.GetDataPresent(typeof(TreeNode))) { TreeNode dragSource = (TreeNode)e.Data.GetData(typeof(TreeNode)); Point point = treeView1.PointToClient(new Point(e.X, e.Y)); TreeViewHitTestInfo info = treeView1.HitTest(point); TreeNode node = (TreeNode)e.Data.GetData(typeof(TreeNode)); node.Remove(); info.Node.Nodes.Add(node); treeView1.SelectedNode = dragSource; } } } |