2009-10-07 25 views
1

J'essaie d'afficher un ensemble de données tabulaires dans un contrôle de base de données, mais j'ai besoin de faire pivoter la table de sorte que les enregistrements individuels soient des colonnes de table plutôt que des lignes de table. Le résultat final est une table avec un nombre fixe de colonnes et un nombre variable de lignes affichant chacune un seul champ pour tous les enregistrements like this. Du fait que les étiquettes < tr/> doivent être définies pour chaque champ, plutôt que pour chaque enregistrement, un répéteur n'est pas un moyen approprié pour ce faire. Ce que je me demande, c'est s'il y a des contrôles ASP.NET intégrés qui peuvent réaliser ce que je veux. Je contrôlais le contrôle ListView, mais je ne suis pas certain qu'il soit capable de ce que je décris.ASP.NET - Contrôle idéal pour afficher un tableau croisé dynamique

En effet, en supposant des disques comme les suivants:

 Number Yardage Par ... 
(Hole) 1  300  4 ... 
(Hole) 2  275  4 ... 
(Hole) 3  390  5 ... 
(Hole) ... ... ... ... 

Je dois montrer:

  1 2 3 ... 
Yardage: 300 275 390 ... 
    Par: 4 4 5 ... 
    ...: ... ... ... ... 

une alternative viable à se battre avec < tr/> balises serait bien sûr d'utiliser l'affichage: en ligne <divs> avec quelques CSS gracieux, mais si je peux préserver la structure <table> qui serait idéal.

Merci!

Répondre

5

J'ai récemment rencontré le même problème et, en cherchant une réponse, j'ai trouvé que la plupart des solutions fonctionnaient autour du pivotement des données source.

Il m'est apparu que le problème n'est pas avec les données source mais avec la façon dont nous souhaitons le rendre. Bien que la réponse de Chris ci-dessus cherche à modifier les données au moment du rendu, j'ai trouvé pour mes besoins que ce ne serait pas assez flexible si j'avais besoin d'une vue de grille de modèle. Il s'est alors produit qu'une meilleure solution au problème serait peut-être de capturer le balisage HTML d'une grille et de modifier cela - cela signifierait en fait que la solution pourrait être appliquée à tout contrôle rendant le balisage de table, et tout contrôle de gabarit contenu à l'intérieur continuerait à travailler.

En raison des contraintes de temps, j'ai seulement implémenté la solution avec une vue de grille; A l'origine, je voulais faire un contrôle serveur de template, que si un contrôle était placé à l'intérieur il vérifierait sa sortie de balisage et ferait pivoter toutes les tables qu'il contient)

De toute façon, voici le code nécessaire pour implémenter cette solution pour une vue de grille .

Code de contrôle serveur personnalisé

[ToolboxData("<{0}:PivotGridView runat=server></{0}:PivotGridView>")] 
public class PivotGridView : GridView 
{ 
    bool _pivotGrid = true; 

    [Browsable(true)] 
    public bool PivotGrid 
    { 
     get 
     { 
      return _pivotGrid; 
     } 
     set 
     { 
      _pivotGrid = value; 
      EnsureChildControls(); 
     } 
    } 

    protected override void RenderContents(HtmlTextWriter output) 
    { 
     if (_pivotGrid) 
     { 
      System.IO.TextWriter baseOutputTextWriter = new System.IO.StringWriter(); 
      HtmlTextWriter baseOutputHtmlWriter = new HtmlTextWriter(baseOutputTextWriter); 

      base.RenderContents(baseOutputHtmlWriter); 

      output.Write(HtmlParserService.PivotHtmlTableMarkup(baseOutputTextWriter.ToString())); 
     } 
     else 
     { 
      base.RenderContents(output); 
     } 
    } 
} 

HTML Parser Code de service, séparés pour la mise en œuvre facile ailleurs.

//... using System.Text.RegularExpressions; 

