Search code examples
c#asp.netlistviewsqldatasourceitemtemplate

Dynamically-generated Columns in ItemTemplate for Data-bound ListView


I have a page in my ASP.NET web application that is essentially a tool for generating ad-hoc reports. Users set values among a multitude of critera and then submit those to retrieve a result set. The SQL needed to get the result is generated at run-time; in code I use the SQL to create a SqlDataSource and then tie that to a ListView. Up to that point everything is working just fine.

What I can't figure-out is how to, then, dynamically generate the columns for the ListView. The problem lies in the fact that I don't know the number/names of the columns at compile-time, since they are dependent upon the output of the dynamically-generated SQL. I assumed this would be a very simple little exercise, but I cannot seem to find a simplified example that illustrates how to do this.

My existing code is almost irrelevant, since I'm basically asking for ground-up examples. I'm not even sure if my approach is correct--i.e., using a ListView in markup and trying to manipulate its template in code, at runtime. Here are some snippets, however...

Here is my method that generates the SQL, just for the sake of reference:

    public static string GetReportSQL(HttpContext c = null)
    {
        if (c.Equals(null)) c = HttpContext.Current;

        string report_id = c.Request.Params["report_id"].ToString();
        string select_list = null;
        string where_list = null;
        string field = null;
        string criteria_value = null;

        POST.App_Objects.Report report = new POST.App_Objects.Report();
        POST.App_Objects.Report report_fields = new POST.App_Objects.Report();
        DataSet rds = report.GetReport_Summary_ByID(Int32.Parse(report_id));
        DataSet rfds = report_fields.GetReportFields(Int32.Parse(report_id));

        foreach (DataRow f in rfds.Tables[0].Rows)
        {
            field = f["report_field_name"].ToString();

            if (!String.IsNullOrEmpty(c.Request["show_field__" + field]))
            {
                select_list += (!String.IsNullOrEmpty(select_list) ? ", " : null) + field + " AS '" + f["report_field_title"].ToString() + "'";
            }
            if (!String.IsNullOrEmpty(c.Request["criteria_value__" + field]))
            {
                criteria_value = c.Request["criteria_value__" + field].ToString();
                where_list += (!String.IsNullOrEmpty(where_list) ? " AND " : null) + field;

                switch (f["report_field_filter_type"].ToString())
                {
                    case FILTER_TYPE_SEARCH_TERM:
                        where_list += " LIKE '%" + criteria_value + "%'";
                        break;
                    case FILTER_TYPE_TEXT_LIST:
                        break;
                    case FILTER_TYPE_MULTIPLE_SELECT:
                        where_list += " IN ('" + criteria_value.Replace(",", "','") + "')";
                        break;
                }
            }
        }
        string sql = "SELECT " + select_list + " FROM " + rds.Tables[0].Rows[0]["report_definition_source"].ToString() + " WHERE " + where_list;
        return sql;
    }

Here is my Init method for the ListView control, which associates it with a SqlDataSource based on the above SQL-generating code:

    public void ReportResultListView_Init(object sender, ListViewEditEventArgs e)
    {
        ListView rlv = (ListView)ReportFormView.FindControl("ReportResultListView");
        SqlDataSource rds = new SqlDataSource(
            ConfigurationManager.ConnectionStrings["POST"].ConnectionString.ToString(),
            GetReportSQL(HttpContext.Current)
        );
        rlv.DataSource = rds;
        ListViewDataItem lvdi = new ListViewDataItem(0,0);
        rlv.Items.Add(lvdi);
        rlv.DataBind();
    }

And lastly, here's the snippet of markup where I've got the relevant ListView (which is inside a FormView, just FYI):

    <asp:ListView
        ID="ReportResultListView"
        OnInit="ReportResultListView_Init"
        runat="server">
        <ItemTemplate>
            <li><%# Eval("Organization Code").ToString() %></li>
        </ItemTemplate>
        <EmptyDataTemplate>
            Nothing.
        </EmptyDataTemplate>
    </asp:ListView>

I've got that one binder ("Organization Code") in there simply for testing--I know that particular column will show-up regardless. To clarify, what I'm after is specifying the fields to output at run-time, with their names being the result of a different query...

I know, of course, that this is possible, I'm just not seeing the example that suits what I need. If I wasn't so new to .NET I imagine this would be fairly obvious...


Solution

  • Michael, give a chance to a GridView. Create it at you ASPX like this:

    <asp:gridview id="gvReportResults" autogeneratecolumns="True" runat="server" ></asp:gridview>
    

    And, at your code-behind:

    gvReportResults.DataSource = rds;
    gvReportResults.DataBind();
    

    I'm not sure if this will be enough to solve your problem, but that's a good start.

    Regards