65.9K
CodeProject 正在变化。 阅读更多。
Home

为 Report Viewer 本地报告动态生成 RDLC

starIconstarIconstarIconstarIconstarIcon

5.00/5 (30投票s)

2014年1月2日

CPOL

14分钟阅读

viewsIcon

174100

downloadIcon

7105

本文介绍如何动态创建 RDLC。

 

介绍 

您的报表是否包含大量变化,以至于您认为在设计时静态设计所有报表是不可行的?那么,本文可能有助于您动态生成报表——即,在设计时而不是在设计时设计*.rdlc文件。上面的截图是我为本文动态创建的一个示例报表——一个包含超链接和嵌入图像的元素周期表。

您可以下载使用 C# 编写并使用 Visual Studio 2013 开发的源代码。

背景

在一个最近的软件项目中,我遇到了一个需求,需要根据用户确定的条件报告多达 100 张表和图表。在某些情况下,用户希望同时显示表 1、3、54 和 88,以及图表 12、13 和 55。在其他情况下,报表应显示表 2 和 4,但没有图表,等等。如果不是无限多,那么组合的数量是巨大的。

我在 Google 上搜索了有关动态生成 RDLC 的文章,但似乎信息不多。微软在 http://technet.microsoft.com/en-us/library/ms170239(v=sql.105).aspx 中提供的内容过于粗糙,无法作为我生成自己的特定报表的基础。简而言之,GenerateRdl() 函数只是糟糕的、单体的代码,没有任何结构,并且使用大量的字符串文字。

因此,我着手创建了一组类,以便我可以编写类似这样的代码——如果用户想要表 1、3、54 和 88,那么我将使用 C# 的控制流语句(如“if”和“switch”)将这些表添加到报表中。

Using the Code

现在,让我们看一下显示填充了元素周期表的报表生成器的 Form 类代码

namespace PeriodicTable
{
    using System.Windows.Forms;
    using Microsoft.Reporting.WinForms;
    public partial class Form1 : Form
    {
        public Form1()
        {
            this.InitializeComponent();
        }
        private void Form1_Load(object sender, System.EventArgs e)
        {
            var rv = new ReportViewer { Dock = DockStyle.Fill };
            this.Controls.Add(rv);
            new PeriodicTableReportGenerator(rv.LocalReport).Run();
            rv.RefreshReport();
        }
    }
}

Form1_Load() 函数实例化一个 ReportViewer,将其放入 Controls,将其 LocalReport 传递给 PeriodicTableReportGenerator 构造函数,然后调用 Run() 方法。PeriodicTableReportGenerator 动态生成 RDLC 并设置 LocalReport。最后,Form1 调用 RefreshReport() 来显示元素周期表。

下面是 PeriodicTableReportGenerator 的代码

namespace PeriodicTable
{
    using Microsoft.Reporting.WinForms;
    using PeriodicTable.Properties;
    using PeriodicTable.Report.Rdl;
    public class PeriodicTableReportGenerator : ReportGenerator
    {
        public PeriodicTableReportGenerator(LocalReport localReport)
            : base(localReport)
        {
        }
        public override void Run()
        {
            var dataSetForMainTable = CreateDataSet();
            var mainTable = MainTable.Create(dataSetForMainTable.Name);
            var dataSetForSubTable = CreateDataSet();
            var subTable = SubTable.Create(dataSetForSubTable.Name);
            var body = new Body();
            body.AddReportItem(mainTable);
            body.AddReportItem(subTable);
            this.Report.AddReportSection(new ReportSection(body));
            
            this.Report.AddDataSet(dataSetForMainTable);
            this.Report.AddDataSet(dataSetForSubTable);
            this.DataSources.Add(new ReportDataSource(dataSetForMainTable.Name,
                MainTableDataSource.Create()));
            this.DataSources.Add(new ReportDataSource(dataSetForSubTable.Name,
                SubTableDataSource.Create()));
            this.CreateEmbeddedImages();
            base.Run();
        }
        private static DataSet CreateDataSet()
        {
            var dataSet = new DataSet();
            dataSet.AddField(ElementProperty.Group);
            dataSet.AddField(ElementProperty.Period);
            dataSet.AddField(ElementProperty.Name);
            dataSet.AddField(ElementProperty.AtomicNumber);
            dataSet.AddField(ElementProperty.ChemicalSymbol);
            dataSet.AddField(ElementProperty.RelativeAtomicMass);
            dataSet.AddField(ElementProperty.MassUncertainty);
            dataSet.AddField(ElementProperty.ChemicalProperty);
            dataSet.AddField(ElementProperty.NaturalOccurrence);
            dataSet.AddField(ElementProperty.CountryOfDiscovery);
            dataSet.AddField(ElementProperty.State);
            dataSet.AddField(ElementProperty.Url);
            return dataSet;
        }
        private void CreateEmbeddedImages()
        {
            this.Report.AddEmbeddedImage(
                new EmbeddedImage(Resources.NoBackground, 
                CountryOfDiscovery.Undiscovered.ToString()));
            this.Report.AddEmbeddedImage(
                new EmbeddedImage(Resources.Aristotle, 
                CountryOfDiscovery.KnownToAncients.ToString()));
            …
        }
    }
}

