Conditional classes on rows

Sep 20, 2012 at 3:44 PM

I would like to add some classes to my grid rows depending on a property on the grid item. It might be possible in the current setup, but I would have to extend a lot of base classes for it to happen.

The GridCellRenderer does have classes and styles, but the methods to add more are protected, so I would have to extend GridCellRenderer to set custom classes. To provide a new GridCellRenderer I will have to extend or replace GridColumn to provide the new renderer. To use the new GridColumn class I would also have to extend or replace the DefaultColumnBuilder. At last I will be able to replace the column collection in my custom grid class with a new one initialized with my custom column builder.

A rather tedious way just to do a simple thing like adding classes to items I think :) it might just be me not seeing the obvious way though?

Sep 21, 2012 at 5:35 AM

Yes it is.

That's cool that you have such problems - we can see it and can improve the grid.

There are some ways to solve this problem.

The best way - allow user to specify style settings declaratively, for example:

columns.Add(o => o.Customers.IsVip).AddCssClass("myclass");

I think made a default cell renderer as internal was a mistake and we need to open his. After that you can make an extension method to do this, or specify it obviously:


var column = columns.Add(o => o.Customers.CompanyName)
                    .Titled("Company Name");


Also I think we need  specify a public setter for CellRenderer property, if you want to replace the default behavior (rendering "<td>" element).

I made a some fixes for this problem, you can see it in the repository, if it's ok I will update the grid in the near future.

Other methods:

You can modify "_Grid.cshtml" or create a new one, and use it to render custom columns, like this:





@foreach (IGridColumn column in Model.Columns)
    if(column.Name == "CustomColumn")
          <td class="customclass">@column.GetCell(item).ToString()</td>
        @Html.Raw(column.CellRenderer.Render(column, column.GetCell(item).ToString()))


Not so good, but...

Other method - use a client side (javascript) to select cells and specify their class.




Sep 21, 2012 at 6:11 AM

I am happy to help you improve this grid :) I really like the structure of it.

I will take a look at your fixes later.

Sep 21, 2012 at 12:50 PM

Your changes look nice for enabling classes on column basis and customizing column rendering even more.

I downloaded the source code and played around with it a bit to see how to best solve the row class problem. It ended up with modifying GridBase and IGrid being the best way to add classes to specific rows.

I have added the following lines to IGrid.cs:


IDictionary<object, List<string>> ItemClasses { get; }
string GetItemClasses(object item);


This default implementation to GridBase.cs:


private IDictionary<object, List<string>> _classes;
public IDictionary<object, List<string>> ItemClasses { get { return _classes; } }
public string GetItemClasses(object item)
     List<string> cls;
     ItemClasses.TryGetValue(item, out cls);
     return String.Join(" ", cls);


Default initilializing of _classes in GridBase.cs constructor:


_classes = items.ToDictionary(s => (object)s, s => new List<string>());


And at last changed line 36 in _Grid.cshtml:

<tr class="grid-row @Model.GetItemClasses(item)">

It works out nicely for me adding custom row classes in the constructor of my derived Grid class.

I do not know if this is an optimal implementation, but it works so it might be something for you to work with? :)

Sep 21, 2012 at 5:40 PM

Looks nice, but I did not understand how you will be setup row options from user's side?

Sep 21, 2012 at 6:53 PM

I haven't thought through how to add it through the html helper extensions yet... As of now I just loop through the items collection in the grid constructor (I extended Grid<T> to setup the columns in a class file instead of in my view) and add classes with ItemClasses[item].Add("class");.

But I guess you could make some kind of helper method on the GridHtmlOptions taking an Expression from the grid type to a boolean and a string to add to the class list. Then run through the grid items adding the class to all items satifying the expression body. I will try making some code later to show what I mean.

Sep 21, 2012 at 6:57 PM

Also I was wondering why you decided not to make the IGrid interface generic?

Sep 21, 2012 at 7:50 PM

So I ended up with this in IGridHtmlOptions.cs:

IGridHtmlOptions<T> AddItemClass(Expression<Func<T, bool>> constraint, string cssClass);

And this implementation in GridhtmlOptions.cs

public IGridHtmlOptions<T> AddItemClass(System.Linq.Expressions.Expression<Func<T, bool>> constraint, string cssClass)
     foreach (T item in _beforeItems.Where(constraint).ToList())
     return this;

And to do that I had to change the _beforeItems from private to protected in GridBase.cs. I don't know if I should rather use _afterItems than _beforeItems as I haven't looked further into how the processors work yet.

Sep 24, 2012 at 4:23 AM

I don't made IGrid generic because it useed in "_Grid.cshtml" view

I looks nice, but I suggest to setup Expression<T, string> instead of Expression<T, bool>. It provides more flexibility, for example if you will have an custom property in your model, that specifies a custom css classes, you can use like this:




Or use this mehtod with other properties:





Sep 24, 2012 at 6:21 PM

Yeah, I like your idea with Expression<T, string> better :)

Does it matter whether it is generic for usage in views?

Sep 25, 2012 at 4:58 AM
Edited Sep 25, 2012 at 5:38 AM

If I used a generic class as view model if _Grid.cshtml - it's require a specifying generic type, which is unknown in the view. And what reason to use generics in IGrid?

I made a fix for rendering custom grid rows. This option setup by specifying delegate in SetRowCssClasses method:

@Html.Grid(Model).Columns(...).SetRowCssClasses(item => item.Customers.IsVip ? "vip-row" : string.Empty);
 Ready for commnets :)

Oct 1, 2012 at 3:11 PM

Looks nice and clean :)

Mar 19, 2014 at 4:57 PM
if i need another color for my row how i can do it to change the class name of the row?
  @Html.Grid(Model).Columns(c => {
             c.Add(o => o.Vehicle.RegistrationPlate).Titled("Patente vehiculo");
             c.Add(o => o.StateTravel.Name).Titled("Estado Viaje");  <--- i need to change this row depending of value( En espera = red, En tramite = blue ..... etc ...)
             c.Add(o => o.ProgrammedDate).Titled("Fecha Programada");
             c.Add(o => o.StartDate).Titled("Fecha de inicio");
             c.Add(o => o.EndDate).Titled("Fecha Finalizacion");
excuse me for my bad english.
Mar 22, 2014 at 10:43 AM
Try to use RenderValueAs mehtod:
@helper CustomRenderingOfColumn(MyModelType item)
    if (item.StateTravel.Name == "En espera")
    <span style="color:red;">@item.StateTravel.Name</span>
    else if (...)
                  ... and so on
c.Add(o => o.StateTravel.Name).Titled("Estado Viaje")
Mar 24, 2014 at 2:08 PM
Bukharin, thank for the solution, works 100%.