顯示具有 SSRS 標籤的文章。 顯示所有文章
顯示具有 SSRS 標籤的文章。 顯示所有文章

2016年8月2日 星期二

[RDLC] 使用子報表(SubReport)從既有RDLC建立多筆報表

 #RDLC   #Report viewer   #SSRS



Introduction


小弟日前完成了單張報表RDLC(以單號=XXX帶出)的情況下,使用者提出是否可同時列印多張報表(一次帶入多個單號)的需求;
因為這張報表很複雜,不太可能為了一次列印多張而重拉RDLC

這時候比較聰明的作法就是利用「子報表」Sub-Report搭配「資料表」DataTable的方式, 重複使用既有已經設計好的報表來完成多張報表的需求


Related articles



我們將延續以上文章的範例,實作利用子報表完成多張報表的功能。



Implement


以下是此範例將會新增的項目~

 

資料集:DataSetCityName

首先,我們必須設計一個DataSet來存放帶出多張報表的Key(參數值),例如本例的城市名稱 將用來作為帶入子報表的資料庫查詢參數。



RDLCCityCollection.rdlc (母報表)

RDLC加入前一步驟的資料集。

 

另外我們在run time時,會將DataSetCityName裡面的Name值,注入到子報表作為參數做查詢。 所以這一張母報表需要加入子報表的資料來源 (但是不需要子報表的資料集)

做法是先加入子報表的資料集,此時一併帶入資料來源,接著再移除子報表的資料集。


接著重頭戲來了,開始設計RDLC!!
先加入資料表DataTable


我們只需資料表留一列,並加入資料集:DataSetCityName[Name]
接著插入一個群組內-下方的資料列,並加入SubReport


插入子報表


設定子報表要使用哪一張既有的RDLC,本例的子報表是 : City

 

在子報表控制項加入一個參數,其值指向DataSetCityName[Name]



 


到這邊已經完成了母報表RDLC的設計, 重點在於
l   帶入多個城市名稱給母報表RDLC
l   指派每個城市名稱給子報表RDLC作為查詢參數


RDLCCity.rdlc (子報表)

子報表只有一個地方需要修改,由於子報表的查詢參數是由母報表帶入,所以需要加入一個報表參數: CityName




ASPX:帶入資料到母報表及子報表的ReportViewer

ASPX請自行加入ReportViewerScriptManager控制項。

l   Page_Load

以下Sample code是在Page_Load時,利用Url parameter帶入母報表所需的多個城市名稱,再存入DataSet帶入到母報表的RDLC

protected void Page_Load(object sender, EventArgs e)
{
            if (!IsPostBack)
            {
                var qStr = Request.QueryString["cityNames"].ToString();
                char[] separators = new char[] { ',' };
                string[] cityNames = qStr.Split(separators);

                try
                {

                    rptViewer.LocalReport.SetBasePermissionsForSandboxAppDomain(new PermissionSet(PermissionState.Unrestricted));
                    rptViewer.ShowPrintButton = true;
                    rptViewer.ProcessingMode = ProcessingMode.Local;
                    rptViewer.LocalReport.EnableExternalImages = true;
                    rptViewer.LocalReport.ReportPath = Server.MapPath("~/Reports/Rdlc/CityCollection.rdlc");
                    rptViewer.LocalReport.DataSources.Clear();

                    #region MainReport
                    DataSetCityName ds = getDataSet(cityNames.ToList());
                    ReportDataSource datasource = new ReportDataSource("DataSetCityName", ds.Tables[0]);
                    rptViewer.LocalReport.DataSources.Add(datasource);
                    #endregion

                    #region SubReport
                    rptViewer.LocalReport.SubreportProcessing += new SubreportProcessingEventHandler(SetSubDataSource);
                   
                    #endregion

                    rptViewer.LocalReport.Refresh();
                }
            }
}

private DataSetCityName getDataSet(List<string> cityNames)
{               
             var dataset = new DataSetCityName();
             var dataTable = dataset.Tables[0];
             foreach (var cn in cityNames)
             {
                var dr = dataTable.NewRow();
                dr["Name"] = cn;
                dataTable.Rows.Add(dr);
             }
             return dataset;
}

至於子報表的資料要如何帶入? 設定rptViewer.LocalReport.SubreportProcessing 這個delegate eventhandler來另行處理。

底下是負責SubreportProcessingevent,可以看到帶出子報表的方式其實很熟悉,
l   取得查詢的參數值(city)
l   呼叫Store Procedure
l   將資料放入DataSet
l   DataSet放入子報表RDLCDataSource

唯一的不同在於查詢的參數值,改由報表參數帶入,也就是我們在上面其中一個步驟加入到子報表的參數:CiteName



public void SetSubDataSource(object sender, SubreportProcessingEventArgs e)
{
                var city = e.Parameters["CityName"].Values.First();

                DataSetCity dsCity = getCityData(city);
                ReportDataSource datasource = new ReportDataSource("DataSetCity", dsCity.Tables[0]);
                e.DataSources.Add(datasource);

}

