UltraTree にはノードの選択履歴を残す機能がありません。今回はノードの選択履歴を残し、Ctrl + Z で選択履歴をさかのぼる機能を実装したいと思います。
概要
UltraTree コントロールを使用し、ツリー構造上でのノード選択履歴を管理・操作する機能をご紹介します。
主な機能としては、ユーザーがツリー内のノードを選択するたびにその履歴をスタックに保存し、Ctrl+Z キー操作により直前の選択状態に「戻る」ことが可能になっています。
実装機能について
ツリー構造の構築
- ノードの階層構造
- ルートノード「Top」を作成し、その下に「Fruits」や「Meat」などのカテゴリノードを追加。
- 各カテゴリ内で、さらに子ノード(例:「A」「B」「C」などのグループ、及びそれらの中に「Apple」「Orange」などのアイテム)が設定されています。
- 全ノードの展開
- ExpandAll() メソッドにより、ツリー全体が展開された状態で表示されます。
選択履歴の管理
- 履歴用スタック
- Stack<UltraTreeNode> 型の selectionHistory を用いて、ユーザーのノード選択履歴を保持。
- 初期状態では「Top」ノードを履歴に追加。
- ノード選択時のイベント処理
- AfterSelect イベントハンドラ内で、通常の選択操作が行われた場合に新たな選択ノードを履歴に追加。
- 履歴操作(Undo)中には、履歴の更新を防ぐために isUndoing フラグを活用。
「Undo」機能(Ctrl+Z 操作)
- キー操作の検出
- KeyDownイベントハンドラで、Ctrl + Z キーが押されたかをチェック。
- 履歴を用いた選択状態の復元
- 履歴スタックに複数のノードが存在する場合、現在の選択ノードを一旦スタックから取り除き、直前のノードを再選択。
- 再選択の際、 AfterSelect イベントで再び履歴に追加されないよう、isUndoing フラグを一時的に true に設定。
- ListBox への反映
- UpdateHistoryListBox メソッドで、スタックの内容を逆順(古い順)に ListBox に表示。これにより、ユーザーは履歴の流れを視覚的に確認できるようになります。
サンプルコード
public partial class Form1 : Form { // 選択履歴を保持するスタック private Stack<UltraTreeNode> selectionHistory = new Stack<UltraTreeNode>(); // 履歴操作中かどうかを示すフラグ(イベントループ防止用) private bool isUndoing = false; public Form1() { InitializeComponent(); InitializeUltraTree(); } private void Form1_Load(object sender, EventArgs e) {} private void InitializeUltraTree() { // ツリーデータの作成 // ルートノード: Top UltraTreeNode topNode = ultraTree1.Nodes.Add("Top", "Top"); // フルーツ UltraTreeNode fruitsNode = topNode.Nodes.Add("Fruits", "フルーツ"); // A UltraTreeNode aNode = fruitsNode.Nodes.Add("A", "A"); aNode.Nodes.Add("Apple", "りんご"); aNode.Nodes.Add("Orange", "みかん"); // B UltraTreeNode bNode = fruitsNode.Nodes.Add("B", "B"); bNode.Nodes.Add("Pear", "なし"); bNode.Nodes.Add("Grape", "ぶどう"); bNode.Nodes.Add("Banana", "ばなな"); // C UltraTreeNode cNode = fruitsNode.Nodes.Add("C", "C"); cNode.Nodes.Add("Peach", "もも"); // 肉 UltraTreeNode meatNode = topNode.Nodes.Add("Meat", "肉"); // D UltraTreeNode dNode = meatNode.Nodes.Add("D", "D"); dNode.Nodes.Add("Pork", "豚"); dNode.Nodes.Add("Beef", "牛"); // 全ノードを展開して表示 ultraTree1.ExpandAll(); // 初期選択(Top ノード)を履歴に追加 topNode.Selected = true; UpdateHistoryListBox(); } // Ctrl+Z キー操作により直前のノードに戻るイベントハンドラ private void ultraTree1_KeyDown(object sender, KeyEventArgs e) { // Ctrl + Z が押された場合の処理 if (e.Control && e.KeyCode == Keys.Z) { // 履歴に複数のノードがある場合のみ戻る if (selectionHistory.Count > 1) { // 履歴戻し操作中であることを示すフラグを true に設定 isUndoing = true; // 現在選択されているノードを履歴から取り除く(現在の状態を一旦破棄) selectionHistory.Pop(); // 履歴のトップにある直前のノードを取得する UltraTreeNode previousNode = selectionHistory.Peek(); // 直前のノードを再び選択状態にする // ※この際、AfterSelect イベントで履歴に再度追加されないように isUndoing フラグが true になっている previousNode.Selected = true; // 選択履歴の内容を ListBox に反映させるため、ListBox を更新する UpdateHistoryListBox(); // 履歴戻し操作が完了したので、フラグを false に戻す isUndoing = false; } } } /// UltraTree のノード選択後に呼び出されるイベントハンドラです。 /// このメソッドは、選択操作が履歴戻し操作中ではない場合に、 /// 現在選択されているノード(SelectedNodes[0])を選択履歴のスタックに追加し、 /// ListBox に履歴内容を反映させるための更新処理を行います。 /// 選択されているノードが1つだけの場合に処理を実施します。 private void ultraTree1_AfterSelect(object sender, SelectEventArgs e) { // isUndoing が false(つまり、履歴戻し操作中ではない)かつ // 選択されているノードが1つだけの場合に処理を実行する if (!isUndoing && ultraTree1.SelectedNodes.Count == 1) { // 現在選択されているノード(SelectedNodes[0])を選択履歴のスタックに追加する selectionHistory.Push(ultraTree1.SelectedNodes[0]); // 選択履歴の内容を ListBox に反映させるため、ListBox を更新する UpdateHistoryListBox(); } } /// selectionHistory の内容を ListBox に表示する private void UpdateHistoryListBox() { listBox1.Items.Clear(); foreach (var node in selectionHistory.Reverse()) { listBox1.Items.Add(node.Text); } } }
実行イメージ
