[ASP NET MVC] 使用ReportViewer執行用戶端報表定義檔案(.rdlc)產出報表

使用ReportViewer執行用戶端報表定義檔案(.rdlc)來產出報表

前言

 

以往使用ASP.NET WebForm進行網站開發時,筆者面對報表的產出多會使用ReportViewer來進行,並且搭配用戶端報表定義檔案(.rdlc)來設計報表外觀,其實是相當靈活的解決方案;如今使用ASP.NET MVC進行開發,雖然View中無法加入任何WebForm Control了,但我們依舊可以建立一個共用WebForm頁面,在此頁面上加入熟悉的ReportViewer來協助產出報表。詳細實作細節請參考以下文章。

 

 

實作說明

 

整體實作概念就是建立一個WebForm頁面於ASP.NET MVC專案中,利用該WebForm頁面來實作ReportViewer相關產出報表工作;最終當有報表產出需求時,僅需在Controller中傳遞ReportViewer所需相關資訊至WebForm頁面中,利用該WebForm頁面來呈現相對應之報表內容。以下進行實作說明。

 

 

建立 ReportViewer Web Form 作為報表頁面

 

首先新增 ReportViewer.aspx 檔案

 

image

 

加入 ScriptManager 與 ReportViewer 於 WebForm 表單中。

 

image



<%@ Register assembly="Microsoft.ReportViewer.WebForms, Version=11.0.0.0, Culture=neutral, PublicKeyToken=89845dcd8080cc91" namespace="Microsoft.Reporting.WebForms" tagprefix="rsweb" %>

<!DOCTYPE html>

<html xmlns="http://www.w3.org/1999/xhtml">
<head runat="server">
<meta http-equiv="Content-Type" content="text/html; charset=utf-8"/>
    <title></title>
</head>
<body>
    <form id="form1" runat="server">
        <asp:ScriptManager ID="ScriptManager1" runat="server"></asp:ScriptManager>
        <div>
            <rsweb:ReportViewer ID="RptViewer" runat="server" Width="599px">
            </rsweb:ReportViewer>
        </div>
    </form>
</body>
</html>

 

由於筆者希望共用 ReportViewer 頁面,因此建立 ReportWarpper 類別來封裝 ReportViewer 所需之各項資訊,最終我們即可於 Controller 中操作該些資訊來產出不同報表表單。封裝的資訊包含 rdlc 檔案位置、資料來源(ReportDataSource、ReportParameter),以及是否直接下載報表控制旗標(IsDownloadDirectly)。

 


{
    // Constructors
    public ReportWrapper()
    {
        ReportDataSources = new List<ReportDataSource>();
        ReportParameters = new List<ReportParameter>();
    }


    // Properties
    public string ReportPath { get; set; }

    public List<ReportDataSource> ReportDataSources { get; set; }

    public List<ReportParameter> ReportParameters { get; set; }

    public bool IsDownloadDirectly { get; set; }

}

 

由於Request會先至Controller中,然後依產出報表內容來取得相關資料填入ReportWarpper中,再轉址至 ReportViewer WebForm 頁面中依據 ReportWarpper 資訊呈現報表;因為有跨頁面訊息傳遞需求,所以在此使用Session作為傳遞ReportWarpper媒介。接著就可依據 ReportWarpper 提供的資訊來實作 ReportViewer 產出報表共用邏輯,其實主要就是在設定戶端報表定義檔案(.rdlc)位置,並且給予資料來源資訊(ReportDataSource、ReportParameter),最後判斷是否直接輸出檔案供用戶下載使用。

 


{
    protected void Page_Load(object sender, EventArgs e)
    {
        if (!IsPostBack)
        {
            GenerateReport();
        }
    }

    private void GenerateReport()
    {

        var ReportWrapperSessionKey = "ReportWrapper";
        var rw = (ReportWrapper)Session[ReportWrapperSessionKey];
        if (rw != null)
        {
            // Rdlc location
            RptViewer.LocalReport.ReportPath = rw.ReportPath;

            // Set report data source
            RptViewer.LocalReport.DataSources.Clear();
            foreach (var reportDataSource in rw.ReportDataSources)
            { RptViewer.LocalReport.DataSources.Add(reportDataSource); }

            // Set report parameters
            RptViewer.LocalReport.SetParameters(rw.ReportParameters);

            // Refresh report
            RptViewer.LocalReport.Refresh();

            // Download report directly
            if (rw.IsDownloadDirectly)
            {
                Warning[] warnings;
                string[] streamids;
                string mimeType;
                string encoding;
                string extension;

                byte[] bytes = RptViewer.LocalReport.Render(
                   "Excel", null, out mimeType, out encoding, out extension,
                   out streamids, out warnings);

                Response.Clear();
                Response.AddHeader("Content-Disposition", "attachment; filename=sample.xls");
                Response.AddHeader("Content-Length", bytes.Length.ToString());
                Response.ContentType = "application/octet-stream";
                Response.OutputStream.Write(bytes, 0, bytes.Length); 
            }

            // Remove session
           Session[ReportWrapperSessionKey] = null;
        }
        
    }
}

 

 

