I have created a custom HtmlHelper
to render a Grid
, and it's working fine, but I know for sure that it isn't written really nicely.
I'll provide you with the code that's most important for the grid.
First of all, the model which I pass to my grid is an IEnumerable<T>
.
Then, the Razor syntax for the grid is as follows:
@(Html.GridFor()
.Name("PageOverviewGrid")
.WithColumns(model =>
{
model.Bind(x => x.Name);
model.Bind(x => x.DateCreated);
model.Bind(x => x.DateUpdated);
})
)
Then I do have a custom HtmlHelper to render this grid:
public static IGridBuilder<TModel> GridFor<TModel>(this HtmlHelper<IEnumerable<TModel>> htmlHelper)
{
return new GridBuilder<TModel>(htmlHelper);
}
Then the GridBuilder
class itself (it's constructed with interfaces so I can use a fluent API):
public class GridBuilder<TModel> : IGridBuilder<TModel>
{
#region Constructors
/// <summary>
/// Creates a new instance of the <see cref="GridBuilder{TModel}"/>.
/// </summary>
/// <param name="htmlHelper">The <see cref="HtmlHelper{TModel}"/> that is used to render this one.</param>
public GridBuilder(HtmlHelper<IEnumerable<TModel>> htmlHelper)
{
this.htmlHelper = htmlHelper;
this.properties = new Dictionary<string, string>();
}
#endregion
#region Properties
public Dictionary<string, string> properties;
#endregion
#region IGridBuilder Members
/// <summary>
/// Gets the name of the <see cref="IGridBuilder{TModel}"/>.
/// </summary>
public string name { get; private set; }
/// <summary>
/// The <see cref="HtmlHelper"/> that is used to build the grid.
/// </summary>
public HtmlHelper htmlHelper { get; private set; }
/// <summary>
/// Sets the name of the <see cref="IGridBuilder{TModel}"/>.
/// </summary>
/// <param name="name">The name that the <see cref="IGridBuilder{TModel}"/> should have.</param>
/// <returns>An <see cref="IGridBuilder{TModel}"/> that is used to construct the grid.</returns>
public IGridBuilder<TModel> Name(string name)
{
this.name = name;
return this;
}
/// <summary>
/// Binds an column to the grid.
/// </summary>
/// <typeparam name="TItem">The type of the column on which to bind the items.</typeparam>
/// <param name="function">The functional that will bind the control to the grid.</param>
public void Bind<TItem>(Expression<Func<TModel, TItem>> function)
{
var metadata = ModelMetadataProviders.Current.GetMetadataForProperty(() => Activator.CreateInstance<TModel>(), typeof(TModel), name);
properties.Add(metadata.PropertyName, metadata.DisplayName ?? metadata.PropertyName);
}
/// <summary>
/// Set the columns of the model that should be bound to grid.
/// </summary>
/// <param name="action">The action that will bind all the columns.</param>
/// <returns>An <see cref="IGridBuilder{TModel}"/> that is used to construct the grid.</returns>
public IGridBuilder<TModel> WithColumns(Action<IGridBuilder<TModel>> action)
{
action.Invoke(this);
return this;
}
#endregion
#region IHtmlString Members
/// <summary>
/// Returns an HTML-encoded string.
/// </summary>
/// <returns>Returns an HTML-encoded string.</returns>
public string ToHtmlString()
{
// Additional rendering is done here.
}
}
And then now the things about which I'm concerned:
Retrieving the property display name or property name. (It's done through reflection right now, but I'm thinking that there is more friendly solution).
How can I render the data of the entity. For example, I want to have the DateCreated
of a single entity in the grid.
foreach (var entity in htmlHelper.ViewData.Model as IEnumerable<GridPageFolderViewModel>)
{
// For the entity get the properties and look in the directory of properties if it should be displayed. If that's the case, render it.
}
I have the feeling that this could be optimized, but it's my very first HTML extension. I would appreciate any help.