private DataSetCity getCityData(string cityName)
{
       var rtnVal = new DataSetCity();

       var connString = ConfigurationManager.ConnectionStrings["JbDbContext"].ConnectionString;
       var sqlConn = new SqlConnection(connString);
       SqlCommand cmdReport = new SqlCommand("usp_getCitys", sqlConn);
       SqlDataAdapter daReport = new SqlDataAdapter(cmdReport);
       using (cmdReport)
       {
                    SqlParameter questionIdPrm = new SqlParameter("@CityName", cityName);
                    cmdReport.CommandType = CommandType.StoredProcedure;
                    cmdReport.Parameters.Add(questionIdPrm);

                    daReport.Fill(rtnVal, "DataTableCity");

       }
       return rtnVal;
}


執行結果

原本單筆的報表(City.rdlc)
 

多筆的報表(CityCollection.rdlc)






Reference

2016年7月3日 星期日

[RDLC] 建立平行排列的data list

 #RDLC   #Report viewer   #SSRS 



Introduction


在設計RDLC清單時,其預設是逐筆將資料垂直顯示於報表 (如下圖一)
我們的目標是將資料平行顯示 (如圖二)

(圖一)

(圖二)

 


Implement



移除資料列群組

右鍵選擇清單,點選 【資料列群組】→【刪除群組

 


加入資料行群組



設定群組依據為該資料表的Key



接著請在畫面上隱藏”Id”

 


 




結果







Reference

[RDLC] MVC專案建立Reporting報表( Report Viewer )

 #RDLC   #Report viewer   #MVC 

Introduction


此篇文章將介紹如何在MVC專案中,加入Report viewer來開發RDLC報表。
由於Report viewer是含有View StateWeb form控制項,因此解法是在MVC專案中加入Web form page來開發報表。


Implement



MVC專案加入Web form (.ASPX)

 

Web form 放入ReportViewerScriptManager控制項







 


設定資料來源及資料集

取得資料來源的方式,採用傳統ADO.NET的方式會比較方便,因為RDLC的資料來源為資料集”(DataSet)。 當然也可以使用ORM,只是需要再轉為DataTable再放入DataSet

另外為了查詢資料邏輯的彈性和維護性,我建議是查詢建立成View,然後以Store Procedure帶入參數取得 View的資料。

l   Store Procedure

CREATE PROCEDURE [dbo].[usp_getCitys]
    @CityName NVARCHAR(20) --傳入之城市名稱
AS
BEGIN
   
    SET NOCOUNT ON;
    SELECT Id,Name
    FROM Citys
    WHERE Name LIKE '%'+ @CityName +'%';
END



l   DataSet

接著在專案加入對應的資料集(.XSD)



設計對應欄位
並記住每個DataTable 的名稱,後面程式碼會使用到。




l   RDLC

下一步就是設計報表囉! 在專案加入報表(.RDLC)

 


並在報表資料中,加入我們剛才建立的資料集。


 

PS. 資料集的名稱很重要唷! 等下我們寫程式碼的時候會用到。

最後請設計RDLC 下一步,我們要開始寫程式碼把資料從Store Procedure讀進Dataset,再將DataSet丟到RDLC


查詢程式

回到Web formPage load function, 加入以下程式

l   Page_Load

//Set base permission, 避免'System.Security.Permissions.SecurityPermission' 的使用權限要求失敗
rptViewer.LocalReport.SetBasePermissionsForSandboxAppDomain(
      new PermissionSet(PermissionState.Unrestricted));

rptViewer.ProcessingMode = ProcessingMode.Local; //Use local report viewer's engine
rptViewer.ShowPrintButton = true; //顯示列印鈕
rptViewer.LocalReport.EnableExternalImages = true;
rptViewer.LocalReport.ReportPath = Server.MapPath("~/Reports/Rdlc/City.rdlc");
rptViewer.LocalReport.DataSources.Clear();

#region  取得資料
DataSetCity dsCity = getCityData(cityName);
ReportDataSource datasource = new ReportDataSource("DataSetCity", dsCity.Tables[0]);
rptViewer.LocalReport.DataSources.Add(datasource);
#endregion

PS. cityName 是使用者輸入的參數,由於.aspx (Web form) 無法以MVC預設的Routing rule來執行,因此在這邊我仍是選擇以MVCView為主,以iframe內嵌這張Web form 並將使用者參數用URL參數的方式帶入到Web form

var cityName = Request.QueryString["cityName"].ToString();


l   getCityData()

private DataSetCity getCityData(string cityName)
{               
var rtnVal = new DataSetCity();

          var connString = ConfigurationManager.ConnectionStrings["JbDbContext"].ConnectionString;
          var sqlConn = new SqlConnection(connString);
          SqlCommand cmdReport = new SqlCommand("usp_getCitys", sqlConn);
          SqlDataAdapter daReport = new SqlDataAdapter(cmdReport);

          using (cmdReport)
          {
             SqlParameter questionIdPrm = new SqlParameter("@CityName", cityName);
             cmdReport.CommandType = CommandType.StoredProcedure;
             cmdReport.Parameters.Add(questionIdPrm);
daReport.Fill(rtnVal, "DataTableCity");
          }
          return rtnVal;
}

PS. 程式碼中,黃色底的部分就是在設計過程中,需要記住的DataSet, SP, DataTable 名稱。


執行結果


 

  


Reference