Click here to Skip to main content
65,938 articles
CodeProject is changing. Read more.
Articles
(untagged)

Generating RDLC Dynamically for the Report Viewer Local Report

0.00/5 (No votes)
2 Jan 2014 1  
This article explains how to dynamically create RDLC.

 

Introduction 

Do your reports have tons of variations that you don’t think it’s a good idea to statically design all of them at design time? Then, this article might help you generating the reports dynamically – i.e., not designing *.rdlc files at design time. The above screenshot is a sample report that I created dynamically for this article – a periodic table that features hyperlinks and embedded images.

You can download the source code written in C# and developed in Visual Studio 2013.

Background

In a recent software project, I had a requirement where as many as 100 tables and charts needed to be reported based on conditions the user determines. In some scenarios, the user wants tables 1, 3, 54, and 88, together with the charts 12, 13, and 55. In others, the report should show the tables 2 and 4 with no charts, and so on. The number of combinations were huge, if not infinite.

I Googled for articles on generating RDLC dynamically, but there didn't seem to be much information. What Microsoft offers in http://technet.microsoft.com/en-us/library/ms170239(v=sql.105).aspx is too crude to use as a basis for generating my own specific reports. Simply put, the GenerateRdl() function is just bad, monolithic code without any structure and uses lots of string literals.

So I embarked on creating a set of classes myself so I can code something like this – if the user wants the tables 1, 3, 54, and 88, then I would add those tables to the report by using C#’s control flow statements such as "if" and "switch".

Using the Code

Now, let’s look at the Form class code that shows the report generator filled with the periodic table:

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() function instantiates a ReportViewer, puts it in the Controls, passes its LocalReport to the PeriodicTableReportGenerator constructor, and then calls the Run() method. PeriodicTableReportGenerator Generates the RDLC dynamically and sets up the LocalReport. Finally Form1 calls RefreshReport() to show the periodic table.

Below is the code of 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()));
            …
        }
    }
}

The Run() method creates two DataSet - one for the MainTable that shows the main periodic table without the Lanthanides and Actinides, and the other for the SubTable that shows them just below the main table as is normally the case. This DataSet just defines the name of the data fields in string and is completely different from the class with the same name in ADO.NET. Because I didn’t want to use string literals to represent the properties in the collection, I resorted to reflection to get them. The ElementProperty Class does that.

The main table’s RDLC is then created by calling MainTable.Create() static method that returns a ReportItem. The second table is created in the same way.

Then, it instantiates a Body and adds the generated main and sub tables to it. Here, if the user does not want to see the Lanthanides and Actinides, then I could just skip adding the sub table. This is the freedom I wanted in my project. The body is enclosed with a ReportSection and is then added to the Report that is instantiated in its base class - ReportGenerator.

Next, we add the two DataSet to the Report. Then we generate the data themselves – i.e., the physical elements data that belong to the main table and those for Lanthanides and Actinides. We assign the data set name for each table and add the data source to DataSources. As you can see in the screenshot, the periodic table features a dimmed national flag that represents the country of the scientist or the team of scientists that first discovered the element. So, we create the images and add them to the Report as well.

Finally, the Run() function calls its base.Run() method. That’s it.

Report Design

Statically designing the same report is of course not difficult. The following figure shows the design of the periodic table as the Visual Studio’s Report Designer renders it:

 

As you can see, there are two tables – the upper left one is that for the main table and the lower right for the second. Note that even if the periodic table has a total of 18 columns, the design surface does not have 18 columns in the table. Instead, it has just one. Likewise, even if the periodic table has a total of 7 rows, there is only a single row in the design. Adding columns and rows dynamically, driven by the data, is the job of the report engine. So, one of our most important jobs in designing a report is to comprehend "what" should be repeated in both column and row directions. Once we understand what is repeating, we can identify the so-called "group expression" that determines what data are grouped together.

In RDLC, the tables are called Tablix – a synthesized word by combining table and matrix. Just for your reference, I included Report1.rdlc file that I statically designed by using the report designer to make the same periodic table. You can compare the results of the dynamic and static RDLC generations by changing the startup form from Form1 to Form2 in Program.cs.

Just for your information, Form2’s load event looks like this:

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();
}

The statically designed RDLC file is assigned to the LocalReport.ReportEmbeddedResource. Exactly the same data sources are added to the report viewer.

Our PeriodicTableReportGenerator generates this same RDLC dynamically.

Dynamically creating a Tablix

Now, before diving deep into the sea of RDLC, we need to make ourselves familiar to some terminology. The upper left corner of the table is called TablixCorner. In the main periodic table, I show "Groupà" and "âPeriod" as string constants in the cell’s Textbox.

