Skip to content

Instantly share code, notes, and snippets.

@thunklife
Created December 27, 2011 17:31
Show Gist options
  • Select an option

  • Save thunklife/1524481 to your computer and use it in GitHub Desktop.

Select an option

Save thunklife/1524481 to your computer and use it in GitHub Desktop.
MVC Tree View alternative using ExpressionBuilder objects
public static class FluentTreeViewHelper
{
/// <summary>
/// Create an HTML tree from a recursive collection of items
/// </summary>
public static FluentTreeView<T> FluentTreeView<T>(this HtmlHelper html, IEnumerable<T> items)
{
return new FluentTreeView<T>(html, items);
}
}
public class FluentTreeView<T> : IHtmlString
{
private readonly HtmlHelper _html;
private readonly IEnumerable<T> _items = Enumerable.Empty<T>();
internal Func<T, string> DisplayProperty = item => item.ToString();
internal Func<T, IEnumerable<T>> ChildrenProperty;
private string _emptyContent = "No children";
internal IDictionary<string, object> HtmlAttributes = new Dictionary<string, object>();
internal IDictionary<string, object> ChildHtmlAttributes = new Dictionary<string, object>();
internal Func<T, HelperResult> ItemTemplate;
public FluentTreeView(HtmlHelper html, IEnumerable<T> items)
{
if (html == null) throw new ArgumentNullException("html");
_html = html;
_items = items;
// The ItemTemplate will default to rendering the DisplayProperty
ItemTemplate = item => new HelperResult(writer => writer.Write(DisplayProperty(item)));
}
public ChildItemsExpression<T> EmptyContent(string emptyContent)
{
if (emptyContent == null) throw new ArgumentNullException("emptyContent");
_emptyContent = emptyContent;
return new ChildItemsExpression<T>(this);
}
public override string ToString()
{
ValidateSettings();
var listItems = _items.ToList();
var ul = new TagBuilder("ul");
ul.MergeAttributes(HtmlAttributes);
if (listItems.Count() == 0)
{
var li = new TagBuilder("li")
{
InnerHtml = _emptyContent
};
ul.InnerHtml = li.ToString();
}
foreach (var item in listItems)
{
RenderLi(ul, item);
AppendChildren(ul, item, ChildrenProperty);
}
return ul.ToString();
}
public string ToHtmlString()
{
return ToString();
}
public void Render()
{
var writer = _html.ViewContext.Writer;
using (var textWriter = new HtmlTextWriter(writer))
{
textWriter.Write(ToString());
}
}
private void ValidateSettings()
{
if (ChildrenProperty == null)
{
throw new InvalidOperationException("You must call the Children() method to tell the tree view how to find child items");
}
}
private void AppendChildren(TagBuilder ul, T parent, Func<T, IEnumerable<T>> childrenProperty)
{
var children = childrenProperty(parent).ToList();
if (children.Count() == 0)
{
return;
}
var innerUl = new TagBuilder("ul");
innerUl.MergeAttributes(ChildHtmlAttributes);
foreach (T item in children)
{
RenderLi(innerUl, item);
AppendChildren(innerUl, item, childrenProperty);
}
ul.InnerHtml += innerUl.ToString();
}
private void RenderLi(TagBuilder ul, T item)
{
var li = new TagBuilder("li")
{
InnerHtml = ItemTemplate(item).ToHtmlString()
};
ul.InnerHtml += li.ToString();
}
}
public class ChildItemsExpression<T>
{
private readonly FluentTreeView<T> _treeView;
public ChildItemsExpression(FluentTreeView<T> treeView)
{
if (treeView != null)
{
_treeView = treeView;
}
}
public ItemTemplateExpression<T> Children(Func<T, IEnumerable<T>> selector)
{
if (selector == null) throw new ArgumentNullException("selector");
_treeView.ChildrenProperty = selector;
return new ItemTemplateExpression<T>(_treeView);
}
}
public class ItemTemplateExpression<T>
{
private readonly FluentTreeView<T> _treeView;
public ItemTemplateExpression(FluentTreeView<T> treeView)
{
if (treeView != null)
{
_treeView = treeView;
}
}
public HtmlAttributeExpression<T> ItemText(Func<T, string> selector)
{
if (selector == null) throw new ArgumentNullException("selector");
_treeView.DisplayProperty = selector;
return new HtmlAttributeExpression<T>(_treeView);
}
public HtmlAttributeExpression<T> ItemTemplate(Func<T, HelperResult> itemTemplate)
{
if (itemTemplate == null) throw new ArgumentNullException("itemTemplate");
_treeView.ItemTemplate = itemTemplate;
return new HtmlAttributeExpression<T>(_treeView);
}
}
public class HtmlAttributeExpression<T>
{
private readonly FluentTreeView<T> _treeView;
public HtmlAttributeExpression(FluentTreeView<T> treeView)
{
if (treeView != null)
{
_treeView = treeView;
}
}
public ChildHtmlAttributExpression<T> HtmlAttributes(object htmlAttributes)
{
HtmlAttributes(HtmlHelper.AnonymousObjectToHtmlAttributes(htmlAttributes));
return new ChildHtmlAttributExpression<T>(_treeView);
}
public ChildHtmlAttributExpression<T> HtmlAttributes(IDictionary<string, object> htmlAttributes)
{
if (htmlAttributes == null) throw new ArgumentNullException("htmlAttributes");
_treeView.HtmlAttributes = htmlAttributes;
return new ChildHtmlAttributExpression<T>(_treeView);
}
public ChildHtmlAttributExpression<T> WithDefaultHtmlAttributes()
{
return new ChildHtmlAttributExpression<T>(_treeView);
}
}
public class ChildHtmlAttributExpression<T>
{
private readonly FluentTreeView<T> _treeView;
public ChildHtmlAttributExpression(FluentTreeView<T> treeView)
{
if (treeView != null)
{
_treeView = treeView;
}
}
public FluentTreeView<T> ChildHtmlAttributes(object htmlAttributes)
{
return ChildHtmlAttributes(HtmlHelper.AnonymousObjectToHtmlAttributes(htmlAttributes));
}
public FluentTreeView<T> ChildHtmlAttributes(IDictionary<string, object> htmlAttributes)
{
if (htmlAttributes == null) throw new ArgumentNullException("htmlAttributes");
_treeView.ChildHtmlAttributes = htmlAttributes;
return _treeView;
}
public FluentTreeView<T> WithDefaultChildHtmlAttributes()
{
return _treeView;
}
}
@fidoogle
Copy link
Copy Markdown

fidoogle commented Jan 5, 2018

Nice

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment