7月13日

RDLC报表,ReportViewer

VS .NET开发中,用什么做报表?
可能的回答是Crystal Report,自.NET“紧密”集成Crystal Report后,这可能是开发人员比较单一的选择。但是,这种集成似 乎并不非常紧密,网络上充斥着关于使用Crystal Report的抱怨,太复杂也许是其最为令人诟病的地方,自定义性比较差也不能为程序员们所容忍。
当然,必须承认Crystal Report的功能还是非常强大的,被Business Object收购以后,商业职能的成分也在逐渐增加,也形成了一定规模的用户群。
Visual Studio .NET进入2005版本以后,Crystal Report与IDE的结合更“紧密”了,至少我们看不到那个讨厌的注册窗口了。但是,Microsoft似乎并不容忍在自己的超级工具中竟然没有报表工具,于是Report Viewer Control出现了,我把它的报表称为RDLC报表。
在VS .NET 2005之前,SQL Server Reporting Services中已经提供了一种被称为报表定义语言(Report Definition Language, RDL)的语言;在VS .NET 2005中,Microsoft提供了针对这种报表的设计器,并提供了在WinForm和WebForm中使用这种报表的能力。Microsoft将这种报表的后缀定为RDLC,RDL仍然是Report Definition Language的缩写,那么C代表什么呢?C代表Client-side processing,凸显了它的客户端处理能力。
这种报表的易用性和可定制性让我们完全有理由放弃Crystal Report,让我们来看看它的强大功能吧:
    1、简单易用的控件,特别是Table控件,非常方便字段在报表上的排列;
    2、灵活的可定制性,用XML来描述一个报表相关的一切,不用说了吧?
    3、高度可编程性,在你的项目中,甚至不需要有一个报表文件,通过代码就可以实现报表生成、预览和打印等一系列操作;
    4、支持DrillThrough数据钻取功能;
    5、导出的Excel文件格式非常完美,任何其它报表在这方面都不能与之比拟,而且并不需要安装Excel;
      ……[偷偷懒,其实我并不擅长总结某某的特点,我只能从实际经验中得到一点点结论,而且我也不原意去抄袭帮助中的New Features,呵呵……]
在以后的几篇随笔中,我将结合最近一段时间使用RDLC报表的经验继续探讨相关的一些问题,大致内容包括:报表设计器的使用、LocalReport的一些相关操作,如何自定义纸张等等,欢迎大家提出参考意见。
需要说明的是,现在关于VS. NET 2005中的Report Viewer Control的内容非常少,我只能按照自己的理解来说一些东西,这其中肯定会有一些偏差,欢迎各位的批评指正。另外,我所涉及的内容都是关于LocalReport的,对于ServerReport没有进行研究。
相关的资源:
MSDN的Visual Studio Controls论坛  http://forums.microsoft.com/MSDN/ShowForum.aspx?ForumID=75&SiteID=1
    GotReportViewer   http://www.gotreportviewer.com/ (有几个非常经典的例子,不知为什么最近我无法上这个网站了,呵呵,谁知道请告诉我原因,谢谢!)
晕死,总是见到朋友们评论要GotReportViewer的实例,请在下面的连接中下载: http://files.cnblogs.com/waxdoll/RDLC.rar
在这篇随笔中,我主要分析一下GotReportViewer上的几个经典例子,我们可以从中看到ReportViewer Control的强大功能:
    1、Web Log Analyzer   
    这是一个比较典型的OLAP应用,我们可以看到RDLC报表强大的Chart和Navigation功能。当然了,例子中解析W3C标准日志文件的代码也非常有借鉴意义。这个Starter Kit在我的随笔 http://waxdoll.cnblogs.com/archive/2006/01/19/320280.html中曾经提到过,不再详述。 
    2、子报表 
    展示如何使用子报表显示主记录的详细信息,这种应用很像Access中的子报表功能。主要使用SubreportProcessing事件为子报表提供数据。 
    3、钻取报表 
    钻取报表是通过设置Navigation(HyperLink)和Parameters来实现的,通常在OLAP应用程序中很有用。
   4、具有子报表的钻取报表
    这个例子实现的功能类似Excel中数据透视表(Pivot Table)的功能,在一个复杂的交叉表中可以进行时间和商品两个维度的向下钻取。这在别的报表中恐怕是很难实现的。如图所示, 
