【问题标题】:Dynamically create HTML table in C#在 C# 中动态创建 HTML 表
【发布时间】:2016-07-28 07:19:31
【问题描述】:

有没有比我现在尝试的更有效的方法来构建 HTML 表格?

我正在获取一个对象,其中包含一些实体列表。所以我需要遍历它们中的每一个,首先构建一个单元格,然后将其添加到行中,最后将其添加到表格中。

我正在尝试的东西完全是凌乱的,有点工作,但它有太多的冗余代码。

public static string CreateNotificationMailMessage(NotificationMailMessage mailMessageObject)
{
    var table = new HtmlTable();
    var mailMessage = new StringBuilder();
    string html;

    if (mailMessageObject.InvalidCompanies.Any())
    {
        HtmlTableRow row;
        HtmlTableCell cell;

        foreach (var invalidCompany in mailMessageObject.InvalidCompanies)
        {
            row = new HtmlTableRow();
            cell = new HtmlTableCell();
            cell.InnerText = invalidCompany.BusinessName;
            row.Cells.Add(cell);
            cell.InnerText = invalidCompany.SwiftBIC;
            row.Cells.Add(cell);
            cell.InnerText = invalidCompany.IBAN;
            row.Cells.Add(cell);
            table.Rows.Add(row);
        }
    }
    using (var sw = new StringWriter())
    {
        table.RenderControl(new HtmlTextWriter(sw));
        html = sw.ToString();
    }

    mailMessage.AppendFormat(html);
    return mailMessage.ToString();
}

最后我想返回创建的 HTML 表格的文本版本。 问题是我拥有比这 3 个(BusinessName、SwiftBIC 和 IBAN)更多的属性,而且我在 mailMessageObject 内还有一个对象列表,所以代码会很糟糕。

有人知道如何以更简单、更清洁的方式解决这个问题吗?

【问题讨论】:

  • 您是否尝试使用TagBuilderSystem.Web.Mvc 来生成HTML?我的意思不是 MVC 应用程序,仅包括 System.Web.Mvc 的参考。
  • "。问题是我有比这 3 个(BusinessName、SwiftBIC 和 IBAN)更多的属性”可能是反射的用例和一个 customTag 说“将其包含在 html 表中”。或者也许只是 XmlSerialize 列表并将转换保留为 html 表作为查看器上的 xslt 转换?
  • 另外,我认为您似乎至少要问两件事('我如何迭代一堆列表中的一堆字段/属性')=> 反射,可能&怎么做我为给定的工作集有效地构建了一个 HTML 表。所有的答案似乎都是关于 html 的,所以如果你的问题实际上是关于迭代的,那么可能需要稍微改写一下。

标签: c# html asp.net


【解决方案1】:

由于我最近开始尝试创建 IDisposable 类,我认为这对于这项特定任务来说既有效又更易于阅读:

创建一些非常简单的类

    /// <summary>
    /// https://stackoverflow.com/a/36476600/2343
    /// </summary>
    public class Table : IDisposable
    {
        private StringBuilder _sb;

        public Table(StringBuilder sb, string id = "default", string classValue="")
        {
            _sb = sb;
            _sb.Append($"<table id=\"{id}\" class=\"{classValue}\">\n");
        }

        public void Dispose()
        {
            _sb.Append("</table>");
        }

        public Row AddRow()
        {
            return new Row(_sb);
        }

        public Row AddHeaderRow()
        {
            return new Row(_sb, true);
        }

        public void StartTableBody()
        {
            _sb.Append("<tbody>");

        }

        public void EndTableBody()
        {
            _sb.Append("</tbody>");

        }
    }

    public class Row : IDisposable
    {
        private StringBuilder _sb;
        private bool _isHeader;
        public Row(StringBuilder sb, bool isHeader = false)
        {
            _sb = sb;
            _isHeader = isHeader;
            if (_isHeader)
            {
                _sb.Append("<thead>\n");
            }
            _sb.Append("\t<tr>\n");
        }

        public void Dispose()
        {
            _sb.Append("\t</tr>\n");
            if (_isHeader)
            {
                _sb.Append("</thead>\n");
            }
        }

        public void AddCell(string innerText)
        {
            _sb.Append("\t\t<td>\n");
            _sb.Append("\t\t\t"+innerText);
            _sb.Append("\t\t</td>\n");
        }
    }
}

然后您可以使用以下方法定义您的表格:

StringBuilder sb = new StringBuilder();

using (Html.Table table = new Html.Table(sb))
{
    foreach (var invalidCompany in mailMessageObject.InvalidCompanies)
    {
        using (Html.Row row = table.AddRow())
        {
            row.AddCell(invalidCompany.BusinessName);
            row.AddCell(invalidCompany.SwiftBIC);
            row.AddCell(invalidCompany.IBAN);
        }
    }
}

string finishedTable = sb.ToString();

【讨论】:

  • 史蒂夫的好主意!我补充了你的类定义,以便在我的回答中进一步构建它。
【解决方案2】:

我只想用一个更完善的类库来补充 Steve Harris 的回答。他的回答是一个完全优雅的解决方案,让我创建的 Windows 服务不必无缘无故地引用 System.Web!