The upper right corner that represents the table’s column hierarchy is called TablixColumnHierarchy, which is where we put a group expression so that columns can be added horizontally, driven by the data, in addition to showing the group number text string. In the main table, a column represents the "group" of the element, ranging from 1 to 18. Because the group number must change dynamically, I cannot use a string constant as I did for the TablixCorner. Instead we have to use "expression" so the report engine can dynamically evaluate it. The simplest expression is just a field of a DataSet. This is exactly the case for this cell and the design surface shows "[Group]" for the cell value, which means that the report engine takes the field of "Group" from the data and convert it to a string dynamically. Expression of this simplest form is presented as the field name enclosed by angle brackets by the report designer.

The lower left corner that represents the table’s row hierarchy is called TablixRowHierarchy in which we specify another group expression so that rows can be added vertically, driven by the data, in addition to showing the period number text string. Again, the text is the simplest expression – "[Period]".

The lower right corner – the entire rectangle enclosed in maroon – is called TablixBody, which is the meat of the periodic table and represents the details of each element. In designing it, I tried to mimic one from Wikipedia - http://en.wikipedia.org/wiki/Periodic_table_(large_version), including the cell background color and frame style. I also added a background image to show the country of origin.

Now, unlike the other three cells in the main table’s Tablix, we have to put a lot more information in the TablixBody cell – atomic number, chemical symbol, name of the element with a hyperlink, and relative atomic mass with possible uncertainty. So, instead of putting a single Textbox, I put in the cell a Rectangle in which another Tablix that has four rows in itself is placed.

Finally, we are ready to look at the code that generates the main table’s 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);
        }
    }
}

The static Create() method just instantiates a Tablix and returns it as a ReportItem. As you may have imagined, a Tablix <is a> ReportItem. As I explained in the previous section, a Tablix is made of four parts. It also needs to know the data set name that it will work on. As a result, the constructor has five parameters.

Creating TablixCorner

The CreateTablixCorner() method creates a TablixCorner, where I have to add the text constants - "Groupà" and "âPeriod" separated by a new line in a Textbox. A TextRun represents a "text run" that shares the same font parameters. The Report Viewer’s default font is Arial with 10pt font size. If you do not specify the font parameters in TextRun, it will use the default font. Since I wanted to show "Group" in bold face, I specified it as shown below:

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

This is how you specify a text run and add it to TextRuns, which is a collection of TextRun.

Now, the left arrow character is in Windings font. So, I need to create another TextRun and add it to the same TextRuns:

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

Next, I have to add "âPeriod" in the same text box, but on a new line. This means that I need two Paragraphs – one with the "Groupà" text run and the other with the "âPeriod" text run. The Textbox will then be complete:

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

Finally, the function instantiates a TablixCorner with the Textbox as its CellContents and returns it.

Creating TablixColumnHierarchy

The CreateTablixColumnHierarchy() method creates a TablixColumnHierarchy, where I have to add the group and sort expressions together with the text that represents the "group" of the element. If you get confused with the two usages of the term "group", the onus is on me in having chosen an example that has the same term in business domain. The two expressions are created in a similar way:

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);

We are grouping each element by using its Group property – i.e., Group 1 elements, Group 2 elements, up to Group 18 elements. The sort expression is also the group number because we want to order the elements in its ascending order. If we do not add the sort expression, then the Report Viewer will place the elements as it appears in the data source.

Now, let’s talk about the expressions. Expressions in RDLC always start with an equal sign. The simplest expression is, as I mentioned earlier, the data set’s field value and should look like this:

=Fields!AtomicNumber.Value

You could put an expression by using string literals like this:

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

But I wanted to avoid it because I always make typos and string literals cannot be checked by the compiler. If the Report Viewer finds anything wrong with the supplied RDLC, it shows an error message on the screen when its RefreshReport() method is called:

 

When you see one of those, you will have to debug your RDLC generation. I added a debug statement in the Run() method of the ReportGenerator class:

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

By uncommenting the first statement, you would be able to see the entire RDLC in Visual Studio’s Output window in debug mode. 

Finally, we create a TablixHeader in which we place the row header text that is also an expression:

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));

Note that TextAlign is an attribute of a Paragraph. I added TextAlign.Center in order to show the group number in the center of the cells. The first parameter for the constructor represents the height of the header row in inch.

Creating TablixRowHierarchy

The CreateTablixRowHierarchy() method creates a TablixRowHierarchy, where I have to add the group and sort expressions together with the text that represents the "period" of the element. TablixRowHierarchy looks very much like TablixColumnHierarchy. Note that the VerticalAlign is an attribute of a Textbox. Because I wanted to show the period number in the middle of the row header cell, I put VerticalAlign.Middle for the Textbox:

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

