This project is read-only.

Custom renders and suggestions

Dec 29, 2012 at 11:43 AM
Edited Dec 29, 2012 at 2:20 PM

Hi.

I just stumpled across this awesome webgrid, which is far better than the existing ones. Great job guys! I have a few questions / suggestions - if i may =)

Is there any way to customize the Header and Cell renders (DI)? They return empty "styles" tags (if not defined) which i would like to remove to lower the size of the rendered HTML (kb). Wouldnt use of "TagBuilder" take care of this "problem"?

The below suggestions is mainly about the size of the rendered HTML (kb). It seams like there is way to many attributes, which is used by JS or CSS - and could be removed by minor changes. This would improve on the render times and size of the HTML.

GridHeader

The sorting links should be encoded (&). The "TH" has a class "grid-header" which is only used by CSS. Could use ".grid-wrap >thead > th" instead.

GridCell

The class "grid-cell" is only used one time in the gridmvc.js. Shouldnt this be changeable so the selector instead uses "TD" as selector - since there can't be anything else below it. The same goes for the "data-name". This is already defined in the TH (find the TH with the same index as the current cell).

Suggested Changes

If you update the javascript with the following.

    /***
    * Handle Grid row events
    */
    gridMvc.prototype.initGridRowsEvents = function () {
    	var $this = this;
        this.jqContainer.find("tbody tr").click(function () {
            $this.rowClicked.call(this, $this);
        });
    };
    /***
    * Trigger on Grid row click
    */
    gridMvc.prototype.rowClicked = function ($context) {
    	var row = $(this).closest("tr");
    	if (row.length <= 0)
    		return;
    	var gridRow = {};
    	var gridTable = $(this).closest("table.grid-table");
    	var gridHeaders = $("thead th .grid-filter", gridTable);
    	row.find("td").each(function (index, element) {
    	    var columnName = $(gridHeaders[index]).attr("data-name") || "";
            if (columnName.length > 0)
            	gridRow[columnName] = $(element).text();
        });
        $context.markRowSelected(row);
        $context.notifyOnRowSelect(gridRow);
    };
    /***
    * Mark Grid row as selected
    */
    gridMvc.prototype.markRowSelected = function (row) {
        this.jqContainer.find("tbody tr.grid-row-selected").removeClass("grid-row-selected");
        row.addClass("grid-row-selected");
    };
/* Grid headers */
table.grid-table th.sorted-asc .grid-sort-arrow:after { content" \2193"; }
table.grid-table th.sorted-desc .grid-sort-arrow:after { content" \2191"; }

You can remove the following default inline attributes:

CssClass: ".grid-header", ".grid-cell" and ".grid-row".
Data attribute: "data-name" on TableCell (now finds it by table header with same index).

You can test it by adding

$(".grid-table thead th").removeClass("grid-header");
$(".grid-table thead th[class='']").removeAttr("class");
$(".grid-table thead th[style='']").removeAttr("style");
$(".grid-table tbody tr, .grid-table tbody td").removeAttr("class").removeAttr("style").removeAttr("data-name");
Dec 29, 2012 at 3:46 PM
Edited Dec 29, 2012 at 6:16 PM

I've downloaded the source, and made the changes mentioned abow:

Reference: "System.Web.WebPages".

GridStyledRenderer (added "sb.ToString().Trim()")

protected string GetCssClassesString()
{
	var sb = new StringBuilder();
	foreach (string className in _classes)
	{
		sb.Append(className + " ");
	}
	return sb.ToString().Trim();
}

protected string GetCssStylesString()
{
	var sb = new StringBuilder();
	foreach (string style in _styles)
	{
		sb.Append(style + " ");
	}
	return sb.ToString().Trim();
}

GridCellRenderer (added TagBuilder) 

public class GridCellRenderer : GridStyledRenderer
{
	//private const string TdClass = "grid-cell";

	public GridCellRenderer()
	{
		//AddCssClass(TdClass);
	}

	public override string Render(IGridColumn column, string content)
	{
		var cssStyles = GetCssStylesString();
		var cssClass = GetCssClassesString();

		TagBuilder builder = new TagBuilder("td");
		if (!string.IsNullOrWhiteSpace(cssClass))
			builder.AddCssClass(cssClass);
		if (!string.IsNullOrWhiteSpace(cssStyles))
			builder.MergeAttribute("style", cssStyles);
		builder.InnerHtml = content;

		return builder.ToString();
	}
}