Run() 方法创建两个 DataSet - 一个用于显示主元素周期表(不包含镧系和锕系)的 MainTable,另一个用于显示主元素周期表下方(正常情况)的镧系和锕系的 SubTable。这个 DataSet 仅定义字符串形式的数据字段名称,与 ADO.NET 中的同名类完全不同。因为我不想使用字符串文字来表示集合中的属性,所以我求助于反射来获取它们。ElementProperty 类就是这样做的。

主表的 RDLC 是通过调用 MainTable.Create() 静态方法创建的,该方法返回一个 ReportItem。第二个表以同样的方式创建。

然后,它实例化一个 Body 并将生成的主表和子表添加到其中。在这里,如果用户不想看到镧系和锕系,我就可以跳过添加子表。这就是我的项目想要的自由。body 被包含在一个 ReportSection 中,然后被添加到在其基类 - ReportGenerator 中实例化的 Report 中。

接下来,我们将两个 DataSet 添加到 Report。然后我们生成数据本身——即属于主表和镧系和锕系的物理元素数据。我们为每个表分配数据集名称,并将数据源添加到 DataSources。正如您在截图中看到的,元素周期表包含一个变暗的国家国旗,代表发现该元素的科学家或科学家团队的国家。因此,我们也创建图像并将它们添加到 Report 中。

最后,Run() 函数调用其 base.Run() 方法。就是这样。

报表设计

当然,静态设计同一个报表并不难。下图显示了元素周期表的設計,Visual Studio 的報表設計師將其渲染

 

如您所见,有两张表——左上角是主表的表,右下角是第二张表。请注意,即使元素周期表共有 18 列,设计界面中的表也没有 18 列。相反,它只有一列。同样,即使元素周期表共有 7 行,设计中也只有一行。动态添加列和行(由数据驱动)是报表引擎的工作。因此,在设计报表时,我们最重要的任务之一是理解“什么”应该在列和行方向上重复。一旦我们理解了什么在重复,我们就可以识别确定数据分组在一起的所谓“分组表达式”。

在 RDLC 中,表称为 Tablix——这是 table 和 matrix 合并而成的合成词。仅供参考,我包含了一个 Report1.rdlc 文件,我使用报表设计器静态设计了它,以制作相同的元素周期表。您可以通过在 Program.cs 中更改启动窗体从 Form1 到 Form2 来比较动态和静态 RDLC 生成的结果。

仅供您参考,Form2 的加载事件如下所示

private void Form2_Load(object sender, EventArgs e)
{
    var rv = new ReportViewer { Dock = DockStyle.Fill };
    rv.LocalReport.ReportEmbeddedResource = "PeriodicTable.Report.Report1.rdlc";
    this.Controls.Add(rv);
    rv.LocalReport.DataSources.Add(
        new ReportDataSource("DataSet1", MainTableDataSource.Create()));
    rv.LocalReport.DataSources.Add(
        new ReportDataSource("DataSet2", SubTableDataSource.Create()));
    rv.RefreshReport();
}