5、引用外部代码块
此示例演示从另外一个类Util中读取函数返回值到报表中:首先,使用LocalReport的AddTrustedCodeModuleInCurrentAppDomain方法允许Util类中的方法在Report Viewer中执行,然后在报表中使用TextBox控件的Value节点调用Util中的静态方法Factorial在报表中显示其返回值。
   另外一个相关的示例基本上与此相同,只不过在Util类中访问一个文本文件并将该文本文件中的字符显示在报表中
6、导出到Excel
RDLC报表导出到Excel中的效果非常好,曾经看到另外一个报表设计器(好像就是Crystal Report)导出为Excel文件后的效果非常差,单元格根本不对齐,用户无法在其基础上进行二次操作,而RDLC报表导出的Excel文件就没有这个缺点,而且基本上完全保留了原报表设置的格式。如图所示,

    可以直接使用Report Viewer控件自带的按钮生成Excel文件,也可以使用如下代码来完成操作:
Microsot.Reporting.WinForms.Warning[] Warnings; 
string[] strStreamIds; 
string strMimeType; 
string strEncoding; 
string strFileNameExtension;
byte[] bytes = this.rptViewer.LocalReport.Render("Excel", null, out strMimeType, out strEncoding, out strFileNameExtension, out strStreamIds, out Warnings);
string strFilePath = @"D:\report.xls";
using (System.IO.FileStream fs = new FileStream(strFilePath, FileMode.Create)) 

fs.Write(bytes, 0, bytes.Length); 
}

   对于LocalReport的Render方法,以后的随笔中将进行详细阐述。
   7、交互排序
在RDLC报表的众多交互功能中,这个功能是比较新颖的,终端用户可以通过报表中列标题上的图标进行数据的排序,而预览及打印的效果完全取决于用户的排序。[此处好像有一个Bug,即对数据进行一种排序后的预览效果会保持不变,除非再次开启这个应用程序并重新排序。]而这一切不需要在代码中做任何操作,只需要在报表定义文件中添加<UserSort>节并指定相应的SortExpression和SortExpressionScope。
8、RSS新闻阅读器
用ReportViewer实现RSS新闻阅读器?是的!如图所示,

   当然这个sample是为了展示对Object类型数据源的支持,报表参数ReportParameter的使用也在其中得到体现。
9、允许钻取的主子表
另外一种允许向下钻取的主子报表。如图所示,

   10、从命令行中打印报表
RDLC报表允许用户不通过ReportViewer图形界面直接使用代码控制输出和打印,还是使用LocalReport的Render方法,以后的随笔中将参照这个示例介绍一个如何自定义纸张的方法。
11、票据生成
这个示例允许用户输入一个单据及其明细后直接生成一个可供打印的票据。如图所示,

   12、动态生成一个RDLC文件