GridHeaderRenderer  (added TagBuilder)

public class GridHeaderRenderer : GridStyledRenderer
{
	//private const string ThClass = "grid-header";

	private readonly List<IGridColumnRenderer> _additionalRenders = new List<IGridColumnRenderer>();

	public GridHeaderRenderer()
	{
		//AddCssClass(ThClass);
	}

	public override string Render(IGridColumn column, string content)
	{
		var cssStyles = GetCssStylesString();
		var cssClass = GetCssClassesString();

		if (!string.IsNullOrWhiteSpace(column.Width))
			cssStyles = string.Concat(cssStyles, " width:", column.Width, ";").Trim();

		TagBuilder builder = new TagBuilder("th");
		if (!string.IsNullOrWhiteSpace(cssClass))
			builder.AddCssClass(cssClass);
		if (!string.IsNullOrWhiteSpace(cssStyles))
			builder.MergeAttribute("style", cssStyles);
		builder.InnerHtml = string.Concat(content, RenderAdditionalContent(column, content));

		return builder.ToString();
	}

	...
}

QueryStringFilterColumnHeaderRenderer  (added TagBuilder) 

internal class QueryStringFilterColumnHeaderRenderer : IGridColumnRenderer
{
	private const string FilteredButtonCssClass = "filtered";

	private readonly QueryStringFilterSettings _settings;

	public QueryStringFilterColumnHeaderRenderer(QueryStringFilterSettings settings)
	{
		_settings = settings;
	}

	#region IGridColumnRenderer Members
	public string Render(IGridColumn column, string content)
	{
		if (!column.FilterEnabled)
			return string.Empty;

		var filterButtonCss = "grid-filter-btn";
		var filterType = GridFilterType.Equals;
		var filterValue = string.Empty;
		if (column.Name.Equals(_settings.ColumnName))
		{
			filterType = _settings.Type;
			filterValue = _settings.Value;
			filterButtonCss += string.Concat(" ", FilteredButtonCssClass);
		}

		//determine current url:
		var queryBuilder = new CustomQueryStringBuilder(_settings.Context.Request.QueryString);
		string url =
			queryBuilder.GetQueryStringExcept(new[]
											{
												GridPager.DefaultPageQueryParameter,
												_settings.TypeQueryParameterName,
												_settings.ColumnQueryParameterName,
												_settings.ValueQueryParameterName
											});


		TagBuilder gridFilterButton = new TagBuilder("span");
		gridFilterButton.AddCssClass(filterButtonCss);
		gridFilterButton.Attributes.Add("title", Strings.FilterButtonTooltipText);

		var dataKeyList = new Dictionary<string, string>() { 
			{ "data-type", column.FilterWidgetTypeName },
			{ "data-name", column.Name },
			{ "data-filtertype", ((int)filterType).ToString() },
			{ "data-filtervalue", filterValue },
			{ "data-url", url}
		};

		TagBuilder gridFilter = new TagBuilder("span");
		gridFilter.InnerHtml = gridFilterButton.ToString();
		gridFilter.AddCssClass("grid-filter");
		foreach (var data in dataKeyList)
		{
			if (!string.IsNullOrWhiteSpace(data.Value))
				gridFilter.Attributes.Add(data.Key, data.Value);
		}
		return gridFilter.ToString();
	}

	#endregion
}

Since the data attributes now are conditional, you need to change the gridmvc.js

var columnType = $(this).attr("data-type") || "";
var columnName = $(this).attr("data-name") || "";
var filterType = $(this).attr("data-filtertype") || 0;
var filterValue = $(this).attr("data-filtervalue") || "";
var filterUrl = $(this).attr("data-url") || "";
Jan 9, 2013 at 11:21 AM

Hi,

Thank you for improving Grid.Mvc. I'll make this changes.

Aug 23, 2013 at 11:58 PM
Hi bukharin.

Any plans on including these updates/changes ?
Aug 26, 2013 at 5:41 AM
Yes, I already include some changes. Also include TagBuilder to the next release