首頁
編程語言
C語言|JAVA編程
Python編程
網頁編程
ASP編程|PHP編程
JSP編程
數據庫
MYSQL數據庫|SqlServer數據庫
Oracle數據庫|DB2數據庫
您现在的位置: 程式碼之家 >> 編程語言 >  >> .NET 網頁程式設計 >> 關於.NET

RDLC 報表實戰系列(四):主子報表設計與資料綁定完整指南

     編輯:關於.NET

當報表需求涉及「一對多」關係(如:員工與其出差記錄、訂單與明細行),子報表 是最佳解決方案。本文以「員工出差記錄總表」為例,完整演示 RDLC 主子報表的設計、參數傳遞、資料篩選與後端程式碼實作。

報表模板設計:雙模板架構

  1. 建立專案:VS 2008+ 建立 Web Application,新增兩個報表檔案 Master.rdlc(主報表)與 Sub.rdlc(子報表)。
  2. 主報表佈局:拖入 Table 控制項綁定主資料集(如員工基本資料),在需顯示子明細處插入 Subreport 控制項。
  3. 關聯子報表:右鍵 Subreport → 屬性 → 選擇 Sub.rdlc,並新增參數(如 EmpOid)傳遞給子報表作為篩選條件。
  4. 子報表設計:新增對應資料集,建立同名參數 EmpOid,在 Table 的 Filters 頁籤設定篩選條件 =Parameters!EmpOid.Value,僅顯示該員工的出差記錄。

新增報表檔案對話框

插入 Subreport 控制項

設定 Subreport 參數綁定

後端核心程式碼:SubreportProcessing 事件處理

關鍵在於攔截 LocalReport.SubreportProcessing 事件,動態為子報表注入資料源:

public class CustomPageBase : Page {
    private List<ReportDataSource> _subDataSources = new List<ReportDataSource>();

    public void GetReportMultipleDataSourceFile(
        List<ReportDataSource> reportDataSources, 
        string templatePath, 
        List<ReportParameter> parameters, 
        string fileType, 
        List<string> subDataSourceNames) 
    {
        var viewer = new ReportViewer {
            ProcessingMode = ProcessingMode.Local,
            LocalReport = { ReportPath = Server.MapPath(templatePath) }
        };
        viewer.LocalReport.DataSources.Clear();

        // 區分主/子報表資料源
        foreach (var ds in reportDataSources) {
            if (subDataSourceNames.Contains(ds.Name))
                _subDataSources.Add(ds); // 暫存子報表資料
            else
                viewer.LocalReport.DataSources.Add(ds); // 主報表直接加入
        }

        // 訂閱子報表處理事件
        viewer.LocalReport.SubreportProcessing += SubreportProcessingEventHandler;

        if (parameters.Count > 0)
            viewer.LocalReport.SetParameters(parameters);

        // 渲染輸出
        string mimeType, encoding, extension, deviceInfo = @"True";
        string[] streamIds; Warning[] warnings;
        byte[] bytes = viewer.LocalReport.Render(fileType, deviceInfo, out mimeType, out encoding, out extension, out streamIds, out warnings);

        Response.Buffer = true; Response.Clear();
        Response.ContentType = mimeType;
        Response.AddHeader("Content-Disposition", $"attachment; filename=Report.{extension}");
        Response.BinaryWrite(bytes); Response.End();
    }

    void SubreportProcessingEventHandler(object sender, SubreportProcessingEventArgs e) {
        foreach (var ds in _subDataSources)
            e.DataSources.Add(ds); // 將預載資料源注入子報表
    }
}

頁面調用範例

public partial class _Default : CustomPageBase {
    protected void ButtonGenerate_Click(object sender, EventArgs e) {
        var reportDataSources = new List<ReportDataSource>();
        var subDataSourceNames = new List<string>();
        var ds = new ReportDataSet();

        using (var conn = new SqlConnection(ConfigurationManager.ConnectionStrings["PQMSConnectionString"].ConnectionString)) {
            var cmd = conn.CreateCommand();
            // 主表資料:ISO_DRAWING 前 200 筆
            cmd.CommandText = "SELECT TOP 200 Oid, DrawingNumber, CWPNo, PaintCode, MasterRevision FROM ISO_DRAWING";
            new SqlDataAdapter(cmd).Fill(ds, "ISO_DRAWING");

            // 子表資料:關聯的 SPOOL 明細
            cmd.CommandText = @"SELECT S.Oid, S.ISOOid, S.SPOOLNo, S.Location, S.IssueNo, S.CP2 
                                FROM SPOOL S INNER JOIN (SELECT TOP 200 OID FROM ISO_DRAWING) T ON S.ISOOID = T.OID";
            new SqlDataAdapter(cmd).Fill(ds, "SPOOL");
        }

        reportDataSources.Add(new ReportDataSource("ReportDataSet_ISO_DRAWING", ds.ISO_DRAWING));
        reportDataSources.Add(new ReportDataSource("ReportDataSet_SPOOL", ds.SPOOL));
        subDataSourceNames.Add("ReportDataSet_SPOOL"); // 標記此資料源給子報表

        GetReportMultipleDataSourceFile(reportDataSources, "Master.rdlc", new List<ReportParameter>(), "PDF", subDataSourceNames);
    }
}

關鍵技術要點總結

環節核心操作常見坑點
模板關聯Subreport 控制項指定 .rdlc 檔 + 參數映射參數名稱大小寫必須與子報表完全一致
資料篩選子報表 Table 屬性 → Filters 設定參數比對資料型別不匹配導致篩選失效
事件處理SubreportProcessing 事件中 Add DataSources未清空舊資料源導致重複顯示
資料源區分傳入 subDataSourceNames 列表標記子報表專用資料主子報表使用同名資料集造成衝突

進階應用場景

  • 多層巢狀子報表:遞迴訂閱 SubreportProcessing,層層傳遞參數與資料源。
  • 動態子報表切換:根據主報表資料動態決定載入哪個 .rdlc 模板。
  • 效能優化:單次查詢取回所有主子資料(如上例 JOIN 查詢),避免 N+1 查詢問題。

掌握主子報表機制,即可輕鬆應對複雜的多層級報表需求。核心在於:模板參數映射正確 + 事件中精準注入對應資料源。

  1. 上一篇文章:
  2. 下一篇文章:
Copyright © 程式碼之家 All Rights Reserved