静态设计的 RDLC 文件被分配给 LocalReport.ReportEmbeddedResource。完全相同的数据源被添加到报表查看器。

我们的 PeriodicTableReportGenerator 动态生成了相同的 RDLC。

动态创建 Tablix

现在,在深入了解 RDLC 的海洋之前,我们需要熟悉一些术语。表的左上角称为 TablixCorner。在主元素周期表中,我在单元格的 Textbox 中显示字符串常量“Groupà”和“âPeriod”。

右上角代表表的列层次结构,称为 TablixColumnHierarchy,我们在这里放置一个分组表达式,以便可以水平添加列(由数据驱动),此外还可以显示组编号文本字符串。在主表中,一列代表元素的“组”,范围从 1 到 18。因为组号必须动态更改,所以我不能像在 TablixCorner 中那样使用字符串常量。相反,我们必须使用“表达式”,以便报表引擎可以动态评估它。最简单的表达式就是 DataSet 的一个字段。这正是本单元格的情况,设计界面显示单元格值为“[Group]”,这意味着报表引擎从数据中获取“Group”字段并动态将其转换为字符串。最简单的形式的表达式由报表设计器以方括号括起来的字段名表示。

左下角代表表的行层次结构,称为 TablixRowHierarchy,我们在其中指定另一个分组表达式,以便可以垂直添加行(由数据驱动),此外还可以显示周期编号文本字符串。同样,文本是最简单的表达式——“[Period]”。

右下角——由酒红色包围的整个矩形——称为 TablixBody,它是元素周期表的精华,代表每个元素的详细信息。在设计时,我尝试模仿维基百科的一个——http://en.wikipedia.org/wiki/Periodic_table_(large_version),包括单元格背景颜色和边框样式。我还添加了背景图像来显示原产国。

现在,与主表 Tablix 中的其他三个单元格不同,我们必须在 TablixBody 单元格中放入更多信息——原子序数、化学符号、带超链接的元素名称以及相对原子质量(可能带有不确定性)。因此,我没有放入单个 Textbox,而是在单元格中放入了一个 Rectangle,其中包含另一个 Tablix,它本身有四行。

最后,我们准备查看生成主表的 Tablix 的代码

