Can't Display DataTable in ReportViewer aspx.net

ぐ巨炮叔叔 提交于 2020-01-17 12:25:54

问题


I am having difficulty displaying the contents of a DataTable object in a ReportViewer control. There are no errors, just a blank report viewer being shown on the page. I have looked at the solutions presented in various similar questions found here, here and here - that last one is particularly frustrating as the last comment says "let's continue this discussion in chat" with no answer provided and it is essentially my exact issue.

Page code:

    <div class="panel-body">
         <rsweb:ReportViewer ID="ReportViewer1" runat="server" Width="100%">
              <LocalReport ReportPath="reports\Report1.rdlc">
              </LocalReport>
         </rsweb:ReportViewer>
   </div>

Codebehind:

protected void Page_Load(object sender, EventArgs e)
    {
        if (!IsPostBack)
        {
            string repstr = Request.Form["repId"];
            int repId = -1;
            int.TryParse(repstr, out repId);
            CustomReport rpt = new CustomReport(repId);
            data = rpt.ReportResult.Copy();//The report result property is a DataTable object that can have varying # of columns
            data.TableName = "CustomReport";
            ReportViewer1.ProcessingMode = ProcessingMode.Local;
            ReportDataSource source = new ReportDataSource("CustomReport", data);
            ReportViewer1.LocalReport.DataSources.Clear();
            ReportViewer1.LocalReport.DataSources.Add(source);
            ReportViewer1.DataBind();
            ReportViewer1.LocalReport.Refresh();
            ReportViewer1.Visible = true;
        }
    }

I have verified that the DataTable is being populated with data and through debugging I've verified that the ReportViewer data properties appear to have the data as well - it's just not being displayed....

Any help would be greatly appreciated!


回答1:


So after a lot of research and googling and help from my awesome development colleagues I came across a solution that essentially involves having to reconstruct the xml structure of the .rdlc file in order to support dynamic columns. There are a couple of caveats to this approach. 1. You have to manually configure the xml referencing the datasource to point at your object method that provides the datatable as the report designer does not support this. I've included my xml changes below. 2. You have to add a table to the report definition with only one column and one row. Maybe somebody can do this better.

So the solution. First the rdlc xml datasource config:

<DataSet Name="DataSet1">
  <Query>
    <DataSourceName>DataLayer</DataSourceName>
    <!--//Put the table name of your datatable object here-->
    <CommandText>CustomReportsDs</CommandText>
  </Query>
  <!--//This is a single empty field for mapping to the table control-->
  <Fields>
    <Field Name="ReportTitle">
      <DataField>ReportTitle</DataField>
      <rd:TypeName>System.String</rd:TypeName>
    </Field>
  </Fields>
  <rd:DataSetInfo>
    <!--//This must be configured to point to the method that supplies the datatable
        //In my instance the DataSetName is the namespace of the object, 
        //TableName is the object that provides the method and
        //ObjectDataSourceSelectMethod is the method that provides the datatable-->        
    <rd:DataSetName>DataLayer</rd:DataSetName>
    <rd:TableName>CustomReport</rd:TableName>
    <rd:ObjectDataSourceSelectMethod>ReportResult</rd:ObjectDataSourceSelectMethod>
    <rd:ObjectDataSourceType>DataLayer.CustomReport, DataLayer, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null</rd:ObjectDataSourceType>
  </rd:DataSetInfo>
</DataSet>

Next you have to configure the report viewer to recieve the report definition from a memory stream:

            //load the data
            int repId = -1;
            int.TryParse(Request.Form["repId"], out repId);
            CustomReport rpt = new CustomReport(repId);
            DataTable data = rpt.ReportResult.Copy();//The report result property is a DataTable object that can have varying # of columns
            data.TableName = "CustomReportDs";
            //setup the report viewer
            ReportViewer1.ProcessingMode = ProcessingMode.Local;
            ReportViewer1.LocalReport.ReportPath = string.Empty;
            //prepare to load the report definition xml file
            string ReportPath = Server.MapPath(@"\reports\Report1.rdlc");
            //my datatable comes with "friendly" column headings - these don't work well as the xml requires cls compliant
            //columnheadings so here we rename the columns in the data table but preserve the friendly names for use later
            Dictionary<string, string> columns = new Dictionary<string, string>();
            int counter = 0;
            foreach(DataColumn col in data.Columns)
            {
                columns.Add("column"+counter.ToString(), col.ColumnName);
                col.ColumnName = "column" + counter.ToString();
                counter++;
            }
            //format the report definition xml
            byte[] reportDefinitionBytes = Encoding.UTF8.GetBytes(ConfigurXMLReport(ReportPath, "CustomReportDs", columns).OuterXml);
            //load the formatted report into the reportviewer
            MemoryStream stream = new MemoryStream(reportDefinitionBytes);
            ReportViewer1.LocalReport.LoadReportDefinition(stream);
            //bind and display the data as normal
            ReportDataSource source = new ReportDataSource("DataSet1", data);
            ReportViewer1.LocalReport.DataSources.Clear();
            ReportViewer1.LocalReport.DataSources.Add(source);
            ReportViewer1.DataBind();
            ReportViewer1.LocalReport.Refresh();