这可能是一个最最重要的示例了,RDLC文件是用XML来描述的,可以直接使用代码生成之,这样的报表就可以非常灵活了。像网上比较流行的从DataGridView直接预览、打印数据的程序完全可以用这种方法来替代;进一步引申的话,完全可以实现一个自己的基于RDLC的报表设计器,这样可以让终端用户参与到报表的设计中,至少可以使他们能够修改报表中一些标题、表头等。
GotReportViewer还提供了几个其他的例子,如设置报表参数、通过Email邮寄报表等,由于不是非常典型,不再赘述。
GotReportViwer不知道为什么最近上不去了,有需要这些例程的朋友可以留下Email。
仔细想了一下,我觉得一篇step by step的随笔似乎是不必要的,由于RDLC报表设计时的简易性,任何有报表经验的人都可以在摸索后很容易就掌握其报表的设计方法。本来在这篇随笔中想谈一下对RDLC报表文件的解析,但是MISGoldPrinter的作者flygoldfish(长流支流)已经对这方面进行了详细的总结(见 http://blog.csdn.net/flygoldfish/archive/2005/12/16/554035.aspx),长江支流对报表非常有研究,建议大家到他的Blog上看看,不过我觉得他实现的金质打印通完全可以用RDLC报表中的内容所替代,这只是个人意见,希望以后能见到他的更多作品。
    另外,我手头有一份RDL规范(Report Definition Language Specification),非常值得阅读,推荐给大家 http://files.cnblogs.com/waxdoll/RDLCS.rar。两幅截图:

Matrix

Table
偶然看到的一个关于RDL的评论:
Microsoft   use RDL as the linch pin in its new Reporting Services. Even MS Managers admit that Reporting Services’ success is dependent on adoption of RDL by third party BI ISVs. But Microsoft   has consistently taken markets away from its BI partners and ISVs. Free Reporting Services is just the latest which starts with free OLAP Server, free Analytical/Data Mining Services, and a rash of free client side OLAP to Office 2003 utilities and templates.
Now Microsoft  will argue that its just expanding the BI markets. ISV and BI vendors will have to decide if they want to help Microsoft  “expand the BI markets”. So far Cognos has said it will support RDL at some time in the future; while Business Objects/Crystal says it is considering RDL. Curiously, except for Computerworld, the XML and BI community of publications have been virtually silent about RDL - neither its virtues/weaknesses nor the dilemma it poses to Microsoft’s “BI partners” have made the usually boisterous trade press.
RDLC报表中有一个概念叫数据区域(Data Region),数据区域是数据绑定的报表项目,在数据区域中可以显示来自数据集的多行数据。RDLC报表设计器中的数据区域包含控件面板上的一系列控件:List、Table、Matrix、Chart,如图所示,

        List控件的用处在于这是一个在其中可以自由安排像TextBox、Image等控件;Chart控件用于显示图表,和Excel中的图表比较相似。这篇随笔不会涉及到这两个控件,主要讲一下Table和Matrix两个控件。
先来看Table控件。Table控件有多个部分组成,如标题行(header)、表尾(footer)、数据行(detailed rows)、分组表头(group header)、分组表尾(group footer)等,如图所示,

        之所以设计这样一个控件,我想Microsoft一定是在简化报表的设计:
在这个控件未出现之前,我们看一下一个具有heaer、detailed rows和带统计信息的footer的报表时如何实现的。首先,这个报表需要显示报表页眉、主体和报表页脚三个部分,然后在报表页眉中拖曳进一系列Label形成表格的标题行,接着在主体部分拖曳进一系列TextBox用于显示数据行,并在报表页脚中拖曳进一系列Label或TextBox用于显示统计信息;要命的是,这些Label或TextBox需要在某个方向上进行对齐,控件宽度的调节也非常麻烦,如果需要显示表格线,那么没办法,需要使用Line控件手工画,这是非常麻烦的一件差事,相信没人会喜欢用这么麻烦的方法来设计这么一个简单的报表。
现在,Microsoft推出了Table控件,你需要做的只是确定表格的列数(因为表格的列数需要是固定的),然后将字段从Data Sources面板中拖曳到Table控件的数据行中就可以了,报表设计器会自动为你生成标题行中的标题信息;至于footer中的统计信息Microsoft甚至为我们设计了一个表达式生成器,使用起来非常简便;列宽可以通过拖曳来调整,表格线可以通过设置Table控件的属性来完成。
是的,一切就是这么简单!这也很可能是为什么你在Visual Studio 2005的报表设计器中找不到报表页眉和报表页脚的原因,只有页面页眉(page header)和页面页脚(page footer)就足够了!
当然,可能报表中也需要不是像表格这么整齐排列的数据区域,这时候就需要用到List控件了。
在实际应用中,发现一个表格控件可以具有零个、一个或多个header或footer,甚至可以没有detailed rows,这样的表格控件有什么作用呢?我们知道detailed rows是用来显示多行数据的,而heaer或footer都可以用来显示sum、count等统计信息。假如我们的数据集中同时具有主子表(如通过Inner Join获得的一个查询)的信息,而这时候我们需要将主报表的信息单独显示出来使整个报表形成一个主子表的样式,那么我们就可以用到没有detailed rows的Table控件了,为header或footer中的单元格指定First(Fields!字段名称.Value, "数据源名称")就可以了,这样至少减少了我们排列这些字段信息的烦恼。也就是说,Table控件是非常灵活的。CodeProject上有一篇文章One to Many Reports with VS.NET 2005 (2.0) Report Designer( http://www.codeproject.com/dotnet/1tomanyreports_vsnet2005.asp)即是用这种主子数据集显示的主子报表,但是个人觉得不如上面描述的方法来的简单,顶多给报表增加一个可以标识主记录的参数而已。当然,正儿八经的主子报表还是需要借助SubReport控件来实现。
再来看一下Matrix控件,这个控件可以看作是Microsoft的又一个创新,以前的报表中可能会有交叉表(crosstab),但Matrix控件反映的不只是一个交叉表,还可以看作是一个带钻取功能的数据透视表(pivot table)。Matrix控件由以下部分组成:

 
如果由我们自己使用一个普通的数据集来绘制一个crosstab,那会是一个非常麻烦的工作,我们需要:为数据透视报表设置行标题;计算可能的列标题数量并设置列标题,根据行标题和列标题在数据集中循环查询由当前行标题和列标题决定的值,整个过程的计算量就够受的了。作为被Microsoft封装过的一个控件,Matrix控件显然不需要这么麻烦,简单的拖曳操作并设置其属性就可以了。当然,在报表中使用交叉表最重要的一点是最终显示的结果必须是有意义的。
需要注意的是,当包含Matrix控件的报表导出到Excel文件中以后,即使是未显示的带有钻取标志的区域也将被显示出来,可能你会有这样的疑问:既然是数据透视表,为什么在Excel中不能显示成折叠的样式呢?这是因为报表的导出功能是并不依赖于Excel的。

        随着Visual Studio 2005中文版的推出,Microsoft汉化了MSDN的大部分内容,开发者再也不用啃英文了,本来想介绍一下LocalReport的Render方法,现在您可以到 http://msdn2.microsoft.com/zh-cn/library/ms252207(VS.80).aspx获得关于这部分的详细信息。之所以以前想介绍这个方法,是因为我将想大家介绍一种在Crystal Report中无法实现的自定义票据打印纸张的方法。Anyway,现在我直接向大家介绍这种方法,可能这种方法并不是很好的,但是确实是我经过一段时间的摸索总结出来的。萝卜( http://luobos.cnblogs.com)曾经提到过的变通的方法不知道是不是我要介绍的这一种,欢迎和我进行交流!
要想使用RDLC报表并进行页面设置,我们先来看一下LocalReport是否有类似PageSettings的类、属性、方法或事件等,我仔细找了一下,发现Microsoft.Reporting.WinForms.ReportPageSettings类具有PaperSize属性和Margin属性,但可惜的是它们都是只读的,对我们来说没有意义;另外,LocalReport具有GetDefaultPageSettings()方法,这也只能是获取当前报表的页面设置。没办法,只能采用变通的方法了。在.NET中如果想使用自定义纸张,最好的方法莫过于使用System.Drawing.Printing.PrintDocument类了,还记得我在前面提到的一个GotReportViewer的例子吗?
private int m_currentPageIndex;
    private IList<Stream> m_streams;
private Stream CreateStream(string name, string fileNameExtension, Encoding encoding, string mimeType, bool willSeek)
     {
        Stream stream = new FileStream(name + "." + fileNameExtension, FileMode.Create);
        m_streams.Add(stream);
        return stream;
    }
private void Export(LocalReport report)
     {
        string deviceInfo =
          "<DeviceInfo>" +
          "  <OutputFormat>EMF</OutputFormat>" +
          "  <PageWidth>8.5in</PageWidth>" +
          "  <PageHeight>11in</PageHeight>" +
          "  <MarginTop>0.25in</MarginTop>" +
          "  <MarginLeft>0.25in</MarginLeft>" +
          "  <MarginRight>0.25in</MarginRight>" +
          "  <MarginBottom>0.25in</MarginBottom>" +
          "</DeviceInfo>";
        Warning[] warnings;
        m_streams = new List<Stream>();
        report.Render("Image", deviceInfo, CreateStream, out warnings);
foreach (Stream stream in m_streams)
            stream.Position = 0;
    }
private void PrintPage(object sender, PrintPageEventArgs ev)
     {
        Metafile pageImage = new Metafile(m_streams[m_currentPageIndex]);
        ev.Graphics.DrawImage(pageImage, ev.PageBounds);
m_currentPageIndex++;
        ev.HasMorePages = (m_currentPageIndex < m_streams.Count);
    }
private void Print()
     {
        const string printerName = "Microsoft Office Document Image Writer";
if (m_streams == null || m_streams.Count == 0)
            return;
PrintDocument printDoc = new PrintDocument();
        printDoc.PrinterSettings.PrinterName = printerName;
        if (!printDoc.PrinterSettings.IsValid)
         {
            string msg = String.Format("Can't find printer \" {0}\".", printerName);
            Console.WriteLine(msg);
            return;
        }
        printDoc.PrintPage += new PrintPageEventHandler(PrintPage);
        printDoc.Print();
    }
private void Run()
     {
        LocalReport report = new LocalReport();
        report.ReportPath = "Report.rdlc";
        report.DataSources.Add(new ReportDataSource("Sales", LoadSalesData()));
Export(report);
m_currentPageIndex = 0;
        Print();
    }
对,就是那个通过命令行而不是ReportViewer的GUI界面进行打印报表的例子,这个例子就使用LocalReport的Render方法将报表的内容导出为EMF图像流,然后在PrintDocument的PrintPage事件中使用时事件参数System.Drawing.Printing.PrintEventArgs类的DrawImage方法将EMF图像流输出到打印机。我在上面说的变通的方法也要使用这种方法。具体的细节将在以后的随笔中陆续给出。
既然我们使用这种方法进行报表的打印,那么Visual Studio的控件ReportViewer的工具栏就不再符合我们的要求了。因为这个报表浏览器的工具栏上的按钮虽然可以设置属性显示或隐藏其中的一部分,但是我们却不能自己往这个工具栏上添加按钮(显然,我们需要实现自己的页面设置、预览和打印按钮),在这一点上,建议Microsoft将工具栏和报表浏览器分离,应该做得和BindingNavigator那样就好了。
我们先设置ReportViewer控件的ShowToolBar方法为false,然后在ReportViewer控件纸上添加除页面设置、预览、打印外的应该有的按钮,像刷新、终止、导出、缩放、搜索、导航等,这些按钮的Click事件定义如下:
/** <summary>
        /// 获取当前时间组成的字符串,用作生成不会重复的文件名
        /// </summary>
        /// <returns></returns>
        private string GetTimeStamp()
         {
            string strRet = string.Empty;
            System.DateTime dtNow = Pub.DateTimeEx.ServerTime;
            strRet +=   dtNow.Year.ToString() +
                        dtNow.Month.ToString("00") +
                        dtNow.Day.ToString("00") +
                        dtNow.Hour.ToString("00") +
                        dtNow.Minute.ToString("00") +
                        dtNow.Second.ToString("00") +
                        System.DateTime.Now.Millisecond.ToString("000");
            return strRet;
}
/** <summary>
        /// 导出到Excel
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private void toolExcel_Click(object sender, EventArgs e)
         {
Microsoft.Reporting.WinForms.Warning[] Warnings;
            string[] strStreamIds;
            string strMimeType;
            string strEncoding;
            string strFileNameExtension;
byte[] bytes = this.rptViewer.LocalReport.Render("Excel", null, out strMimeType, out strEncoding, out strFileNameExtension, out strStreamIds, out Warnings);
string strFilePath = @"D:\" + this.GetTimeStamp() + ".xls";
using (System.IO.FileStream fs = new FileStream(strFilePath, FileMode.Create))
             {
                fs.Write(bytes, 0, bytes.Length);
            }
if (Pub.WinForm.Msg.Question("报表打印: \r\n    成功导出Excel文件!" + strFilePath + "\r\n    要现在打开文件" + strFilePath + "吗?") == DialogResult.Yes)
             {
                System.Diagnostics.Process.Start(strFilePath);
            }
}
/** <summary>
        /// 刷新报表数据
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private void tool刷新_Click(object sender, EventArgs e)
         {
            this.rptViewer.RefreshReport();
        }
/** <summary>
        /// 在加载报表数据时终止报表数据的加载
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private void tool终止_Click(object sender, EventArgs e)
         {
            this.rptViewer.CancelRendering(0);
        }
/** <summary>
        /// 从DrillThrough报表返回到导航页面
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private void tool返回_Click(object sender, EventArgs e)
         {
            if (this.rptViewer.LocalReport.IsDrillthroughReport)
                this.rptViewer.PerformBack();
        }
/** <summary>
        /// 回到报表的第一页
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private void tool第一页_Click(object sender, EventArgs e)
         {
            this.rptViewer.CurrentPage = 1;
        }
/** <summary>
        /// 跳转到报表的最后一页
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private void tool最后一页_Click(object sender, EventArgs e)
         {
            this.rptViewer.CurrentPage = this.rptViewer.LocalReport.GetTotalPages();
        }
/** <summary>
        /// 以25%的比例显示报表
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private void tool25_Click(object sender, EventArgs e)
         {
            this.rptViewer.ZoomMode = ZoomMode.Percent;
            this.rptViewer.ZoomPercent = 25;
        }
/** <summary>
        /// 以50%的比例显示报表
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private void tool50_Click(object sender, EventArgs e)
         {
            this.rptViewer.ZoomMode = ZoomMode.Percent;
            this.rptViewer.ZoomPercent = 50;
        }
/** <summary>
        /// 以100%的比例显示报表
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private void tool100_Click(object sender, EventArgs e)
         {
            this.rptViewer.ZoomMode = ZoomMode.Percent;
            this.rptViewer.ZoomPercent = 100;
        }
/** <summary>
        /// 以200%的比例显示报表
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private void tool200_Click(object sender, EventArgs e)
         {
            this.rptViewer.ZoomMode = ZoomMode.Percent;
            this.rptViewer.ZoomPercent = 200;
        }
/** <summary>
        /// 以400%的比例显示报表
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private void tool400_Click(object sender, EventArgs e)
         {
            this.rptViewer.ZoomMode = ZoomMode.Percent;
            this.rptViewer.ZoomPercent = 400;
        }
/** <summary>
        /// 将缩放模式设置为整页
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private void tool整页_Click(object sender, EventArgs e)
         {
            this.rptViewer.ZoomMode = ZoomMode.FullPage;
        }
/** <summary>
        /// 将缩放模式设置为页宽
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private void tool页宽_Click(object sender, EventArgs e)
         {
            this.rptViewer.ZoomMode = ZoomMode.PageWidth;
        }
/** <summary>
        /// 在报表中搜索txtSearch中的字符
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private void tool搜索_Click(object sender, EventArgs e)
         {
            if (this.txtSearch.Text.Trim() == string.Empty)
                return;
this.rptViewer.Find(this.txtSearch.Text.Trim(), 1);
        }
/** <summary>
        /// 搜索报表中下一处txtSearch中的字符
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private void tool搜索下一个_Click(object sender, EventArgs e)
         {
            if (this.txtSearch.Text.Trim() == string.Empty)
                return;
this.rptViewer.FindNext();
        }
/** <summary>
        /// 跳转到上一页
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private void tool上一页_Click(object sender, EventArgs e)
         {
            if (this.rptViewer.CurrentPage != 1)
                this.rptViewer.CurrentPage--;
        }
/** <summary>
        /// 跳转到下一页
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private void tool下一页_Click(object sender, EventArgs e)
         {
            if (this.rptViewer.CurrentPage != this.rptViewer.LocalReport.GetTotalPages())
                this.rptViewer.CurrentPage++;
        }
/** <summary>
        /// 跳转到由txt跳转中指定的页数
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private void tool跳转_Click(object sender, EventArgs e)
         {
            if (this.txt跳转.Text.Trim() == string.Empty)
                return;
int intJump = 0;
if (System.Int32.TryParse(this.txt跳转.Text.Trim(), out intJump))
                if (intJump <= this.rptViewer.LocalReport.GetTotalPages())
                    this.rptViewer.CurrentPage = intJump;
}

转载于:https://www.cnblogs.com/Alum/archive/2010/04/20/3587985.html

7月13日RDLC报表,ReportViewer使用详解相关推荐

  1. 绝地求生服务器维护啥意思,2019绝地求生7月10日正式服维护内容详解介绍

    2019绝地求生7月10日正式服维护内容详解介绍 2019-07-10 09:13:52来源:pubg官方微博编辑:君辞-v-评论(0) 2019绝地求生7月10日正式服维护更新了什么内容?游戏在今天 ...

  2. 与 Scott Guthrie 一道感受技术激情 1月13日于北京

    可能很多朋友已经知道了这个消息,我觉得还是写一下,别让这个机会白白溜走.Scott Guthrie是谁,我就不介绍了,简单说:ASP.NET之父,Silverlight 的主要创始人,还管着太多微软的 ...

  3. 参与2011年7月13日举行的Azure国际猜拳锦标赛,赢取5,000美元大奖

    你想要编写自己的"bot"角色并测试你的技能,在线同来自美国,加拿大,中国,德国,新西兰,瑞典和英国的Windows Azure开发者一教高下,并赢取5,000美元大奖吗?请先注册 ...

  4. 2008年12月13日上海USB驱动开发深度解析讲座PPT

    讲座PPT:宋宝华2008年12月13日上海USB驱动开发深度解析讲座PPT [url]http://www.linuxdriver.cn/200812/20081213172619_836.rar[ ...

  5. PANDAS 数据合并与重塑(concat篇) 原创 2016年09月13日 19:26:30 47784 pandas作者Wes McKinney 在【PYTHON FOR DATA ANALYS

    PANDAS 数据合并与重塑(concat篇) 原创 2016年09月13日 19:26:30 标签: 47784 编辑 删除 pandas作者Wes McKinney 在[PYTHON FOR DA ...

  6. 中科院计算所开源Easy Machine Learning:让机器学习应用开发简单快捷 By 机器之心2017年6月13日 13:05 今日,中科院计算所研究员徐君在微博上宣布「中科院计算所开源了

    中科院计算所开源Easy Machine Learning:让机器学习应用开发简单快捷 By 机器之心2017年6月13日 13:05 今日,中科院计算所研究员徐君在微博上宣布「中科院计算所开源了 E ...

  7. 开博首发2017年1月13日开博大吉

    开博首发2017年1月13日开博大吉 转载于:https://blog.51cto.com/wangheyu1/1891673

  8. 龙剑服务器为什么总是维修,《龙剑》2014年3月13日更新维护公告

    亲爱的<龙剑>技术测试玩家: 为了保证服务器的稳定和服务质量,本周例行维护将于2014年3月13日上午8:00停机,维护时间为上午8点至11点左右.如果在维护期间无法完成维护内容,开机时间 ...

  9. 【参会指南】神策 2020 数据驱动用户大会,10 月 13 日将重磅开幕!

    10 月 13-14 日,主题为「数字化·正当潮」的 2020 数据驱动用户大会,将在北京香格里拉如约举行. 「数据驱动用户大会」是由神策数据创办的年度盛会,已连续举办五年,致力于为数据爱好者打造一个 ...

最新文章

  1. IT规划宜分步走 忌盲目好大喜功(载)
  2. API 分页探讨:offset 来分页真的有效率吗?
  3. 数据结构读书笔记(三)(C语言)
  4. 扫描路径_SolidWorks沿路径扭转扫描的线缆实例
  5. 人工神经网络——笔记摘抄1
  6. 为Kindeditor控件添加图片自动上传功能
  7. easy ui dialog 关闭之后的怪异问题
  8. Windows Phone 7 自适应键盘输入
  9. ajax 连接java,如何使用Ajax连接到Java servlets?
  10. 要么出色,要么出局!威马公布三大核心技术矩阵背后的大野心
  11. sga_target大于sga_max_size数据库无法启动
  12. redis与mysql性能对比、redis缓存穿透、缓存雪崩
  13. win8下IE10停止工作解决办法
  14. php输出一个直角三角形,php hypot()直角三角形斜边长度 is_infinite()是否为无限值...
  15. Vue + MathLive 实现数学公式可编辑
  16. c语言调用calculate函数,关于c语言中int calculate函数求解。谢谢
  17. 网络层次模型及各层对应协议
  18. Zabbix的SNMPTrap监控配置
  19. FreeCAD-0.19源码编译教程
  20. linux 操作系统笔记基础命令

热门文章

  1. [PHP初级]手把手教你写注册程序 2
  2. Linux-高级IO之poll、epoll
  3. 英语发音之音标1---掐头法(a和其他6个辅音字母总共7个字母发音)
  4. 2023天津理工大学计算机考研信息汇总
  5. 软件测试工程师又一大挑战:大数据测试
  6. CmsEasy 模板均放置在template文件夹
  7. 随机过程--Metropolis-Hastings算法
  8. IP包头ARP协议笔记
  9. DNS初体验之完美邂逅
  10. 青蛙跳台问题的递归实现(c语言)