c# - Writing an MVC HtmlHelper for an IEnumerable<TModel> to display an HTML Table -


i trying write customer htmlhelper allows easy creation of html table view being passed ienumerable <tmodel> tmodel model object type. calling syntax view of style:

@html.tablefor(model => model.addeduserid, model => model.clientid......., model model => anothrefildiwantdisplayed)); 

i starting off trying working 1 expression being passed first e.g.

@html.tablefor(model => model.addeduserid) 

once works comma separated list working using params , taking more 1 expression.

my view code looks this

@using mynamespacetomyhelper @model ienumerable<user>  @{     viewbag.title = "index"; } <p>  </p>  @html.tablefor(model => model.addeduserid)); 

my attempt @ writing htmlhelper is:

using system; using system.collections.generic; using system.io; using system.linq; using system.linq.expressions; using system.text; using system.threading.tasks; using system.web; using system.web.mvc; using system.web.mvc.html; using system.web.ui;  namespace mynamespacetomyhelper {     public static class gridextensions     {         public static mvchtmlstring tablefor<tmodel, tvalue>(this htmlhelper<ienumerable<tmodel>> html, expression<func<tmodel, tvalue>> expression)         {             var writer = new htmltextwriter(new stringwriter());              writer.renderbegintag(htmltextwritertag.table);              writer.renderbegintag(htmltextwritertag.thead);              writer.renderbegintag(htmltextwritertag.th);             writer.renderbegintag(htmltextwritertag.td);              //colum headers             writer.write(html.displaynamefor(expression));              writer.renderendtag();             writer.renderendtag();              writer.renderendtag();//close thead              //column data             //html.displayfor((expression<func<tmodel, tvalue>>)expression);             foreach (viewdatadictionary<tmodel> vdm in html.viewdata.values)             {                 writer.renderbegintag(htmltextwritertag.tr);                 writer.renderbegintag(htmltextwritertag.td);                  //doesn't compile                 html.displayfor(expression);                 writer.renderendtag();                 writer.renderendtag();             }               writer.renderendtag(); //close header               return new mvchtmlstring(writer.innerwriter.tostring());         }     }  } 

this not compile because of:

html.displayfor(expression);

as far understanding goes because htmlhelper passed method htmlhelper<ienumerable<tmodel>> whereas when using displayfor needs htmlhelper<tmodel>.

because of error seems feasible working splitting table header , data 2 separate method calls , view call header method have loop inside actual view calls method each row or something. this

@using mynamespacetomyhelper @model ienumerable<user>  @{     viewbag.title = "index"; } <p>  </p>  @html.tableheadersfor(model => model.addeduserid)); @foreach (var item in model) {        @html.tablerowsfor(model => model.addeduserid)); } 

this should work because tableheadersfor can call displaynameextensions.displaynamefor takes htmlhelper<ienumerable<tmodel>> , tablerowsfor take htmlhelper<tmodel> , call displayextensions.displayfor htmlhelper<tmodel> incompatible htmlhelper types problem go away.

however not ideally want if @ possible want calling syntax simple possible. basic premise of question try , maintain simple calling syntax without writing loop in view , duplicating expressions choosing columns. entire purpose here design re-usable , easy use others possible

i had in ilspy @ how displaynameextensions.displaynamefor works handles htmlhelper<ienumerable<tmodel>> looks somehow exstension method taking htmlhelper<ienuermable<tmodel>> calling internally extension method on htmlhelper<tmodel> code ilspy producing doesn't recompile getting no understanding how .net able this.

is possible in 1 extension method or forced split compromising calling syntax?

update 1 have managed following working quite lot of work:

@html.begintablefor(model => model.addeduserid                     , model => model.active                     , new { @class = "grid" })   @foreach (var item in model) {      @html.displayrowfor(model => item.addeduserid                         , model => item.active) }  @html.endtablefor() 

it's not concise , involves heap of work because each expression begintablefor , displayrowfor can return different type meaning have had implement method overload number of expressions (i've done 100 work upto 100 columns!). same problem had in .net action , func delegates had write different versions different numbers of parameters e.g. action t1, t2,..,t16. it's extremely messy rather have nice calling syntax avoid many method overloads.

have @ this article direction , modify according needs.

first need html extensions this:

public static class razorextensions {     public static helperresult list<t>(this ienumerable<t> items,        func<t, helperresult> template) {         return new helperresult(writer => {             foreach (var item in items) {                 template(item).writeto(writer);             }         });     } } 

then in html can following:

{     var comics = new[] {          new comicbook {title = "groo", publisher = "dark horse comics"},         new comicbook {title = "spiderman", publisher = "marvel"}     }; }  <table> @comics.list(   @<tr>     <td>@item.title</td>     <td>@item.publisher</td>   </tr>) </table> 

Comments