The first parameter for the constructor represents the width of the header column in inch:

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

Creating TablixBody

At last, we create the TablixBody. As I said, it’s the meat of the periodic table. But the method is unexpectedly short. This is because we delegate creating the contents of the body cell to another class - ElementCell. We just add the created ReportItem in a Rectangle and finish creating TablixBody. We specify the body cell’s width in TablixColumn and height in TablixRow.

Note that you could put the created ReportItem directly in the CellContents of the TablixCell without enclosing it first with a Rectangle. But if you do that, the element’s cells would have no margins between them as shown below:

 

By adding a Rectangle and specifying the Top and Left properties in the class’s constructor allows us to add the 0.025 inch margin:

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()
        };
    }
…

Creating RDLC for the second table

The second table for the Lanthanides and Actinides is very similar to the main table. The only point of interest is the group expression of the column hierarchy; we cannot use the simplest "group" expression because all Lanthanides and Actinides share the same group number which is 3. So, we need to come up with something that can be used as "group". I noticed the following fact; if I subtract the atomic number of Lanthanum from all Lanthanides and that of Actinium form all Actinides, then I will have a new "group" number that starts with zero for both Lanthanides and Actinides. This is done by using the following expression:

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 + ")";
}

Here, I use the IIf() function that is available for RDLC expressions. Translating the expression into plain English would give the following – if the chemical property is Lanthanide, then subtract 57 from the atomic number, else subtract 89 from the atomic number. The report engine will take that number as the group.

Creating RDLC for the Element Cell

The element cell is also made of a Tablix, but it is made of only a single column with four rows as you can see on the design surface:

 

The first row cell shows "[AtomicNumber]" and the second one shows "[ChemicalSymbol]" in the specified font size and styles. These are just simple field expressions. The third and fourth rows are much more complex. This is why the Report Designer shows "<<Expr>>".

The third row’s text value expression is created by the following function:

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();
}

It is very complex, but basically it creates an HTML to represent the hyper link that the user can click to see the element’s details in the browser. In plain English, it means the following – if the URL property is missing, then hyper link to mailto:me.com, else hyper link to the specified URL property and shows the Name property with the underline. Here, I used a single quote instead of double quote in writing the string literal because I wanted to see the entire string a little easier without the escape sequence (\") to represent a double quote. The mailto:me.com is just a place holder for the blank elements that appear in the upper middle of the main table. Without that, the Report Viewer will show a warning message while rendering the RDLC in the Output window of the Visual Studio when running under debug mode. The ReplaceSingleQuoteWithDoubleQuote() method replaces all single quotes by double quotes.

In order to let the Report Viewer to recognize it as an HTML, we need to specify MarkupType as HTML for the TextRun in which the expression belongs to as shown below:

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

The fourth row’s text value expression is created by the following function:

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();
}

It means the following – if mass uncertainty is not equal to zero, then show the relative atomic mass followed by the mass uncertainty enclosed by a parenthesis. Else if the atomic number is numeric, then show the relative atomic mass enclosed by an angle bracket. Otherwise, show a blank. Molybdenum is an example of the first case and Technetium is one for the second case:

 

Since this Tablix does not have a TablixCorner, the construction is a little different from that of the main table and sub table. But I believe that you can relatively easily follow the source code, by now.

Lowest level functions that actually creates RDLC

So far, you have seen many class names such as Tablix, TextRuns, Textbox, and so on, which are used to construct the main table, sub table, and element cell. How do those lower level classes create the actual RDLC – i.e., XML code with the predefined names and attributes? Let’s look at the Textbox to see how XML is generated:

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);
            }
        }
    }
}

I decided to use the XElement class, rather than the XmlDocument, because Jon Skeet "thoroughly recommended" the use of it in http://stackoverflow.com/questions/1542073/xdocument-or-xmldocument. Probably, XmlDocument can do the same, but XElement is newer. And the latest and greatest is always better, right?

I will not explain the details of the class, but if you create the Textbox that is used to create the TablixCorner in the main table, the following XML code will be generated:

<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>

Conclusion

I showed how a report as complex as periodic table with hyperlinks and graphical images can be dynamically generated for the Report Viewer control.

Reference

Below is the link of the latest version of the RDL spec that is used in Visual Studio 2013:

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

History

12/29/2013:   Initial post

License

This article has no explicit license attached to it but may contain usage terms in the article text or the download files themselves. If in doubt please contact the author via the discussion board below.

A list of licenses authors might use can be found here