sealed class Dumper { public static string Dump(Node root) { // get all nodes (used later for "to parent" iteration var allNodeTuples = EnumerateNodes(root, null); // main query, later we'll agregate results var query = allNodeTuples.Select(tuple => { var node = tuple.Item1; var parent = tuple.Item2; var s = new List {node.Text, Environment.NewLine}; if (parent != null) { // thats for 'closest' childs s.Insert(0, parent.Children.Last() != node ? "├" : "└"); s.Insert(1,"-"); do { // build indention node = parent; var oldParent = parent; parent = allNodeTuples.Where(t => t.Item1 == oldParent).FirstOrDefault().Item2; if (parent != null) s.Insert(0, (parent.Children.Last() != node ? "│ " : " ")); } while (parent != null); } // joins results into single line return string.Join(String.Empty,s); }); // run query with string builder to aggregate return query.Aggregate(new StringBuilder(), (s, v) => s.Append(v)).ToString(); } // Enumerate all tree nodes, returning tuple with first item as node, second as node's parent private static IEnumerable> EnumerateNodes(Node root, Node parent) { /* recursive: yield return new Tuple(root, parent); foreach (var child in root.Children) foreach (var sub in EnumerateNodes(child, root)) yield return sub;*/ var workStack = new Stack>(); workStack.Push(new Tuple(root, parent)); while (workStack.Count>0) { var current = workStack.Pop(); yield return current; foreach (var child in current.Item1.Children.Reverse()) workStack.Push(new Tuple(child, current.Item1)); } } }