namespace PeriodicTable
{
    using PeriodicTable.Report.Rdl;
    public class MainTable
    {
        public static ReportItem Create(string dataSetName)
        {
            return new Tablix(
                CreateTablixCorner(),
                CreateTablixColumnHierarchy(),
                CreateTablixRowHierarchy(),
                CreateTablixBody(dataSetName),
                dataSetName);
        }
        private static TablixCorner CreateTablixCorner()
        {
            var textRuns1 = new TextRuns(new TextRun
                { Value = "Group", FontWeight = FontWeight.Bold });
            textRuns1.Add(new TextRun { Value = "", FontFamily = "Wingdings" });
            var textRuns2 = new TextRuns(new TextRun
                { Value = "", FontFamily = "Wingdings" });
            textRuns2.Add(new TextRun
                { Value = "Period", FontWeight = FontWeight.Bold });
            var textbox = new Textbox(new Paragraph(textRuns1))
                { TextboxStyle = new TextboxStyle() };
            textbox.AddParagraph(new Paragraph(textRuns2));
            return
                new TablixCorner(
                    new TablixCornerRows(
                        new TablixCornerRow(
                            new TablixCornerCell(new CellContents(textbox)))));
        }
        private static TablixColumnHierarchy CreateTablixColumnHierarchy()
        {
            var group =
                new Group(
                    new GroupExpressions(
                        new GroupExpression(
                            "=" + Expression.FieldsValue(ElementProperty.Group))));
            
            var sortExpression =
                new SortExpression(new Value(
                    "=" + Expression.FieldsValue(ElementProperty.Group)));
            var sortExpressions = new SortExpressions(sortExpression);
            
            var textRun = new TextRun
                {
                    Value = "=" + Expression.FieldsValue(ElementProperty.Group),
                                  FontWeight = FontWeight.Bold
                };
            var paragraph = new Paragraph(new TextRuns(textRun))
                { TextAlign = TextAlign.Center };
            var textbox = new Textbox(paragraph)
                { TextboxStyle = new TextboxStyle() };
            var header = new TablixHeader(new Inch(0.4), new CellContents(textbox));
            return new TablixColumnHierarchy(
                new TablixMembers(
                    new TablixMember(group, sortExpressions, header)));
        }
        private static TablixRowHierarchy CreateTablixRowHierarchy()
        {
            var group =
                new Group(
                    new GroupExpressions(
                        new GroupExpression(
                            "=" + Expression.FieldsValue(ElementProperty.Period))));
            
            var sortExpression =
                new SortExpression(
                    new Value(
                        "=" + Expression.FieldsValue(ElementProperty.Period)));
            var sortExpressions = new SortExpressions(sortExpression);
            var textRun = new TextRun
                {
                    Value = "=" + Expression.FieldsValue(ElementProperty.Period),
                                  FontWeight = FontWeight.Bold
                };
            var paragraph = new Paragraph(new TextRuns(textRun))
                { TextAlign = TextAlign.Center };
            var textbox = new Textbox(paragraph)
                {
                     TextboxStyle = new TextboxStyle
                         { VerticalAlign = VerticalAlign.Middle }
                };
            var header = new TablixHeader(new Inch(0.7), new CellContents(textbox));
            return new TablixRowHierarchy(
                new TablixMembers(new TablixMember(group, sortExpressions, header)));
        }
        private static TablixBody CreateTablixBody(string dataSetName)
        {
            var rectangle = new Rectangle();
            rectangle.AddReportItem(ElementCell.Create(dataSetName));
            var tablixColumns = new TablixColumns(
                new TablixColumn(new Width(new Inch(1.05))));
            var tablixCells = new TablixCells(
                new TablixCell(new CellContents(rectangle)));
            var tablixRows = new TablixRows(
                new TablixRow(new Inch(0.85), tablixCells));
            return new TablixBody(tablixColumns, tablixRows);
        }
    }
}

静态的 Create() 方法只是实例化一个 Tablix 并将其作为 ReportItem 返回。正如您可能已经想象到的,Tablix 是一个 ReportItem。正如我在上一节中解释的,一个 Tablix 由四部分组成。它还需要知道它将要使用的数据集名称。因此,构造函数有五个参数。

创建 TablixCorner

CreateTablixCorner() 方法创建一个 TablixCorner,我必须在 Textbox 中添加文本常量——“Groupà”和“âPeriod”,用换行符分隔。TextRun 代表一个具有相同字体参数的“文本运行”。报表查看器的默认字体是 Arial,10pt 字号。如果您不指定 TextRun 中的字体参数,它将使用默认字体。因为我想将“Group”显示为粗体,所以我像下面这样指定了它

var textRuns1 = new TextRuns(
    new TextRun { Value = "Group", FontWeight = FontWeight.Bold });

这就是指定文本运行并将其添加到 TextRuns(它是 TextRun 的集合)的方式。

现在,左箭头字符是 Windings 字体。因此,我需要创建另一个 TextRun 并将其添加到同一个 TextRuns

textRuns1.Add(new TextRun { Value = "", FontFamily = "Wingdings" });

接下来,我必须在同一个文本框中添加“âPeriod”,但要换行。这意味着我需要两个 Paragraph——一个带有“Groupà”文本运行,另一个带有“âPeriod”文本运行。然后,Textbox 将完成

var textbox = new Textbox(new Paragraph(textRuns1));
textbox.AddParagraph(new Paragraph(textRuns2));

最后,该函数使用 Textbox 作为其 CellContents 来实例化一个 TablixCorner 并返回它。

创建 TablixColumnHierarchy

CreateTablixColumnHierarchy() 方法创建一个 TablixColumnHierarchy,我必须在那里添加分组和排序表达式以及代表元素“组”的文本。如果您对“组”这两个术语的两种用法感到困惑,那是因为我选择了一个在业务领域中具有相同术语的示例。这两个表达式的创建方式相似

var group = new Group(
    new GroupExpressions(
        new GroupExpression("=" + Expression.FieldsValue(ElementProperty.Group))));
            
