Generate columns dynamically in RDLC

丶灬走出姿态 提交于 2019-11-29 11:25:14

I had the same demand and did a bit survey of solutions.
I didn't try them all but still list those I think making sense.

  1. Use RDL instead of RDLC
    Because RDLC isn't easy to achieve dynamic columns. You may want just change to RDL.
  2. List all columns then hide those you don't need
    This works when your columns have max limitation and quite easy to implement by setting "Column visibility" with expression.

  3. Create RDLC at runtime
    RDLC is based on XML so it makes sense that you generate an RDLC at runtime which fit your data structure.
    Well, I didn't choose this since I think the schema of RDLC is a bit complicated plus my table is actually simple even it needs dynamic columns.
    If you really need to use this ultimate solution you may want to search if there are some sort of libs which can help you with the construction.

  4. Divide table into list of cells and reassembly them by group row and column identity
    This was suggested by my colleague Jimmy. A bit hacky but I found it very useful if your table doesn't have complex structures like ColumnSpan or stuff. Detail as the following example

ProductName | Qty    | Date
----------- | ------ | ---------
ProductA    | 1      | 2016-01-01
ProductA    | 2      | 2016-01-15
ProductA    | 3      | 2016-01-31
ProductA    | 1      | 2016-02-01
ProductA    | 2      | 2017-01-01
ProductA    | 3      | 2017-01-15
ProductA    | 1      | 2017-01-31
ProductA    | 2      | 2017-02-01
ProductA    | 3      | 2017-02-15
ProductA    | 1      | 2017-02-28
ProductB    | 2      | 2016-01-01
ProductB    | 3      | 2016-01-15
ProductB    | 1      | 2016-01-30
ProductB    | 2      | 2016-02-01
ProductB    | 3      | 2017-01-01

I need to total quantity by month or year and the result need to form like

ProductName | Jan    | Feb   
----------- | ------ | ------
ProductA    | 12     | 7   
ProductB    | 9      | 2     
ProductName | 2016   | 2017   
----------- | ------ | ------
ProductA    | 7      | 12   
ProductB    | 8      | 3     

When group by month I can list all 12 months and hide those I don't need.
However, it won't work when group by year.

To implement solution 4.
First, Prepare the DataTable formed exactly you need.

Second, Divide the DataTable into List<ReportCell>.

public class ReportCell
{
    public int RowId { get; set; }
    public string ColumnName { get; set; }
    public string Value { get; set; }

    public static List<ReportCell> ConvertTableToCells(DataTable table)
    {
        List<ReportCell> cells = new List<ReportCell>();

        foreach (DataRow row in table.Rows)
        {
            foreach (DataColumn col in table.Columns)
            {
                ReportCell cell = new ReportCell
                {
                    ColumnName = col.Caption,
                    RowId = table.Rows.IndexOf(row),
                    Value = row[col.ColumnName].ToString()
                };

                cells.Add(cell);
            }
        }

        return cells;
    }
}

Third, Use this List as the source of ReportViewer.

// in DAO
public List<ReportCell> GetReportCells(DataTable table)
{
    return ReportCell.ConvertTableToCells(table);
}    

// in aspx.cs
ReportViewer_main.LocalReport.ReportPath = Server.MapPath("~/RDLC/Report_main.rdlc");
ReportViewer_main.LocalReport.DataSources.Add(
    new ReportDataSource("DataSet1", dao.GetReportCells(tableGroupByMonth)));
ReportViewer_main.LocalReport.Refresh();

Finally, In RDLC add a table. Remove everything except data field.
Set data with the "Value" property.

Then Add Parent Group By the "ColumnName" property(Remove the sorting)

and make Details group on the "RowId" property

Now the rdlc should able to display everything exactly formed as the DataTable.

For better understanding, I made a demo project on my GitHub.

I am not sure your question is framed too elegantly. It is not possible to create a truly ad-hoc report. The work around is this. - Create the rdlc file - Create a dataset xsd file in the format you would get your data from. You will have to do this manually and each column should match your eventual output - Bind the rdlc file to this dataset datasource - Design your report

Now in the query generating part, you have to bind the dynamic data to the rdlc like this

                Dim repDS As New Microsoft.Reporting.WebForms.ReportDataSource
                repDS.Value = dT
                repDS.Name = "dsConsolReq_dt1" 'hard coded here, but you should get it dynamically perhaps from the querystring or the db

                RV1.LocalReport.DataSources.Clear()
                RV1.LocalReport.DataSources.Add(repDS)
                RV1.LocalReport.ReportPath = "Reports\rep_itemr1.rdlc" 'same again, use dynamic values
                RV1.ProcessingMode = Microsoft.Reporting.WebForms.ProcessingMode.Local
                RV1.LocalReport.Refresh()

The key here is the NAME of the report data source. It should exactly match the dummy dataset/datatable that you created for designing your report

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!