ASP.NET - идеальный элемент управления для отображения сводной таблицы - PullRequest
1 голос
/ 07 октября 2009

Я пытаюсь отобразить табличный набор данных в элементе управления с привязкой к данным, но мне нужно повернуть таблицу так, чтобы отдельные записи представляли собой столбцы таблицы, а не строки таблицы. Конечным результатом является таблица с фиксированным количеством столбцов и переменным количеством строк, в каждой из которых отображается одно поле для всех записей , например . В связи с тем, что теги

должны быть определены для каждого поля, а не для каждой записи, ретранслятор не является подходящим способом для этого. Мне интересно, есть ли какие-то встроенные элементы управления ASP.NET, которые могут достичь того, что я хочу. Я смотрел элемент управления ListView, однако я не уверен, что он действительно способен к тому, что я описываю.

Эффективно, если предположить записи, подобные следующим:

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

Мне нужно отобразить:

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

Жизнеспособной альтернативой борьбе с тегами

будет, конечно, использование display: inline с некоторым изящным CSS, но если я смогу сохранить структуру

Ответы [ 4 ]

5 голосов
/ 11 ноября 2009

Недавно я столкнулся с той же проблемой, и при поиске ответа обнаружил, что большинство решений работали вокруг поворота исходных данных.

Мне пришло в голову, что проблема не в исходных данных, а в том, каким образом мы хотим их отобразить. Хотя приведенный выше ответ Криса действительно пытается изменить данные в точке рендеринга, я обнаружил, что для своих нужд он не будет достаточно гибким, если мне понадобится шаблонное представление сетки. Тогда-то и случилось, что, возможно, лучшим решением проблемы было бы перехватить HTML-разметку таблицы таблицы и изменить это - это фактически означало бы, что решение можно было применить к абсолютно любому элементу управления, отображающему разметку таблицы, и к любым элементам управления шаблона внутри он будет продолжать работать.

Из-за нехватки времени я реализовал решение только в виде сетки; Первоначально я хотел создать элемент управления сервером шаблонов, чтобы, если какой-либо элемент управления был помещен в него, он проверял его выходные данные разметки и поворачивал все содержащиеся в нем таблицы)

В любом случае, вот код, который необходим для реализации этого решения в виде сетки.

Пользовательский код управления сервером

[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-код службы синтаксического анализа, выделенный для простоты реализации в другом месте.

//... 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 голосов
/ 07 октября 2009

Лучше всего будет создать собственный серверный элемент управления.

Я сделал нечто похожее на это, создав собственный серверный элемент управления, который расширил GridView. Затем я повернул источник данных и динамически добавил столбцы в сетку при привязке данных.

Я использовал подход GridView, чтобы внешний вид и поведение были похожи на остальную часть моего сайта. В качестве альтернативы вы можете создать более простой пользовательский элемент управления, который создает HTML для таблицы в методе Render.

Удачи.

РЕДАКТИРОВАТЬ: Вот пример того, как сделать серверный элемент управления на основе таблицы (это было не так сложно, но будьте осторожны, я не проверял это ...):

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 голосов
/ 11 октября 2009

Доступны сторонние элементы управления кросс-таблицами. DevXpress ASPxPivotGrid приходит на ум первым.

И если вам нужны перекрестные таблицы исключительно для автономных неинтерактивных отчетов, то вы можете рассмотреть возможность использования отдельных инструментов отчетности и встроить отчеты в элемент управления просмотра отчетов ASP.NET. В SQL Server Reporting Services 2008 есть хорошая область табликса для табличных и матричных данных. Telerik Reporting также имеет кросс-таблицы.

Но, если ваши потребности просты и четко определены, развертывание элемента управления таблицей HTML в соответствии с предложением, вероятно, является самым быстрым решением.

0 голосов
/ 07 октября 2009

Вы можете использовать компонент Table, но вам нужно много работать, чтобы получить то, что вы хотите. Лучшее решение для меня - использовать GridView. Вы можете заполнить сводный результирующий набор данными и создать динамически создаваемые столбцы и строки с данными. Когда вы получите желаемый формат, вы можете связать данные с GridView.

Я делал нечто подобное раньше, это было гораздо проще реализовать, чем пытаться создать свой собственный элемент управления.

...