Friday, 27 December 2013

ASP.NET setting "ColSpan" breaks table layout with fixed columns by overriding RenderContents

This is an interesting problem.

If you have a table where you have very long words you find that rather than breaking the words the browser resizes the table so it flows off the page.




























This is quite simple to fix by adding the following CSS to the table

table-layout: fixed;
word-wrap: break-word;

And setting a fixed size to the first column using a standard style="width:200px;" for example.

The very long words are then broken appropriately. Very nice











What happens however when you add a title row to the table (using a <Row ColSpan="2"> for example) is that the table ignored the width declarations for the subsequent rows because when using a fixed layout the width of the columns in the first row is consulted. The first column then sizes itself automatically. In the screenshot you can see if resizes to around 50% of the browser window as the browser is resized.









To solve this in HTML is quite simple by using <col> definitions to inform the table "upfront" what widths to use for the columns rather than looking at the first row.

<table>
  <col style="width:200px;">
  <col >
</table>

The problem in ASP.NET is that the Table class does not support column definitions however you can add them by overriding the RenderContents(HtmlTextWriter writer) method.

In the following example (which does need some polishing) we enumerate the rows in the table and find the first row that isn't a header row (by checking for cells that have a ColSpan of greater than 1).

We then use this "normal" row to set the column definitions for the table automatically. It would perhaps be nicer to add a "ColumnDefinitions" property to the table and explicitly set the column definitions rather than dynamically detecting them.


/// <summary>
/// Provides additional functionality over the standard table, allowing column definitions to be set on the table.
/// </summary>
/// <remarks>This is a workaround for the issue that the first column width isn't honoured when a title row is found in the table.</remarks>
public class PropertyGridTable:Table
{

    /// <summary>
    /// Renders the contents of the table.
    /// </summary>
    /// <param name="writer">The System.Web.UI.HtmlTextWriter to which the content is written.</param>
    protected override void RenderContents(HtmlTextWriter writer)
    {
        WriteColumnDefinitions(writer);
        base.RenderContents(writer);
    }


    /// <summary>
    /// Adds the column definitions if required.
    /// </summary>
    /// <param name="writer">The System.Web.UI.HtmlTextWriter to which the content is written.</param>
    private void WriteColumnDefinitions(HtmlTextWriter writer)
    {
        bool SizeRow = true;
        foreach (TableRow Row in Rows)
        {
            SizeRow = true;
            foreach (TableCell Cell in Row.Cells)
            {
                if (Cell.ColumnSpan > 1) { SizeRow = false; continue; }
            }
            if (SizeRow)
            {
                foreach (TableCell Cell in Row.Cells)
                {
                    if (Cell.Width.Value > 0) { writer.AddAttribute("Width", Cell.Width.ToString()); }
                    if (!String.IsNullOrEmpty(Cell.Style["width"])) { writer.AddStyleAttribute("width", Cell.Style["width"]); }
                    writer.RenderBeginTag("Col");
                    writer.RenderEndTag();
                }
                return;
            }
        }
    }



}




No comments:

Post a Comment