web-dev-qa-db-fra.com

Créer dynamiquement un tableau HTML en C #

Existe-t-il un moyen plus efficace de créer un tableau HTML que celui que j'essaie actuellement?

Je reçois un objet et il contient une liste d'entités. J'ai donc besoin de passer par chacun d'eux et de construire d'abord une cellule, puis de l'ajouter à une ligne et de l'ajouter enfin dans le tableau.

La chose que j'essaye est totalement désordonnée, genre de travaux, mais elle a trop de code redondant.

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

À la fin, je veux retourner la version texte du tableau HTML créé. Le problème est que j'ai beaucoup plus de propriétés que ces 3 (BusinessName, SwiftBIC et IBAN) et en plus j'ai une liste d'objets à l'intérieur de mailMessageObject, donc le code serait terrible.

Quelqu'un a une idée de comment résoudre ce problème de manière plus simple et plus propre?

10
nemo_87

C'est une approche décente, et juste "ce qu'il faut" pour produire quelque chose d'aussi compliqué que HTML - à moins que vous ne vouliez le faire en utilisant des chaînes simples (ce qui est tout aussi compliqué, sinon pire).

Une amélioration: n'utilisez pas le même objet cellule plusieurs fois, vous courez le risque d'obtenir une sortie incorrecte. Code amélioré:

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

Bien sûr, vous pouvez également créer vos propres assistants pour créer des cellules, pour créer une ligne pleine de cellules, etc. Il existe également de bonnes bibliothèques pour cela, par exemple voir https://www.nuget.org/packages/HtmlTags/ .

2
Peter B

Comme je suis récemment venu jouer avec la création de classes IDisposable, je pense que ce serait à la fois efficace pour cette tâche spécifique et beaucoup plus facile à lire:

Créez des classes très simples

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

Ensuite, vous pouvez définir votre table en utilisant:

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();
5
Steve Harris

Je voudrais juste compléter la réponse de Steve Harris avec une bibliothèque de classes un peu plus élaborée. Sa réponse est une solution totalement élégante qui fait qu'un service Windows que je créais n'a pas à faire référence à System.Web sans raison valable!

Classes définies:

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

Usage:

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
James Eby

Je pense que vous pouvez peut-être ajouter une fonction pour obtenir toutes les propriétés de votre objet. Et puis juste itérer sur eux. Vous pouvez également créer une liste de propriétés qui doivent être affichées dans votre message.

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