var sortExpression = new SortExpression(
    new Value("=" + Expression.FieldsValue(ElementProperty.Group)));
var sortExpressions = new SortExpressions(sortExpression);

我们使用每个元素的 Group 属性对每个元素进行分组——即,Group 1 元素、Group 2 元素,直到 Group 18 元素。排序表达式也是组号,因为我们希望按升序排列元素。如果我们不添加排序表达式,那么报表查看器将按照数据源中出现的顺序排列元素。

现在,让我们谈谈表达式。RDLC 中的表达式始终以等号开头。正如我之前提到的,最简单的表达式是数据集的字段值,应该像这样

=Fields!AtomicNumber.Value

您可以使用字符串文字这样放置一个表达式

var group = new Group(
    new GroupExpressions(new GroupExpression("=Fields!Group.Value")));

但我不想这样做,因为我总是会打字错误,并且字符串文字无法由编译器检查。如果报表查看器发现提供的 RDLC 有任何错误,当调用其 RefreshReport() 方法时,它会在屏幕上显示一条错误消息。

 

当您看到其中一条时,您将不得不调试您的 RDLC 生成。我在 ReportGenerator 类的 Run() 方法中添加了一个调试语句

public virtual void Run()
{
    ////this.Report.Element.Save(Console.Out);
    this.LoadReportDefinition();
}

通过取消注释第一条语句,您可以在调试模式下在 Visual Studio 的输出窗口中看到整个 RDLC。 

最后,我们创建一个 TablixHeader,我们在其中放置也 是表达式的行标题文本

var textRun = new TextRun
    {
        Value = "=" + Expression.FieldsValue(ElementProperty.Group),
        FontWeight = FontWeight.Bold
    };
var paragraph = new Paragraph(new TextRuns(textRun))
    { TextAlign = TextAlign.Center };
var textbox = new Textbox(paragraph)
    { TextboxStyle = new TextboxStyle() };
var header = new TablixHeader(new Inch(0.4), new CellContents(textbox));

请注意,TextAlignParagraph 的一个属性。我添加了 TextAlign.Center 以便在单元格中心显示组号。构造函数的第一个参数表示标题行的高度(以英寸为单位)。

创建 TablixRowHierarchy

CreateTablixRowHierarchy() 方法创建一个 TablixRowHierarchy,我必须在那里添加分组和排序表达式以及代表元素“周期”的文本。TablixRowHierarchyTablixColumnHierarchy 非常相似。请注意,VerticalAlignTextbox 的一个属性。因为我想在行标题单元格的中间显示周期号,所以我为 Textbox 设置了 VerticalAlign.Middle

var textbox = new Textbox(paragraph)
    {
        TextboxStyle = new TextboxStyle { VerticalAlign = VerticalAlign.Middle }
    };
var header = new TablixHeader(new Inch(0.7), new CellContents(textbox));

构造函数的第一个参数表示标题列的宽度(以英寸为单位)

var header = new TablixHeader(new Inch(0.7), new CellContents(textbox));

创建 TablixBody

最后,我们创建 TablixBody。正如我所说,它是元素周期表的精华。但该方法出乎意料地简短。这是因为我们将 body 单元格内容的创建委托给了另一个类——ElementCell。我们只是将创建的 ReportItem 添加到一个 Rectangle 中,然后完成 TablixBody 的创建。我们在 TablixColumn 中指定 body 单元格的宽度,在 TablixRow 中指定高度。

请注意,您可以将创建的 ReportItem 直接放入 TablixCellCellContents 中,而无需先将其包含在 Rectangle 中。但如果您这样做,元素单元格之间将没有边距,如下所示

 

通过添加一个 Rectangle 并在类的构造函数中指定 TopLeft 属性,我们可以添加 0.025 英寸的边距

