使用C#动态创建HTML表格

18

有比我现在尝试的更有效的构建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内部还有一个对象列表,所以代码会非常糟糕。 有人有简单清晰的解决方法吗?

你尝试过使用System.Web.Mvc中的TagBuilder来生成HTML吗?我的意思是不是MVC应用程序,只需要引用System.Web.Mvc - Andrew Orlov
问题在于我有比这三个属性更多的属性(BusinessName、SwiftBIC和IBAN),这可能是反射和自定义标签“在HTML表格中包含此内容”的用例。或者只需将列表XmlSerialize并将转换留给查看器上的xslt转换为HTML表格? - tolanj
还有我认为你似乎在问至少两件事情('如何迭代一堆列表中的一堆字段/属性')=> 反射,大概和如何高效地为给定的工作集构建HTML表格。所有的答案似乎都是关于html的,所以如果你的问题实际上是关于迭代的,可能需要稍微改一下措辞。 - tolanj
4个回答

13

最近我开始使用 IDisposable 类,我认为这将为特定任务提高效率,并且更易读:

创建一些非常简单的类。

    /// <summary>
    /// https://dev59.com/WloV5IYBdhLWcg3wRs8w#36476600
    /// </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();

太棒了,Steve!我在我的答案中补充了你的类定义,以便更好地构建它。 - James Eby
列方面怎么样? - c-sharp-and-swiftui-devni

12

我想要补充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();

5

这是一种不错的方法,也是输出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循环中,并且肯定会有几行。现在它们只是作为表格中的一个大行出现。是否可能以某种方式添加新的<tr>? - nemo_87
请仔细检查您是否每次都使用new HtmlTableRow(),也许您的实际代码与问题不同,并且以某种方式为所有单元格使用相同的行?另外,请查看我的答案补充。 - Peter B

0

我认为你可以添加一个函数来获取对象的所有属性。然后只需对它们进行迭代。此外,您可以创建一个需要在消息中显示的属性列表。

    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);
            }
        }
    }

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接