Finally I have this method, which is a modified version of several methods written by others that I found during my research on the problem

public static XmlDocument ConfigurXMLReport(string path, Dictionary<string, string> Columns)
    {
        XmlDocument objXmlDocument = new XmlDocument();
        objXmlDocument.Load(path);
        XmlNamespaceManager mgr = new XmlNamespaceManager(objXmlDocument.NameTable);
        string uri = "http://schemas.microsoft.com/sqlserver/reporting/2008/01/reportdefinition";
        mgr.AddNamespace("df", uri);

        //locate and create nodes for cloning and modification
        XmlNode tablixCols = objXmlDocument.SelectSingleNode("/df:Report/df:Body/df:ReportItems/df:Tablix/df:TablixBody/df:TablixColumns", mgr);

        XmlNode dataField = objXmlDocument.SelectSingleNode("/df:Report/df:DataSets/df:DataSet/df:Fields/df:Field", mgr);

        XmlNode tablixMember = objXmlDocument.SelectSingleNode("/df:Report/df:Body/df:ReportItems/df:Tablix/df:TablixColumnHierarchy/df:TablixMembers/df:TablixMember", mgr);

        XmlNode rowHeaders = objXmlDocument.SelectSingleNode("/df:Report/df:Body/df:ReportItems/df:Tablix/df:TablixBody/df:TablixRows/df:TablixRow", mgr);
        XmlNode rowValues = rowHeaders.NextSibling.SelectSingleNode("./df:TablixCells", mgr);
        rowHeaders = rowHeaders.SelectSingleNode("./df:TablixCells", mgr);

        XmlNode sampleCol = tablixCols.SelectSingleNode("./df:TablixColumn", mgr);
        XmlNode sampleHeader = rowHeaders.SelectSingleNode("./df:TablixCell", mgr);
        XmlNode sampleValue = rowValues.SelectSingleNode("./df:TablixCell", mgr);
        //Iterate through the column definitions and add nodes to the xml to support the columns
        foreach (KeyValuePair<string,string> column in Columns)
        {
            //clone the sample nodes into new nodes
            XmlNode newDataField = dataField.CloneNode(true);
            newDataField.SelectSingleNode("./df:DataField", mgr).InnerText = column.Key;
            newDataField.Attributes["Name"].Value = column.Key;
            dataField.ParentNode.AppendChild(newDataField);

            XmlNode newCol = sampleCol.CloneNode(true);
            XmlNode newHeader = sampleHeader.CloneNode(true);
            XmlNode newValue = sampleValue.CloneNode(true);

            //update the new nodes with the column data
            newHeader.SelectSingleNode("./df:CellContents/df:Textbox", mgr).Attributes["Name"].Value = "Header" + column.Key;
            newValue.SelectSingleNode("./df:CellContents/df:Textbox", mgr).Attributes["Name"].Value = "Value" + column.Key;

            //because I use friendly column names - modify the report output to use the friendly name as the display value
            newHeader.SelectSingleNode("./df:CellContents/df:Textbox/df:Paragraphs/df:Paragraph/df:TextRuns/df:TextRun/df:Value", mgr).InnerText = column.Value;
            newValue.SelectSingleNode("./df:CellContents/df:Textbox/df:Paragraphs/df:Paragraph/df:TextRuns/df:TextRun/df:Value", mgr).InnerText = string.Format("=Fields!{0}.Value", column.Key);

            //add the new nodes to the document
            tablixCols.AppendChild(newCol);
            rowHeaders.AppendChild(newHeader);
            rowValues.AppendChild(newValue);

            tablixMember.ParentNode.AppendChild(tablixMember.CloneNode(true));
        }

        //remove the nodes used for cloning
        objXmlDocument.SelectSingleNode("/df:Report/df:DataSets/df:DataSet/df:Fields", mgr).RemoveChild(dataField);
        objXmlDocument.SelectSingleNode("/df:Report/df:Body/df:ReportItems/df:Tablix/df:TablixColumnHierarchy/df:TablixMembers", mgr).RemoveChild(tablixMember);
        tablixCols.RemoveChild(sampleCol);
        rowHeaders.RemoveChild(sampleHeader);
        rowValues.RemoveChild(sampleValue);
        //return the completed report definition
        return objXmlDocument;
    }

Many thanks to this article for getting me pointed in the right direction.



来源:https://stackoverflow.com/questions/29778769/cant-display-datatable-in-reportviewer-aspx-net

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