类定义:

  public static class Html
  {
    public class Table : HtmlBase, IDisposable
    {
      public Table(StringBuilder sb, string classAttributes = "", string id = "") : base(sb)
      {
        Append("<table");
        AddOptionalAttributes(classAttributes, id);
      }

      public void StartHead(string classAttributes = "", string id = "")
      {
        Append("<thead");
        AddOptionalAttributes(classAttributes, id);
      }

      public void EndHead()
      {
        Append("</thead>");
      }

      public void StartFoot(string classAttributes = "", string id = "")
      {
        Append("<tfoot");
        AddOptionalAttributes(classAttributes, id);
      }

      public void EndFoot()
      {
        Append("</tfoot>");
      }

      public void StartBody(string classAttributes = "", string id = "")
      {
        Append("<tbody");
        AddOptionalAttributes(classAttributes, id);
      }

      public void EndBody()
      {
        Append("</tbody>");
      }

      public void Dispose()
      {
        Append("</table>");
      }

      public Row AddRow(string classAttributes = "", string id = "")
      {
        return new Row(GetBuilder(), classAttributes, id);
      }
    }

    public class Row : HtmlBase, IDisposable
    {
      public Row(StringBuilder sb, string classAttributes = "", string id = "") : base(sb)
      {
        Append("<tr");
        AddOptionalAttributes(classAttributes, id);
      }
      public void Dispose()
      {
        Append("</tr>");
      }
      public void AddCell(string innerText, string classAttributes = "", string id = "", string colSpan = "")
      {
        Append("<td");
        AddOptionalAttributes(classAttributes, id, colSpan);
        Append(innerText);
        Append("</td>");
      }
    }

    public abstract class HtmlBase
    {
      private StringBuilder _sb;

      protected HtmlBase(StringBuilder sb)
      {
        _sb = sb;
      }

      public StringBuilder GetBuilder()
      {
        return _sb;
      }

      protected void Append(string toAppend)
      {
        _sb.Append(toAppend);
      }

      protected void AddOptionalAttributes(string className = "", string id = "", string colSpan = "")
      {

        if (!id.IsNullOrEmpty())
        {
          _sb.Append($" id=\"{id}\"");
        }
        if (!className.IsNullOrEmpty())
        {
          _sb.Append($" class=\"{className}\"");
        }
        if (!colSpan.IsNullOrEmpty())
        {
          _sb.Append($" colspan=\"{colSpan}\"");
        }
        _sb.Append(">");
      }
    }
  }

用法:

StringBuilder sb = new StringBuilder();
      using (Html.Table table = new Html.Table(sb, id: "some-id"))
      {
        table.StartHead();
        using (var thead = table.AddRow())
        {
          thead.AddCell("Category Description");
          thead.AddCell("Item Description");
          thead.AddCell("Due Date");
          thead.AddCell("Amount Budgeted");
          thead.AddCell("Amount Remaining");
        }
        table.EndHead();
        table.StartBody();
        foreach (var alert in alertsForUser)
        {
          using (var tr = table.AddRow(classAttributes: "someattributes"))
          {
           tr.AddCell(alert.ExtendedInfo.CategoryDescription);
            tr.AddCell(alert.ExtendedInfo.ItemDescription);
            tr.AddCell(alert.ExtendedInfo.DueDate.ToShortDateString());
            tr.AddCell(alert.ExtendedInfo.AmountBudgeted.ToString("C"));
            tr.AddCell(alert.ExtendedInfo.ItemRemaining.ToString("C"));
          }
        }
        table.EndBody();
      }
      return sb.ToString();

【讨论】:

    【解决方案3】:

    这是一种不错的方法,只是“需要什么”来输出像 HTML 这样复杂的东西 - 除非你想使用纯字符串来完成它(这同样是混乱的,如果不是更糟的话)。

    一项改进:不要多次使用相同的单元格对象,否则可能会得到错误的输出。改进的代码:

    row.Cells.Add(new HtmlTableCell { InnerText = invalidCompany.BusinessName });
    row.Cells.Add(new HtmlTableCell { InnerText = invalidCompany.SwiftBIC });
    row.Cells.Add(new HtmlTableCell { InnerText = invalidCompany.IBAN });
    

    当然,您也可以创建自己的助手来创建单元格,创建一个充满单元格的行等。为此也有很好的库,例如见https://www.nuget.org/packages/HtmlTags/

    【讨论】:

    • @PetereB 我喜欢你的方法,它更干净,即使你说动态构建 HTML 总是很混乱。还有一个问题。构建完成后,您将如何在代码中注明要进入新行?此代码将在 foreach 循环中,并且肯定会有几行。现在,它们只是作为表中的一大排出现。是否可以说一些如何以这种方式添加新的 ?
    • 再次检查您是否每次都使用new HtmlTableRow(),也许您的实际代码与问题不同,并且以某种方式对所有单元格使用同一行?还可以查看我对答案的补充。
    【解决方案4】:

    我想也许你可以添加一个函数来从你的对象中获取所有属性。然后只是迭代它们。您还可以创建需要在消息中显示的属性列表。

        private static PropertyInfo[] GetProperties(object obj)
        {
            return obj.GetType().GetProperties();
        }
    
        // -------
        foreach (var invalidCompany in mailMessageObject.InvalidCompanies)
        {
            var properties = GetProperties(invalidCompany);     
            foreach (var p in properties)
            {   
                string name = p.Name;
                if(propertiesThatNeedToBeDisplayed.Contains(name)
                {
                    cell.InnerText = p.GetValue(invalidCompany, null);
                    row.Cells.Add(cell);
                    table.Rows.Add(row);
                }
            }
        }
    

    【讨论】:

      猜你喜欢
      相关资源
      最近更新 更多
      热门标签