The fallowing code snip is designed to take in a flat list of file paths (or similar data) and produce a hierarchy of tree nodes representing those file paths.
/// <summary>
/// Constructs a nested hierarchy of types from a flat list of source types.
/// </summary>
/// <typeparam name="TSource">The source type of the flat list that is to be converted.</typeparam>
/// <typeparam name="TReturn">The type that will be returned.</typeparam>
/// <typeparam name="TPart">The type of the art type.</typeparam>
/// <param name="sourceItems">The source items to be converted.</param>
/// <param name="getParts">A callback function that returns a array of <see cref="TPart"/>.</param>
/// <param name="comparePart">The compare part callback.</param>
/// <param name="getChildren">The get children callback.</param>
/// <param name="addChild">The add child callback.</param>
/// <param name="createItem">The create item callback.</param>
/// <returns>Returns an collection of <see cref="TReturn"/> representing the hierarchy.</returns>
/// <exception cref="Exception">A delegate callback throws an exception. </exception>
private static IEnumerable<TReturn> ToHierarchy<TSource, TReturn, TPart>(
IEnumerable<TSource> sourceItems,
Func<TSource, TPart[]> getParts,
Func<TReturn, TPart, bool> comparePart,
Func<TReturn, IEnumerable<TReturn>> getChildren,
Action<IEnumerable<TReturn>, TReturn> addChild,
Func<TPart[], int, TSource, TReturn> createItem)
{
var treeModels = new List<TReturn>();
foreach (var keyName in sourceItems)
{
IEnumerable<TReturn> items = treeModels;
var parts = getParts(keyName);
for (var partIndex = 0; partIndex < parts.Length; partIndex++)
{
var node = items.FirstOrDefault(x => comparePart(x, parts[partIndex]));
if (node != null)
{
items = getChildren(node);
continue;
}
var model = createItem(parts, partIndex, keyName);
addChild(items, model);
items = getChildren(model);
}
}
return treeModels;
}
An example of how one could use the ToHierarchy method would be like this …
var separator = new[] { Path.AltDirectorySeparatorChar.ToString(CultureInfo.InvariantCulture) };
// paths varible could be something from Directory.GetDirectories method for example.
var nodes = ToHierarchy<string, TreeViewNode, string>(
paths.OrderBy(x => x),
x => x.Split(separator, StringSplitOptions.RemoveEmptyEntries),
(r, p) => string.CompareOrdinal(r.Name, p) == 0,
r => r.Nodes,
(r, c) => ((List<TreeViewNode>)r).Add(c),
this.CreateTreeNode);
private TreeViewNode CreateTreeNode(string[] parts, int index, string source)
{
var node = new TreeViewNode() { Name = parts[index] };
node.Value = string.Join(Path.DirectorySeparatorChar.ToString(CultureInfo.InvariantCulture), parts, 0, index + 1);
if (index == parts.Length - 1)
{
node.Name = Path.GetFileName(source);
}
node.IsFile = File.Exists(node.Value);
return node;
}
Where paths is a array of file paths from say Directory.GetFiles.