3

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 DSGridHelper3<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!

2
  • But here ye be warned - careful with the column name extraction - it doesn't work all the time. The database is basically the Elmah table. When trying to get the ErrorID the expression is actually {x => Convert(x.ErrorId)} which fails in the above code. SOLUTION WELCOME TO THIS TOO! Commented Jun 6, 2011 at 21:09
  • Opened a separate question here stackoverflow.com/questions/6269822/… Commented Jun 7, 2011 at 18:21

1 Answer 1

2

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;
    }
}
Sign up to request clarification or add additional context in comments.

6 Comments

Code is mostly working (SUPER) intellisense wise but struggling with an error when running it: The model item passed into the dictionary is of type 'System.Data.Objects.ObjectQuery1[System.String]', but this dictionary requires a model item of type 'System.Collections.Generic.IEnumerable1
I assume its somewhere in your grid class, maybe when you're adding your columns? I'd have to see a bit more of your code to see whats causing the issue.
SUCCESS! I was doing something very silly LOL. It helps if one passes the correct model type to the view. blush. I'll post my code in a sec. BTW - ur a genius!
Looks good, glad I could help. I'll take a look at the Elmah issue you're having.
One more question I have just thought of - how would I best pass a different model into the implementation you have helped me out with? I may have two tables (maybe a smaller one) that I add to the page using ViewData rather than the model. Thank you once again Justin!
|

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.