diff --git a/Terminal.Gui/Views/TreeView.cs b/Terminal.Gui/Views/TreeView.cs
index 144b9c502..e5bc56b37 100644
--- a/Terminal.Gui/Views/TreeView.cs
+++ b/Terminal.Gui/Views/TreeView.cs
@@ -12,11 +12,21 @@ namespace Terminal.Gui {
///
public interface ITreeNode
{
+ ///
+ /// Text to display when rendering the node
+ ///
+ string Text {get;set;}
+
///
/// The children of your class which should be rendered underneath it when expanded
///
///
IList Children {get;}
+
+ ///
+ /// Optionally allows you to store some custom data/class here.
+ ///
+ object Tag {get;set;}
}
///
@@ -36,6 +46,11 @@ namespace Terminal.Gui {
///
public string Text {get;set;}
+ ///
+ /// Optionally allows you to store some custom data/class here.
+ ///
+ public object Tag {get;set;}
+
///
/// returns
///
@@ -228,6 +243,7 @@ namespace Terminal.Gui {
public TreeView ()
{
TreeBuilder = new TreeNodeBuilder();
+ AspectGetter = o=>o == null ? "Null" : (o.Text ?? o?.ToString() ?? "Unamed Node");
}
}
diff --git a/UICatalog/Scenarios/InteractiveTree.cs b/UICatalog/Scenarios/InteractiveTree.cs
new file mode 100644
index 000000000..4536b0ad1
--- /dev/null
+++ b/UICatalog/Scenarios/InteractiveTree.cs
@@ -0,0 +1,150 @@
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+using System.Threading.Tasks;
+using Terminal.Gui;
+using static UICatalog.Scenario;
+
+namespace UICatalog.Scenarios {
+
+ [ScenarioMetadata (Name: "Interactive Tree", Description: "Create nodes and child nodes in TreeView")]
+ [ScenarioCategory ("Controls")]
+ class InteractiveTree : Scenario{
+
+ TreeView treeView;
+
+ public override void Setup ()
+ {
+ Win.Title = this.GetName();
+ Win.Y = 1; // menu
+ Win.Height = Dim.Fill (1); // status bar
+ Top.LayoutSubviews ();
+
+ var menu = new MenuBar (new MenuBarItem [] {
+ new MenuBarItem ("_File", new MenuItem [] {
+ new MenuItem ("_Quit", "", () => Quit()),
+ })
+ });
+
+ treeView = new TreeView() {
+ X = 0,
+ Y = 0,
+ Width = Dim.Fill(),
+ Height = Dim.Fill(1),
+ };
+ treeView.KeyPress += TreeView_KeyPress;
+
+ Win.Add(treeView);
+
+ var statusBar = new StatusBar (new StatusItem [] {
+ new StatusItem(Key.CtrlMask | Key.Q, "~^Q~ Quit", () => Quit()),
+ new StatusItem(Key.CtrlMask | Key.C, "~^C~ Add Child", () => AddChildNode()),
+ new StatusItem(Key.CtrlMask | Key.T, "~^T~ Add Root", () => AddRootNode()),
+ new StatusItem(Key.CtrlMask | Key.R, "~^R~ Rename Node", () => RenameNode()),
+ });
+ Top.Add (statusBar);
+
+ }
+
+ private void TreeView_KeyPress (View.KeyEventEventArgs obj)
+ {
+ if(obj.KeyEvent.Key == Key.DeleteChar) {
+
+ var toDelete = treeView.SelectedObject;
+
+ if(toDelete == null)
+ return;
+
+ obj.Handled = true;
+
+ // if it is a root object remove it
+ if(treeView.Objects.Contains(toDelete)) {
+ treeView.Remove(toDelete);
+ }
+ else {
+ var parent = treeView.GetParent(toDelete);
+
+ if(parent == null)
+ MessageBox.ErrorQuery("Could not delete",$"Parent of '{toDelete}' was unexpectedly null","Ok");
+ else {
+ //update the model
+ parent.Children.Remove(toDelete);
+
+ //refresh the tree
+ treeView.RefreshObject(parent);
+ }
+ }
+ }
+ }
+
+ private void RenameNode ()
+ {
+ var node = treeView.SelectedObject;
+
+ if(node != null) {
+ if(GetText("Text","Enter text for node:",node.Text,out string entered)) {
+ node.Text = entered;
+ treeView.RefreshObject(node);
+ }
+ }
+ }
+
+ private void AddRootNode ()
+ {
+ if(GetText("Text","Enter text for node:","",out string entered)) {
+ treeView.AddObject(new TreeNode(entered));
+ }
+ }
+
+ private void AddChildNode()
+ {
+ var node = treeView.SelectedObject;
+
+ if(node != null) {
+ if(GetText("Text","Enter text for node:","",out string entered)) {
+ node.Children.Add(new TreeNode(entered));
+ treeView.RefreshObject(node);
+ }
+ }
+ }
+
+ private bool GetText(string title, string label, string initialText, out string enteredText)
+ {
+ bool okPressed = false;
+
+ var ok = new Button ("Ok", is_default: true);
+ ok.Clicked += () => { okPressed = true; Application.RequestStop (); };
+ var cancel = new Button ("Cancel");
+ cancel.Clicked += () => { Application.RequestStop (); };
+ var d = new Dialog (title, 60, 20, ok, cancel);
+
+ var lbl = new Label() {
+ X = 0,
+ Y = 1,
+ Text = label
+ };
+
+ var tf = new TextField()
+ {
+ Text = initialText,
+ X = 0,
+ Y = 2,
+ Width = Dim.Fill()
+ };
+
+ d.Add (lbl,tf);
+ tf.SetFocus();
+
+ Application.Run (d);
+
+ enteredText = okPressed? tf.Text.ToString() : null;
+ return okPressed;
+ }
+
+ private void Quit ()
+ {
+ Application.RequestStop ();
+ }
+ }
+}
diff --git a/UICatalog/Scenarios/TreeUseCases.cs b/UICatalog/Scenarios/TreeUseCases.cs
index 9a5358442..cd09aedc4 100644
--- a/UICatalog/Scenarios/TreeUseCases.cs
+++ b/UICatalog/Scenarios/TreeUseCases.cs
@@ -43,7 +43,7 @@ namespace UICatalog.Scenarios {
}
// Your data class
- private class House : ITreeNode {
+ private class House : TreeNode {
// Your properties
@@ -59,14 +59,10 @@ namespace UICatalog.Scenarios {
return Address;
}
}
- private class Room : ITreeNode{
+ private class Room : TreeNode{
public string Name {get;set;}
-
- // Rooms have no sub objects
- public IList Children => new List();
-
public override string ToString ()
{
return Name;
diff --git a/docfx/articles/treeview.md b/docfx/articles/treeview.md
index 848a191b8..1000d10ad 100644
--- a/docfx/articles/treeview.md
+++ b/docfx/articles/treeview.md
@@ -34,7 +34,7 @@ Having to create a bunch of TreeNode objects can be a pain especially if you alr
```csharp
// Your data class
-private class House : ITreeNode {
+private class House : TreeNode {
// Your properties
@@ -52,7 +52,7 @@ private class House : ITreeNode {
}
// Your other data class
-private class Room : ITreeNode{
+private class Room : TreeNode{
public string Name {get;set;}