Silverlight 报表查看器(使用 View Model (MVVM))






4.88/5 (20投票s)
使用 View Model (MVVM) 在 Silverlight 中查看 .rdlc 报表

在 Silverlight 应用程序中查看和打印报表
报表是Silverlight 业务线 (LOB) 应用程序的重要组成部分。虽然 Silverlight 4 确实提供了打印功能,但多页打印仍然具有挑战性。此外,许多企业已经投入了大量资源来学习使用 Visual Studio 中的报表设计器生成报表。
虽然有一些第三方控件可以在 Silverlight 中显示报表,但它们需要付费。此外,有些控件要求您使用完整的 Microsoft SQL Server Reporting Services (SSRS),这对于许多人来说可能不是一个选项。使用 Visual Studio 创建的 .rdlc 报表不需要 SSRS。
我们在本文中演示的方法是由 Sushant Pandey 开发的。他在这里 描述了它。此实现的不同之处在于,我们将使用 行为使其完全符合 MVVM (在他的实现中,他使用代码隐藏)。
打印报表
我们将从我几个月前发布的 Simple Silverlight Configurator/Pivot (View Model/MVVM) 开始。

该应用程序允许用户更改性别、体重和年龄选择器,它会动态过滤人员列表。

该应用程序已得到增强,允许用户单击“显示报表查看器”按钮,报表将在弹出窗口中显示。
如果他们单击“打印 PDF 报表”,他们将在弹出窗口中看到一份 PDF 报表。
我实际上更喜欢 报表查看器,因为用户在等待报表呈现时会看到动画。
创建报表

第一步是向项目中添加一个 报表。
该项目已经包含以下 Web 服务
[WebMethod]
public List<Person> SearchPeople(string Gender, int AgeHigh, int AgeLow,
int WeightHigh, int WeightLow)
{
List<Person> colPeople = new List<Person>();
var result = from objPeople in People.GetPeople().AsQueryable()
where objPeople.Age <= AgeHigh
where objPeople.Age >= AgeLow
where objPeople.Weight <= WeightHigh
where objPeople.Weight >= WeightLow
select objPeople;
if (Gender != "All")
{
colPeople = (from finalresult in result
where finalresult.Gender == Gender
select finalresult).ToList();
}
else
{
colPeople = result.ToList();
}
return colPeople;
}
我们可以选择该 Web 服务作为报表的数据集。