public static class HtmlParserService 
{   
    /// <summary> 
    /// Takes HTML markup locates any table definition held within it and returns that 
    /// markup with the table HTML pivotted 
    /// </summary> 
    /// <param name="html"></param> 
    /// <returns></returns> 
    public static string PivotHtmlTableMarkup(string html) 
    { 
     html = ReplaceShorthandTableTags(html); 

     int openingTableTagIndex; 
     string openingTableTagText; 
     int closingTableTagIndex; 
     string tableContentText; 

     tableContentText = GetTagContentText("table", html, out openingTableTagIndex, out openingTableTagText, out closingTableTagIndex); 

     MatchCollection rows = GetTagMatches("tr", tableContentText); 
     if (rows.Count > 0) 
     { 
      MatchCollection columns = GetTagMatches("(td|th)", rows[0].Value); 

      StringBuilder pivottedTableMarkup = new StringBuilder(); 

      for (int i = 0; i < columns.Count; i++) 
      { 
       pivottedTableMarkup.Append("<tr>"); 
       foreach (Match row in rows) 
       { 
        if (row.Value.Length > 0) 
        { 
         columns = GetTagMatches("(td|th)", row.Value); 

         if (columns.Count>i) 
         { 
          pivottedTableMarkup.Append(columns[i].Value); 
         } 
        } 
       } 
       pivottedTableMarkup.Append("</tr>"); 
      } 

      string preTableText = ""; 
      if (openingTableTagIndex > 1) 
      { 
       preTableText = html.Substring(1, openingTableTagIndex); 
      } 

      string postTableText; 
      postTableText = html.Substring(closingTableTagIndex, html.Length - closingTableTagIndex); 

      string newHtmlWithPivottedTable; 
      newHtmlWithPivottedTable = preTableText + openingTableTagText + pivottedTableMarkup.ToString() + postTableText; 

      return newHtmlWithPivottedTable; 
     } 
     else 
     { 
      return html; 
     } 

    } 

    /// <summary> 
    /// Gets the content between the specified tag. 
    /// </summary> 
    /// <param name="tag">The tag excluding any markup (e.g. "table" not "<table>"</param> 
    /// <param name="text">The xml text string to extract content from</param> 
    /// <param name="openingTagIndex">Outputs the indexed position of the opening tag</param> 
    /// <param name="openingTagText">Outputs the definition of the tag, e.g. it's attributes etc</param> 
    /// <param name="closingTagIndex">Outputs the indexed position of the closing tag</param> 
    /// <returns></returns> 
    public static string GetTagContentText(string tag, string text, out int openingTagIndex, out string openingTagText, out int closingTagIndex) 
    { 
     string contentText; 

     openingTagIndex = text.ToLower().IndexOf("<" + tag); 
     openingTagText = text.Substring(openingTagIndex, text.IndexOf(">", openingTagIndex) - openingTagIndex+1); 
     closingTagIndex = text.ToLower().LastIndexOf("</" + tag + ">"); 

     contentText = text.Substring(
      openingTagIndex + openingTagText.Length, 
      closingTagIndex - (openingTagIndex + openingTagText.Length)); 

     return contentText; 
    } 

    /// <summary> 
    /// Returns a collection of matches containing the content of each matched tag 
    /// </summary> 
    /// <param name="tag">HTML tag to match. Exclude opening and close angled braces. 
    /// Multiple tags can be matched by specifying them in the following format (tag1|tag2), 
    /// e.g. (td|th)</param> 
    /// <param name="html"></param> 
    /// <returns></returns> 
    public static MatchCollection GetTagMatches(string tag, string html) 
    { 
     Regex regexp = new Regex(@"<" + tag + @"\b[^>]*>(.*?)</" + tag + @">", RegexOptions.IgnoreCase | RegexOptions.Singleline); 
     return regexp.Matches(html); 
    } 

