1

I am currently upgrading a major MVS project and I am converting all the static GridViews to dynamically built HTML tables with Bootstrap CSS. The tables are created and look/function great.

However, I cannot seem to figure out how to call the table and iterate through from the code-behind to change the background color of cells based on the text they contain.

This function creates the table:

 public static string ConvertDataTableToHTML(DataTable dt)
 {
     string html = "<table id=\"tbl_1\" runat=\"server\" class=\"table table-hover table-condensed\">";
     //add header row
     html += "<thead><tr>";
     for (int i = 0; i < dt.Columns.Count; i++)
         html += "<th style=\"background-color: #800000; color: #FFFFFF; font-weight: bold\">" + dt.Columns[i].ColumnName + "</th>";
     html += "</thead></tr>";

     //add rows
     for (int i = 0; i < dt.Rows.Count; i++)
     {
         html += "<tr>";
         for (int j = 0; j < dt.Columns.Count; j++)
             html += "<td>" + dt.Rows[i][j].ToString() + "</td>";
         html += "</tr>";
     }
     html += "</table>";

     return html;
 }

The above function is creating a table with an id of "tbl_1".

Then I have this code that calls the function, assigns the SQL data to the dt, sets some attributes of the dt and then assigns the dt to the dynamically created table and tries to loop through the cells and change their color - but nothing happens....

 OracleDataReader dtReader;
 objCmd = new OracleCommand(varname1, objConn);
 dtReader = objCmd.ExecuteReader();

 if (dtReader.HasRows == true)
 {
     DataTable dt1 = new DataTable();

     dt1.Load(dtReader);
     dt1.Columns[2].ReadOnly = false;
     dt1.Columns[2].MaxLength = 200;
     dt1.Columns[3].ReadOnly = false;
     dt1.Columns[3].MaxLength = 200;
     dt1.Columns[4].ReadOnly = false;
     dt1.Columns[4].MaxLength = 200;
     dt1.Columns[5].ReadOnly = false;
     dt1.Columns[5].MaxLength = 200;
     SG_COUNT_STATUS4(dt1);

     myTBL_1.InnerHtml = ConvertDataTableToHTML(dt1);
    
     System.Web.UI.WebControls.Table dynamicTable = (System.Web.UI.WebControls.Table)FindControl("tbl_1");
     if (dynamicTable != null)
     {
         foreach (System.Web.UI.WebControls.TableRow row in dynamicTable.Rows)
         {
             foreach (System.Web.UI.WebControls.TableCell cell in row.Cells)
             {
                 string cellText = cell.Text.ToLower();
                 if (cellText != "not run") { cell.BackColor = System.Drawing.Color.Red; }
                 
             }
         }
     }
 }

It appears that the code is not actually setting a reference to table. If I remove the if (dynamicTable != null) trigger, it errors out on the next line saying

Object reference not set to an instance of an object.

How do I loop through the dynamically created table and change the background color based on the text inside the cell?