public class ElementCell
{
    public static ReportItem Create(string dataSetName)
    {
        return new Tablix(
            CreateTablixColumnHierarchy(),
            CreateTablixRowHierarchy(),
            CreateTablixBody(),
            dataSetName)
        {
            Top = new Inch(0.025),
            Left = new Inch(0.025),
            Style = CreateTablixStyle()
        };
    }
…

创建第二个表的 RDLC

镧系和锕系的第二个表与主表非常相似。唯一有趣的是列层次结构的组表达式;我们不能使用最简单的“group”表达式,因为所有镧系和锕系都共享相同的组号,即 3。因此,我们需要想出一些可以用作“组”的东西。我注意到以下事实;如果我从所有镧系元素的原子序数中减去镧的原子序数,并从所有锕系元素的原子序数中减去锕的原子序数,那么我将得到一个新的“组”号,对于镧系和锕系都从零开始。这是通过使用以下表达式完成的

private static string CreateColumnGroupExpression()
{
    const int LanthanumAtomicNumber = 57;
    const int ActiniumAtomicNumber = 89;
    return "=IIf("
        + Expression.FieldsValue(ElementProperty.ChemicalProperty) + "="
            + (int)ChemicalProperty.Lanthanide + ","
        + Expression.FieldsValue(ElementProperty.AtomicNumber) + "-"
            + LanthanumAtomicNumber + ","
        + Expression.FieldsValue(ElementProperty.AtomicNumber) + "-"
            + ActiniumAtomicNumber + ")";
}

在这里,我使用了 RDLC 表达式可用的 IIf() 函数。将表达式翻译成通俗的英语如下——如果化学性质是镧系,则从原子序数中减去 57,否则从原子序数中减去 89。报表引擎将该数字视为组。

为元素单元格创建 RDLC

元素单元格也由一个 Tablix 组成,但它只有一个列和四行,正如您在设计界面上看到的

 

第一行单元格显示“[AtomicNumber]”,第二行显示“[ChemicalSymbol]”,并带有指定的字体大小和样式。这些只是简单的字段表达式。第三行和第四行要复杂得多。这就是为什么报表设计器显示“<<Expr>>”的原因。

第三行的文本值表达式由以下函数创建

private static string CreateHyperLinkExpression()
{
    var result =
        "='<a href=''' & IIf(IsNothing("
        + Expression.FieldsValue(ElementProperty.Url) + "),"
        + "'mailTo:me.com',"
        + Expression.FieldsValue(ElementProperty.Url) + ")"
        + " & '''>' & " + Expression.FieldsValue(ElementProperty.Name) + " & '</a>'";
    return result.ReplaceSingleQuoteWithDoubleQuote();
}

它非常复杂,但基本上它创建了一个 HTML 来表示用户可以点击以在浏览器中查看元素详细信息的超链接。通俗地说,这意味着——如果 URL 属性缺失,则链接到 mailto:me.com,否则链接到指定的 URL 属性并显示带有下划线的 Name 属性。在这里,我在编写字符串文字时使用了单引号而不是双引号,因为我想更容易地看到整个字符串,而无需使用转义序列 (\") 来表示双引号。 mailto:me.com 只是主表中上半部分空白元素的占位符。没有它,报表查看器在调试模式下运行并渲染 RDLC 时,会在输出窗口中显示警告消息。ReplaceSingleQuoteWithDoubleQuote() 方法会将所有单引号替换为双引号。

为了让报表查看器将其识别为 HTML,我们需要将 TextRun(该表达式所属的)的 MarkupType 指定为 HTML,如下所示

var textRun = new TextRun
    { Value = CreateHyperLinkExpression(), MarkupType = MarkupType.HTML };

第四行的文本值表达式由以下函数创建

private static string CreateRelativeAtomicMassExpression()
{
    var result =
        "=IIf(" + Expression.FieldsValue(ElementProperty.MassUncertainty) + " <> 0,"
        + Expression.FieldsValue(ElementProperty.RelativeAtomicMass) + " & '(' & "
        + Expression.FieldsValue(ElementProperty.MassUncertainty) + " & ')',"
        + "IIf(IsNumeric(" + Expression.FieldsValue(ElementProperty.AtomicNumber)
        + "),"
        + "'[' &" + Expression.FieldsValue(ElementProperty.RelativeAtomicMass)
        + " & ']',''))";
    return result.ReplaceSingleQuoteWithDoubleQuote();
}

这意味着——如果质量不确定性不等于零,则显示相对原子质量,后跟用括号括起来的质量不确定性。否则,如果原子序数是数字,则显示用尖括号括起来的相对原子质量。否则,显示为空白。钼是第一种情况的示例,锔是第二种情况的示例。

 

由于此 Tablix 没有 TablixCorner,因此构造方式与主表和子表略有不同。但我相信,现在您应该相对容易地按照源代码进行操作。

实际创建 RDLC 的最低级别函数

到目前为止,您已经看到了许多类名,例如 TablixTextRunsTextbox 等,它们用于构建主表、子表和元素单元格。那些较低级别的类如何创建实际的 RDLC——即,具有预定义名称和属性的 XML 代码?让我们以 Textbox 为例,看看 XML 是如何生成的

namespace PeriodicTable.Report.Rdl
{
    using System.Xml.Linq;
    public class Textbox : ReportItem
    {
        private static int index;
        private readonly string name;
        private readonly Paragraphs paragraphs = new Paragraphs();
        private readonly string rdlName;
        public Textbox(Paragraph paragraph)
        {
            this.rdlName = typeof(Textbox).GetShortName();
            this.name = this.rdlName + ++index;
            this.AddParagraph(paragraph);
        }
        public TextboxStyle TextboxStyle { get; set; }
        public void AddParagraph(Paragraph paragraph)
        {
            this.paragraphs.Add(paragraph);
        }
        protected override XElement Build()
        {
            var result = new XElement(
                this.rdlName,
                new XAttribute("Name", this.name),
                new XElement("CanGrow", true),
                new XElement("KeepTogether", true),
                this.paragraphs.Element);
            this.ConfigureTextboxStyle(result);
            this.ConfigureTop(result);
            this.ConfigureLeft(result);
            this.ConfigureHeight(result);
            this.ConfigureWidth(result);
            return result;
        }
        private void ConfigureTextboxStyle(XElement textbox)
        {
            if (this.TextboxStyle != null)
            {
                textbox.Add(this.TextboxStyle.Element);
            }
        }
    }
}

我决定使用 XElement 类,而不是 XmlDocument,因为 Jon Skeet 在 http://stackoverflow.com/questions/1542073/xdocument-or-xmldocument 中“强烈推荐”使用它。可能 XmlDocument 也能做到,但 XElement 是较新的。而最新的、最棒的总是更好的,对吧?

我不会详细解释该类,但如果您创建用于在主表中创建 TablixCornerTextbox,则会生成以下 XML 代码

<Textbox Name="Textbox1">
  <CanGrow>true</CanGrow>
  <KeepTogether>true</KeepTogether>
  <Paragraphs>
    <Paragraph>
      <TextRuns>
        <TextRun>
          <Value>Group</Value>
          <Style>
            <FontWeight>Bold</FontWeight>
          </Style>
        </TextRun>
        <TextRun>
          <Value>?</Value>
          <Style>
            <FontFamily>Wingdings</FontFamily>
          </Style>
        </TextRun>
      </TextRuns>
    </Paragraph>
    <Paragraph>
      <TextRuns>
        <TextRun>
          <Value>?</Value>
          <Style>
            <FontFamily>Wingdings</FontFamily>
          </Style>
        </TextRun>
        <TextRun>
          <Value>Period</Value>
          <Style>
            <FontWeight>Bold</FontWeight>
          </Style>
        </TextRun>
      </TextRuns>
    </Paragraph>
  </Paragraphs>
  <Style>
    <PaddingTop>2pt</PaddingTop>
    <PaddingBottom>2pt</PaddingBottom>
    <PaddingLeft>2pt</PaddingLeft>
    <PaddingRight>2pt</PaddingRight>
  </Style>
</Textbox>

结论

我展示了如何为报表查看器控件动态生成像元素周期表一样复杂的报表,其中包含超链接和图形图像。

参考

下面是 Visual Studio 2013 中使用的最新版本 RDL 规范的链接

http://download.microsoft.com/download/B/E/1/BE1AABB3-6ED8-4C3C-AF91-448AB733B1AF/Report%20Definition.xps

历史

2013 年 12 月 29 日:   初始帖子

© . All rights reserved.