建立戶端報表定義檔案(.rdlc)

 

rdlc為報表設計的核心,可依需求來進行報表外觀及資料呈現設計。舉一個簡單的範例,需求是列出今年度新進員工清單於報表中,最終報表產出畫面如下,黃色部分為異動值,也就是要填入報表的資料。

 

image

 

首先加入名為 UserRpt 的 rdlc檔案

 

image

 

設計畫面如下,簡單使用文字方塊及資料表來呈現報表外觀。

 

image

 

 

設定表單資料來源

 

在此步驟中可以把它想成是在設計報表的ViewModel,而常使用的資料型態分別為DataSet及Parameter,因此我們可以依照報表資料型態的不同來使用不同報表資料來源型態,以下分別進行實作。

 

 

ReportDataSource - DataSet

 

以下黃色區塊很明顯的就是Table部分,因此可以使用DataSet做為報表資料來源。

 

image

 

首先建立資料集檔案(.xsd)並建立對應報表User Table之欄位如下。

 

image   image

 

接著就可以在 rdlc 中加入剛定義之資料集

 

image

 

在此筆者加入UserRptDataSet中User表單作為報表資料集。記得要為此資料集取比較好識別的名稱,因為後續在設定 Report Viewer 時會使用到,因此筆者都會使用 DataSetName_TableName 作為名稱以便識別。

 

image

 

最後就綁定資料集中各欄位至報表中即可。

 

image

 

 

Report Parameter

 

以下黃色區塊由於是單筆資訊,因此偏好使用參數(Parameter)做為報表資料來源。

 

image

 

直接在 rdlc 中加入參數資料

image

 

建立一個名稱為RptMakerParam報表參數。因為後續在設定 Report Viewer 時會使用到,記得要為此參數資料取個比較好識別的名稱,否則參數一多的話可能會很混亂。

 

image

 

接著綁定參數資料至報表中

 

image

 

 

產出報表

 

最後寫個測試網頁,點選瀏覽報表時會透過ReportViewer來顯示報表,而點選下載報表時就不會顯示報表於畫面上,而是直接下載報表為Excel檔案。

 

image

 

瀏覽報表的controller代碼如下

 


{
    public ActionResult BrowseReport()
    {
        // Prepare data in report
        UserRptDataSet ds = new UserRptDataSet();
        ds.User.AddUserRow("chris",  "chris chen", "Taipei");
        ds.User.AddUserRow("eunice", "eunice chen", "New Taipei");

        // Set report info
        ReportWrapper rw = new ReportWrapper();
        rw.ReportPath = Server.MapPath("/Report/Rdlc/UserRpt.rdlc");
        rw.ReportDataSources.Add(new ReportDataSource("UserRptDataSet_User", ds.User.Copy()));
        rw.ReportParameters.Add(new ReportParameter("RptMakerParam", "CHRIS"));
        rw.IsDownloadDirectly = false;

        // Pass report info via session
        Session["ReportWrapper"] = rw;

        // Go report viewer page
        return Redirect("/Report/ReportViewer.aspx");  
    }
}

 

點選後直接顯示報表於頁面上。

 

image

 

下載報表的controller代碼如下,與上差別在設定IsDownloadDirectly旗標為true

 

image

 

點選下載報表連結後,直接下載儲存為Excel檔案

 

image

 

 

參考資訊

 

http://www.c-sharpcorner.com/UploadFile/ff2f08/incorporating-Asp-Net-mvc-and-sql-server-reporting-services/

http://pinnynet.blogspot.tw/2009/11/cliend-side-report.html


希望此篇文章可以幫助到需要的人

若內容有誤或有其他建議請不吝留言給筆者喔 !