    /// <summary> 
    /// Ensures any shorthand XML tags are full expressed, e.g. 
    /// <td/> is converted to <td></td> 
    /// </summary> 
    /// <param name="value"></param> 
    /// <returns></returns> 
    private static string ReplaceShorthandTableTags(string value) 
    { 
     value=value.Replace("<tr/>", "<tr></tr>"); 
     value=value.Replace("<td/>", "<td></td>"); 
     value=value.Replace("<th/>", "<th></th>"); 

     return value; 
    } 


} 
2

Votre meilleur pari sera probablement de créer votre propre contrôle serveur.

J'ai fait quelque chose de similaire en créant un contrôle serveur personnalisé qui étendait un GridView. J'ai ensuite fait pivoter la DataSource et ajouté des colonnes à la grille de manière dynamique sur le databind. J'ai utilisé l'approche GridView pour garder l'apparence et le comportement similaire au reste de mon site. Vous pouvez également créer un contrôle personnalisé plus simple qui a créé html pour une table dans la méthode Render.

Bonne chance.

EDIT: Voici un exemple de la façon de rendre le contrôle du serveur basé sur une table (Il était pas difficile, mais il faut savoir que je n'ai pas testé ça ...):

public class PivotTable : Control 
{ 
    protected Table pivotTable = new Table(); 
    private DataTable _datasource; 
    [DesignerSerializationVisibility(DesignerSerializationVisibility.Content), 
    Category("Data"), Browsable(true), 
    Description("Gets and sets the DataSource for the Control. Needs to be a DataTable")] 
    public DataTable DataSource 
    { 
     get { return _datasource; } 
     set 
     { 
      if (value.GetType() == typeof(DataTable)) 
      { 
       throw new Exception("DataSource must be a DataTable."); 
      } 
      _datasource = value; 
     } 
    } 

    public override ControlCollection Controls 
    { 
     get 
     { 
      EnsureChildControls(); 
      return base.Controls; 
     } 
    } 

    protected override void CreateChildControls() 
    { 
     Table tbl = new Table(); 
     foreach (DataColumn dc in DataSource.Columns) 
     { 
      TableRow tr = new TableRow(); 
      TableHeaderCell header = new TableHeaderCell(); 
      header.Text = dc.ColumnName; 
      tr.Controls.Add(header); 

      foreach (DataRow dr in DataSource.Rows) 
      { 
       TableCell tc = new TableCell(); 
       tc.Text = dr[dc.ColumnName].ToString(); 
       tr.Controls.Add(tc); 
      } 

      tbl.Controls.Add(tr); 
     } 

     Controls.Add(tbl); 
    } 
} 
+0

Ce n'est pas une mauvaise suggestion. J'ai eu l'intention de me faire apprendre à construire un contrôle sur Templated pendant un moment. :) –

0

Vous pouvez utiliser le composant Table mais vous devez travailler dur pour obtenir ce que vous voulez. La meilleure solution pour moi est d'utiliser GridView. Vous pouvez remplir le jeu de résultats pivoté à un datatable et créer des colonnes et des lignes datatable dynamiquement. Lorsque vous obtenez le format souhaité, vous pouvez lier la datatable à GridView.

J'avais fait quelque chose de similaire auparavant, c'était beaucoup plus facile à implémenter plutôt que d'essayer de créer votre propre contrôle.

0

Des contrôles croisés de tierce partie sont disponibles. Je pense d'abord à DevXpress ASPxPivotGrid. Si vous avez besoin des tableaux croisés uniquement pour les rapports non interactifs autonomes, vous pouvez envisager d'utiliser des outils de génération de rapports distincts et les intégrer dans un contrôle de visualiseur de rapports ASP.NET. SQL Server Reporting Services 2008 a une belle tablix region pour les données tabulaires et matricielles. Telerik Reporting a également des tableaux croisés. Mais, si vos besoins sont simples et bien définis, déployer un contrôle de table HTML comme suggéré est probablement la solution la plus rapide.