22
  • 2
    "I am converting all the static GridViews to dynamically built HTML tables" ...why? GridViews don't have to be static, they can have data sources, and be updated. And you can make them use bootstrap without too much trouble. You're going from structured/semi-structured creation of a table using markup and integrated data sources, to hand-building HTML inline in code-behind. This leads to less separation of concerns between code and presentation, code that's harder to debug and reason about what HTML it will generate, and far more scope for silly accidental syntax errors in the HTML Commented Jul 24 at 9:18
  • 2
    Anyway it's a long time since I did WebForms (because it's been legacy for way over a decade) but I'm pretty sure that FindControl only finds server controls (see learn.microsoft.com/en-us/dotnet/api/…). Just bunging some raw HTML into the innerHTML of another control doesn't create a server-managed control (unlike if you'd use a GridView, which is a server-managed control). So my overall advice to you here is "don't do it like this, you should probably just work on amending your gridviews instead". Commented Jul 24 at 9:19
  • 2
    @jdweng there isn't even a table object, never mind any rows or cells, because - as I already said - the OP has just created a raw HTML string, not an ASP.NET server control. Commented Jul 24 at 12:36
  • 1
    i am not sure what you mean by "there isn't even a table object". there is. it fills with data and displays beautifully. so, its there...I didn't say you didn't have a HTML table. I said you didn't have a server-side C# object which represents that in the code-behind, and which you can use to loop through and modify its contents. All you have is the plain pre-prepared HTML string which you generated in your ConvertDataTableToHTML method and dumped directly into another div element. "FindControl" is looking for that server-side object, and can't find it because it's not there. Commented Jul 24 at 15:51
  • 1
    I just need to know how to access it from the code-behind...and, we're back to the start. This is what GridViews are for - to give you structured, programmatic ways to render, control and populate HTML tables from the code-behind. Use the tools that the framework gave you, instead of abandoning them because of an apparent misconception that you wouldn't be able to change the final rendered appearance of the table they generate. So yeah...the "why" does matter. This question is a classic X-Y Problem as far as I can see. Commented Jul 24 at 15:53

2 Answers 2

1

I could suggest putting the most relevant information in special custom attribute, for example data-status, then you would be able to write CSS styles based on values for those attributes. Then you could target appearance of the entire row depending of this attribute value in some td element within the row.

You would need to modify your code slightly for generating HTML table:

//add rows
for (int i = 0; i < dt.Rows.Count; i++)
{
    html += "<tr>";
    for (int j = 0; j < dt.Columns.Count; j++)
        html += "<td" + (j == 0 ? $"data-status='{dt.Rows[i][j]}'" : "") + ">" + dt.Rows[i][j].ToString() + "</td>";
    html += "</tr>";
}

Example usage below with HTML+CSS:

[data-status="error"] {
  color: red;
  
  ~ * {
    color: red;
  }
}

[data-status="all-good"] {
  color: green;
  
  ~ * {
    color: green;
  }
}
<table id="tbl_1" runat="server" class="table table-hover table-condensed">
  <thead>
    <tr>
      <th style="background-color: #800000; color: #FFFFFF; font-weight: bold">Status</th>
      <th style="background-color: #800000; color: #FFFFFF; font-weight: bold">Message</th>
    </tr>
  </thead>
  <tr>
    <td data-status='error'>error</td>
    <td>Something went wrong</td>
  </tr>
  <tr>
    <td data-status='all-good'>all-good</td>
    <td>Something went great</td>
  </tr>
</table>

Sign up to request clarification or add additional context in comments.

Comments

0

I would advise against this approach of hand-coding HTML in general, as I've highlighted in the comments, because it is fragile and difficult to debug syntax errors. It also creates more effort to create and maintain that HTML-generating code when you could achieve the goal more cleanly by just modifying a GridView to use bootstrap's CSS, and using hooks to set extra attributes on the cells if needed.

But if you do wish to persist with this, then your current idea doesn't work because FindControl() can only find server-managed ASP.NET controls. Your table (unlike a GridView, for example) isn't a server-managed control - it's just a HTML string that you inserted directly into the innerHTML of another control. As far as your code-behind is concerned that's just some text, not a structured object.

But what you're trying to end up with isn't complicated - it seems you just want to change a cell's background colour when it contains specific text - and could be solved by just putting the same basic logic directly into your ConvertDataTableToHTML method.

Here's one way to do that:

 public static string ConvertDataTableToHTML(DataTable dt)
 {
     string html = "<table id=\"tbl_1\" runat=\"server\" class=\"table table-hover table-condensed\">";
     //add header row
     html += "<thead><tr>";
     for (int i = 0; i < dt.Columns.Count; i++)
         html += "<th style=\"background-color: #800000; color: #FFFFFF; font-weight: bold\">" + dt.Columns[i].ColumnName + "</th>";
     html += "</thead></tr>";

     //add rows
     for (int i = 0; i < dt.Rows.Count; i++)
     {
         html += "<tr>";
         for (int j = 0; j < dt.Columns.Count; j++)
             //added logic here to control the cell's background colour
             html += "<td" + (dt.Rows[i][j].ToString().ToLower() != "not run" ? " style='background-color: red;'" : "" ) + ">" + dt.Rows[i][j].ToString() + "</td>";
         html += "</tr>";
     }
     html += "</table>";

     return html;
 }

Specifically I amended the line which builds the HTML to:

html += "<td" + (dt.Rows[i][j].ToString().ToLower() != "not run" ? "style='background-color: red;'" : "" ) + ">" + dt.Rows[i][j].ToString() + "</td>";

As you can see, this tests the contents of the cell to see if it equals the "not run" text or not, and inserts a style attribute as needed.

Comments

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.