设计报表后,我们将报表控件放置在 .aspx 页面上。
以下代码用于页面后面的代码,用于接受报表参数,并显示 报表查看器控件 (ShowReportViewer()
),或呈现 .pdf 版本 (RenderReport()
)
public partial class Report : System.Web.UI.Page
{
protected void Page_Load(object sender, EventArgs e)
{
// If parameters are passed just render report
if
(
Request.QueryString["Gender"] != null &&
Request.QueryString["AgeHigh"] != null &&
Request.QueryString["AgeLow"] != null &&
Request.QueryString["WeightHigh"] != null &&
Request.QueryString["WeightLow"] != null
)
{
if (Request.QueryString["ShowReportViewer"] == "False")
{
RenderReport();
}
else
{
if (!Page.IsPostBack)
{
ShowReportViewer();
}
}
}
}
#region RenderReport
private void RenderReport()
{
// CheckBox to see if there is any data
if (LoadData().Rows.Count > 0)
{
LocalReport localReport = new LocalReport();
localReport.ReportPath = Server.MapPath("~/Report1.rdlc");
ReportDataSource reportDataSource =
new ReportDataSource("DataSet1", LoadData());
localReport.DataSources.Add(reportDataSource);
string reportType = "pdf";
string mimeType = "application/pdf";
string encoding;
string fileNameExtension;
//The DeviceInfo settings should be changed based on the reportType
//http://msdn2.microsoft.com/en-us/library/ms155397.aspx
string deviceInfo =
"<DeviceInfo>" +
" <OutputFormat>PDF</OutputFormat>" +
" <PageWidth>8.5in</PageWidth>" +
" <PageHeight>11in</PageHeight>" +
" <MarginTop>0.5in</MarginTop>" +
" <MarginLeft>1in</MarginLeft>" +
" <MarginRight>1in</MarginRight>" +
" <MarginBottom>0.5in</MarginBottom>" +
"</DeviceInfo>";
Warning[] warnings;
string[] streams;
byte[] renderedBytes;
//Render the report
renderedBytes = localReport.Render(
reportType,
deviceInfo,
out mimeType,
out encoding,
out fileNameExtension,
out streams,
out warnings);
//Clear the response stream and write the bytes to the outputstream
//Set content-disposition to "attachment"
//so that user is prompted to take an action
//on the file (open or save)
Response.Clear();
Response.ContentType = mimeType;
//Response.AddHeader("content-disposition",
"attachment; filename=foo." + fileNameExtension);
Response.BinaryWrite(renderedBytes);
Response.End();
}
else
{
// There were no records returned
Response.Clear();
//Response.AddHeader("content-disposition",
"attachment; filename=foo." + fileNameExtension);
Response.Write("No Data");
Response.End();
}
}
#endregion
#region ShowReportViewer
private void ShowReportViewer()
{
this.ReportViewer1.ProcessingMode = ProcessingMode.Local;
this.ReportViewer1.LocalReport.ReportPath = Server.MapPath("~/Report1.rdlc");
ReportViewer1.LocalReport.DataSources.Add(
new ReportDataSource("DataSet1", LoadData()));
}
#endregion
以下方法在运行时为报表提供数据
#region LoadData
private DataTable LoadData()
{
string Gender = Convert.ToString(Request.QueryString["Gender"]);
int AgeHigh = Convert.ToInt32(Request.QueryString["AgeHigh"]);
int AgeLow = Convert.ToInt32(Request.QueryString["AgeLow"]);
int WeightHigh = Convert.ToInt32(Request.QueryString["WeightHigh"]);
int WeightLow = Convert.ToInt32(Request.QueryString["WeightLow"]);
var result = from objPeople in People.GetPeople().AsQueryable()
where objPeople.Age <= AgeHigh
where objPeople.Age >= AgeLow
where objPeople.Weight <= WeightHigh
where objPeople.Weight >= WeightLow
select objPeople;
if (Gender != "All")
{
result = from finalresult in result
where finalresult.Gender == Gender
select finalresult;
}
Utility objUtility = new Utility();
DataTable dt = objUtility.LINQToDataTable(result);
return dt.DefaultView.Table;
}
#endregion
请注意,它使用一个名为 LINQToDataTable
的类,该类将 Linq to SQL 查询转换为报表所需的 DataTable
。
另请注意,我们实际上并没有将参数传递给 报表查看器 控件。对于 .rdlc 报表,我们只需提供报表的数据。我们必须过滤 Linq 查询才能过滤报表。
设计报表时直接针对 Web 服务方法可能看起来很奇怪,但我们必须在代码隐藏中提供数据。您可以在此站点上找到有关报表查看器控件为何以这种方式设计的原因:http://www.gotreportviewer.com。
Silverlight 项目
在 Silverlight 项目中,我们向 View Model 中添加了 ReportPDFURL
和 ReportViewerURL
属性。我们还添加了一个 SetReportUrls
方法,该方法在支持属性更改时触发
private void SetReportUrls()
{
// Set the Report URLs
ReportViewerURL = String.Format("{0}Gender={1}&AgeHigh={2}&AgeLow={3}&
WeightHigh={4}&WeightLow={5}&ShowReportViewer=True",
GetBaseAddress(), Gender, AgeHigh.ToString(), AgeLow.ToString(),
WeightHigh.ToString(), WeightLow.ToString());
ReportPDFURL = String.Format("{0}Gender={1}&AgeHigh={2}&AgeLow={3}&
WeightHigh={4}&WeightLow={5}&ShowReportViewer=False",
GetBaseAddress(), Gender, AgeHigh.ToString(), AgeLow.ToString(),
WeightHigh.ToString(), WeightLow.ToString());
}
我们创建了以下 Behavior
,它将打开 HTML 弹出窗口
namespace Behaviors
{
[System.ComponentModel.Description("Opens an HTML Window")]
public class HTMLPopupWindow : TargetedTriggerAction<Button>, INotifyPropertyChanged
{
#region PopupURL
public static readonly DependencyProperty PopupURLProperty =
DependencyProperty.Register("PopupURL",
typeof(string), typeof(HTMLPopupWindow), null);
public string PopupURL
{
get
{
return (string)base.GetValue(PopupURLProperty);
}
set
{
base.SetValue(PopupURLProperty, value);
this.NotifyPropertyChanged("PopupURL");
}
}
#endregion
#region PopupWidth
public static readonly DependencyProperty PopupWidthProperty =
DependencyProperty.Register("PopupWidth",
typeof(int), typeof(HTMLPopupWindow), null);
public int PopupWidth
{
get
{
return (int)base.GetValue(PopupWidthProperty);
}
set
{
base.SetValue(PopupWidthProperty, value);
this.NotifyPropertyChanged("PopupWidth");
}
}
#endregion
#region PopupHeight
public static readonly DependencyProperty PopupHeightProperty =
DependencyProperty.Register("PopupHeight",
typeof(int), typeof(HTMLPopupWindow), null);
public int PopupHeight
{
get
{
return (int)base.GetValue(PopupHeightProperty);
}
set
{
base.SetValue(PopupHeightProperty, value);
this.NotifyPropertyChanged("PopupHeight");
}
}
#endregion
protected override void OnAttached()
{
base.OnAttached();
}
protected override void Invoke(object parameter)
{
if (true == HtmlPage.IsPopupWindowAllowed)
{
System.Text.StringBuilder codeToRun = new System.Text.StringBuilder();
codeToRun.Append("window.open(");
codeToRun.Append("\"");
codeToRun.Append(string.Format("{0}", PopupURL));
codeToRun.Append("\",");
codeToRun.Append("\"");
codeToRun.Append("\",");
codeToRun.Append("\"");
codeToRun.Append("width=" + PopupWidth.ToString() +
",height=" + PopupHeight.ToString());
codeToRun.Append(",scrollbars=yes,menubar=no,toolbar=no,resizable=yes");
codeToRun.Append("\");");
try
{
HtmlPage.Window.Eval(codeToRun.ToString());
}
catch
{
MessageBox.Show("You must enable popups to view reports.
Safari browser is not supported.",
"Error", MessageBoxButton.OK);
}
}
else
{
MessageBox.Show("You must enable popups to view reports.
Safari browser is not supported.",
"Error", MessageBoxButton.OK);
}
}
protected override void OnDetaching()
{
base.OnDetaching();
}
// Utility
#region INotifyPropertyChanged
public event PropertyChangedEventHandler PropertyChanged;
private void NotifyPropertyChanged(String info)
{
if (PropertyChanged != null)
{
PropertyChanged(this, new PropertyChangedEventArgs(info));
}
}
#endregion
}
}
接下来,我们向 View 添加按钮。
我们向 按钮添加 HTMLPopupWindow Behavior。
然后,我们将 PopupURL
绑定到 View Model 中的相应属性,并设置其他参数。
实际上并未在 Silverlight 中打印
此实现的关键在于我们实际上并未在 Silverlight "内部" 打印。但是,最终用户很可能不会在意。他们将拥有所需的功能。作为开发人员,此方法应该允许您以合理的价格提供专业的解决方案。
部署
您将需要使用 ASP.NET 4.0,并在显示报表的 Web 服务器上安装 Microsoft Report Viewer 2010 Redistributable Package。
历史
- 2010 年 10 月 24 日:初始发布