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);
}
}
}
実行イメージ
