MVC3 razor based htmlhelper with lambda field expressions based on model
I am creating a fluent HtmlHelper in MVC - to create a grid based on HTML. I am aware of mvc contrib and WebGrid - but I am making my own and have a specific problem:
I have to enter this:
@Html.DSGridFor().AddColumn(x=>x.FirstOrDefault().Message)
but I want to be able to type this:
@Html.DSGridFor().AddColumn(x=>x.Message)
The code that gets called when I start with @Html.DSGridFor() - taking in the page based model.
public static DSGridHelper<TModel> DSGridFor<TModel>(this HtmlHelper<TModel> html)
{
return new DSGridHelper<TModel>(html);
}
and then within the class DSGridHelper I have this:
public DSGridHelper<TModel> AddColumn(Expression<Func<TModel, dynamic>> property, string HeaderText = null)
{
string ColumnName = (property.Body as MemberExpression).Member.Name;
DSGridColumn DSGC = new DSGridColumn();
DSGC.ColumnName = ColumnName;
DSGC.HeaderText = HeaderText ?? ColumnName;
DSColumnList.Add(DSGC);
return this;
}
public List<DSGridColumn> DSColumnList { get; set; }
and the column class at the moment is really basic:
public class DSGridColumn
{
public DSGridColumn()
{
}
public string ColumnName { get; set; }
public string HeaderText { get; set; }
}
I can get this code working fine with string based column names, but I want the declaring code in the razor page to be simple in format and strongly typed. At the moment I have to type x=>x.First().Message but I really only need x=>x.Message to identify the column.
I appreciate any help.
UPDATE
Thanks to Justin I can now provide my/our code.
View:
@(Html.DSGridFor3().AddColumn(x => x.Message)
.AddColumn(x => x.Host)
.ToMvcString())
HTML Helper call:
public static DSGridHelper3<T> DSGridFor3<T>(this HtmlHelper<IEnumerable<T>> htmlHelper)
{
return new DSGridHelper3<T>(htmlHelper);
}
Returning class:
public class DSGridHelper3<T>
{
private HtmlHelper _htmlHelper;
//private IEnumerable<T> _dataList;
public List<DSGridColumn> DSColumnList { get; set; }
public DSGridHelper3(HtmlHelper<IEnumerable<T>> htmlHelper)
{
_htmlHelper = htmlHelper;
// _dataList = htmlHelper.ViewData.Model;
DSColumnList = new List<DSGridColumn>();
}
public DSGridHelper3<T> AddColumn(Expression<Func<T, object>> property)
{
string columnName = (property.Body as MemberExpression).Member.Name;
DSGridColumn DSGC = new DSGridColumn();
DSGC.ColumnName = columnName;
DSGC.HeaderText = columnName;
DSColumnList.Add(DSGC);
return this;
}
public MvcHtmlString ToMvcString()
{
sb.Append("<table>");
sb.Append("<tr>");
sb.Append("<td>");
sb.Append("hello world within a table");
sb.Append(@"</td>");
sb.Append("<td>");
sb.Append("hello world within a table");
sb.Append(@"</td>");
sb.Append(@"</tr>");
sb.Append(@"</table>");
return new MvcHtmlString(sb.ToString());
}
}
UPDATE 2
If you wanted to manually insert a different type (perhaps because you are going to get a small amount of table data from ViewData rather than the model of the page) then here is some more code:
View:
@(Html.DSGridFor3<DanSoftware.MVC.Areas.Errors.Code.ELMAH_Error>().AddColumn(x => x.Message).ToMvcString();)
Alternative signature for the DSGridHelper ...helper
public static DSGridHelper3<T> DSGridFor3<T>(this HtmlHelper htmlHelper)
{
return new DSGridHelp开发者_如何学Pythoner3<T>(htmlHelper);
}
Additional constructor:
public DSGridHelper3(HtmlHelper htmlHelper)
{
_htmlHelper = htmlHelper;
// _dataList = htmlHelper.ViewData.Model;
DSColumnList = new List<DSGridColumn>();
}
Hope this helps someone and thanks Justin!
I dont have Visual Studio with me but I'll take a stab at this...
I would take in a collection as a datatype either in your DsGridFor
method or in the AddColumn
method. This will allow you to send Strongly-typed arguments from a collection. Say you wanted a generic method of AddColumn
for a given collection with access to the class properties vs the collection methods, it would look something like this (just an example):
public static DSGridHelper<T> AddColumn<T>(this HtmlHelper<IEnumerable<T>> htmlHelper, Expression<Func<T, object>> property) where T : class
{
string columnName = (property.Body as MemberExpression).Member.Name;
DSGridColumn DSGC = new DSGridColumn();
DSGC.ColumnName = ColumnName;
DSGC.HeaderText = HeaderText ?? ColumnName;
DSColumnList.Add(DSGC);
return this;
}
For your situation, to new-up a DsGridHelper
class I might explicitly set a model-type first and then add overloads as I go:
public static DSGridHelper<T> DSGridFor<T>(this HtmlHelper<IEnumerable<T>> htmlHelper) where T : class
{
return new DSGridHelper<T>(htmlHelper);
}
And then my DsGridHelper might look something like this:
public class DsGridHelper<T>
{
private HtmlHelper _htmlHelper;
private IEnumerable<T> _dataList;
public DsGridHelper(HtmlHelper<IEnumerable<T>> htmlHelper)
{
_htmlHelper = htmlHelper;
_dataList = htmlHelper.ViewData.Model;
}
public DsGridHelper<T> AddColumn(Expression<Func<T, object>> property)
{
string columnName = (property.Body as MemberExpression).Member.Name;
DSGridColumn DSGC = new DSGridColumn();
DSGC.ColumnName = ColumnName;
DSGC.HeaderText = HeaderText ?? ColumnName;
DSColumnList.Add(DSGC);
return this;
}
}
精彩评论