Layout Data

Layout data is data applied to a DLEX file during the layout process that generates a PDF report. During runtime, layout data is dynamically applied to a static DLEX file to generate a PDF report. You can provide layout data from several different sources, including databases, JSON files, reflection to process enumerable data and data lists, and event-driven data

Understanding DLEX files is a prerequisite to understanding layout data. Refer to the following documentation if unfamiliar with DLEX files.

For simple name/value data, use the LayoutData class combined with the Add method. Use single-value data to add values to DLEX files containing non-repeatable data such as headers, footers, and titles. For repeatable data, such as rows in a table, use the LayoutData class combined with an Add method that contains an data collection such as an array or enumerable list of objects.

For example, the following example uses the LayoutData class to list products. The Add method uses reflection to obtain the list of Product instances. The DLEX file, SimpleReport.dlex, contains the XML layout containing placeholders for the layout data. Provided the layout data field names match the field names in the DLEX file, the layout data is applied to the DLEX template and generates a PDF report.

Products productsList = new List<Products>() {
    new Product {ProductName= "Chang", UnitPrice = 19M },
    new Product {ProductName= "Aniseed Syrup", UnitPrice = 10M },
    new Product {ProductName= "Chef Anton's Cajun Seasoning", UnitPrice = 22M }
};

// Create the document's layout from a DLEX template
DocumentLayout layout = new DocumentLayout("SimpleReport.dlex");

// Specify the data to be used
LayoutData layoutData = new LayoutData();
layoutData.Add("PreparedFor", "Alex Smith");
layoutData.Add("Products", productsList); // Any array or enumerable list of objects

// Layout the document and save the PDF
Document document = layout.Layout(layoutData);
document.Draw(outputFilePath);
Dim MyListOfProducts As Products = New List(Of Products)() From {
    New Product With {.ProductName = "Chang", .UnitPrice = 19D},
    New Product With {.ProductName = "Aniseed Syrup", .UnitPrice = 10D}}
    New Product With {.ProductName = "Chef Anton's Cajun Seasoning", .UnitPrice = 22D}
}

' Create the document's layout from a DLEX template
Dim MyLayout As DocumentLayout = New DocumentLayout("SimpleReport.dlex")

' Specify the data to be used
Dim MyLayoutData As NameValueLayoutData = New NameValueLayoutData()
MyLayoutData.Add("PreparedFor", "Alex Smith")
MyLayoutData.Add("Products", MyListOfProducts) ' Any array or enumerable list of objects

' Layout the document And save the PDF
Dim MyDocument As Document = MyLayout.Layout(MyLayoutData)
MyDocument.Draw(outputFilePath)

The names of layout data properties must match the names used in the DLEX template the data is being applied to.

Types of Layout Data

Layout data consists of two types of data used by a DocumentLayout. Top level layout data is data that appears fixed throughout a document and is applied to a single field, for example, a title. Enumerable data is data that is used to generate rows of data in a document.

Top Level Layout Data

Top level data are object values that appear consistently throughout a document (on fixed pages or reports). For example, the following replaces the #PreparedFor# field in the DLEX template and replaces it with the actual value.

layoutData.Add("PreparedFor", "Alex Smith");
MyLayoutData.Add("PreparedFor", "Alex Smith")

This type of data can be accessed from anywhere in a layout document by specifying the data in a dataName property of a record box or by putting it within pound signs in a record area text property (e.g. This was prepared for #PreparedFor#.).

Using Top Level Data for Simple Reports

Layout data need not contain dynamic repeating data elements. You can also use DLEX templates to generate static PDF reports, as the following example illustrates.

// Create the document's layout from a DLEX template
DocumentLayout layoutReport = new DocumentLayout(Util.GetResourcePath("DLEX/FormFill.dlex"));

 // Specify the data.
 LayoutData layoutData = new LayoutData();
 W4Data w4Data = new W4Data
 {
   FirstNameAndI = "Alex B.",
   LastName = "Smith",
   SSN = "123-45-6789",
   HomeAddress = "456 Green Road",
   IsSingle = true,
   CityStateZip = "Somewhere, Earth  12345",
   Allowances = 2
 };

 layoutData.Add("W4Data", w4Data);

 // Layout the document and save the PDF
 Document document = layoutReport.Layout(layoutData);
 document.Author = "DynamicPDF ReportWriter";
 document.Title = "Form Fill Example";
 document.Draw(outputPdfPath);
' Create the document's layout from a DLEX template
Dim documentLayout As DocumentLayout = New DocumentLayout(Util.GetResourcePath("DLEX/FormFill.dlex"))

 ' Specify the data.
 Dim layoutData As LayoutData= New LayoutData()
 Dim w4Data As W4Data = New W4Data()
 w4Data.FirstNameAndI = "Alex B."
 w4Data.LastName = "Smith"
 w4Data.SSN = "123-45-6789"
 w4Data.HomeAddress = "456 Green Road"
 w4Data.IsSingle = True
 w4Data.CityStateZip = "Somewhere, Earth  12345"
 w4Data.Allowances = 2

 layoutData.Add("W4Data", w4Data)

 ' Layout the document And save the PDF
 Dim document As Document = documentLayout.Layout(layoutData)
 document.Author = "DynamicPDF ReportWriter"
 document.Title = "Form Fill Example"
 document.Draw(outputPdfPath)

Note that the associated DLEX template, which completes a USA W4 tax form, contains no repeating report elements.

<?xml version="1.0"?>
<document version="1.2" xsi:schemaLocation="http://www.dynamicpdf.com http://www.dynamicpdf.com/schemas/DLEX12.xsd" xmlns="http://www.dynamicpdf.com" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" id="Document1" author="" keywords="" title="" subject="">
<page templatePath="fw4-p1.pdf" id="Page1" bottomMargin="36" leftMargin="36" rightMargin="36" topMargin="36">
    <recordBox id="RecordBox1" dataFormat="" fontSize="10" font="Courier" x="28" height="12" y="514" width="150" dataName="W4Data.FirstNameAndI"/><recordBox fontSize="10" font="Courier" id="RecordBox2" dataFormat="" height="12" x="184" y="514" width="218" dataName="W4Data.LastName"/>
    <recordBox id="RecordBox3" dataFormat="" font="Courier" fontSize="10" height="12" x="417" y="514" width="122" dataName="W4Data.SSN"/>
    <recordBox id="RecordBox4" dataFormat="" font="Courier" fontSize="10" height="12" x="28" y="538" width="244" dataName="W4Data.HomeAddress"/>
    <recordBox id="RecordBox5" dataFormat="" font="Courier" fontSize="10" height="12" y="562" x="28.5" width="244" dataName="W4Data.CityStateZip"/>
    <recordBox id="RecordBox6" dataFormat="" font="Courier" fontSize="10" height="10" x="477" width="61" align="center" y="576" dataName="W4Data.Allowances"/>
    <symbol id="Symbol1" height="14" width="14" size="10" x="285" y="527" visibilityCondition="W4Data.IsSingle"/>
    <symbol size="10" id="Symbol2" y="527" width="14" height="14" x="328" visibilityCondition="!W4Data.IsSingle"/>
</page>
</document>

Layout Data for Reports

Although DLEX files can generate complex PDF documents using non-repeating layout data elements, DynamicPDF Report's real strength comes from it's dynamic data capabilities using enumerable data. Enumerable data (a list of data) is data used by a report in a DLEX layout document. This data can be an enumerable list or array of objects, a JSON document, or data from a database. Data used by a report is specified with its dataName property (for example, Products in the example above). The dataName property of the data object corresponds with the dataName property in the DLEX template. For example, the following specifies UnitPrice as its dataName value.

<recordBox id="UnitPriceRecordBox" x="364" y="2" width="82" height="12" align="right" dataFormat="#,##0.00" dataName="UnitPrice"/>

Layout elements in a report's detail section then accesses the current object or record dataName property that matches the property or field name for that record in the DLEX template. Layout elements in a report's header or footer, will then display the data for the first object or record. If a Layout element specifies data that is not on the object, a value by that name will try retrieved recursively from its parents (if it is on a subreport) or from a top level data value from the layout data. For example, if a RecordBox in the details of a report has its DataName property set to PreparedFor, the value will first try to be retrieved from the report's data and if it is not found there, retrieved from the layout data.

Using Layout Data from JSON Data

The JavaScript Object Notation (JSON) is a widely used format for data interchange and file formatting. It consists of attribute/value pairs and arrays. For Example, the following example illustrates a simple JSON record-set consisting of an order and the order's two products.

{
  "OrderID": 11077,
  "LineItems": [
    {
      "ProductID": 2,
      "Quantity": 24,
      "ProductName": "Chang",
      "UnitPrice": 19
    },
    {
      "ProductID": 3,
      "Quantity": 4,
      "ProductName": "Aniseed Syrup",
      "UnitPrice": 10
    } 
  ]
}

The property names in the JSON data correspond with the dataName layout elements in the DLEX template.

using Newtonsoft.Json;

...
    
DocumentLayout documentLayout = new DocumentLayout(@"OrderDetails.dlex");
LayoutData layoutData = new LayoutData(JsonConvert.DeserializeObject(File.ReadAllText(@"InvoiceData.json")));
Document document = documentLayout.Layout(layoutData);
document.Draw(@"orderdetails.pdf");
Imports Newtonsoft.Json

...
    
Dim documentLayout As DocumentLayout = New DocumentLayout("OrderDetails.dlex")
Dim layoutData As LayoutData = New LayoutData(JsonConvert.DeserializeObject(File.ReadAllText("InvoiceData.json")))
Dim document As Document = documentLayout.Layout(layoutData)
document.Draw("orderdetails.pdf")

Recall that the LayoutData.Add method takes an Object supporting reflection to add data to a LayoutData instance. In the above example, it uses the DeserializeObject method from the JsonConvert class to deserialize the JSON text data into an object hierarchy.

Newtonsoft Nuget Package

DynamicPDF relies upon the Newtonsoft framework for processing JSON data. Install the Newtonsoft.Json NuGet package to install the Newtonsoft framework. After installing the NuGet package, you can then use the framework, as the previous example illustrates.

Using LINQ With XML Data

Language Integrated Query (LINQ) is a Microsoft .NET Framework component that adds native data querying capabilities to .NET languages. When using XML data, an easy way to process that data is through a LINQ query. For example, consider an XML dataset to use as a report's layout data.

<ProductList>
  <Product ProductID="17" ProductName="Alice Mutton" QuantityPerUnit="20 - 1 kg tins" UnitPrice="39.0000" Discontinued="1"/>
  <Product ProductID="3" ProductName="Aniseed Syrup" QuantityPerUnit="12 - 550 ml bottles" UnitPrice="10.0000" Discontinued="0"/>
  <Product ProductID="40" ProductName="Boston Crab Meat" QuantityPerUnit="24 - 4 oz tins" UnitPrice="18.4000" Discontinued="0"/>
</ProductList>

Using LINQ, you can load the XML dataset and query the elements, as the following example illustrates.

XElement products = XElement.Load(@"ProductList.xml");
var productQuery = from product in products.Descendants("Product")
select new
{
  ProductName = product.Attribute("ProductName").Value,
  QuantityPerUnit = product.Attribute("QuantityPerUnit").Value,
  UnitPrice = decimal.Parse(product.Attribute("UnitPrice").Value),
  Temp = from product2 in products.Descendants("Product")
     select new
     {
       ProductName = product2.Attribute("ProductName").Value,
       QuantityPerUnit = product2.Attribute("QuantityPerUnit").Value,
       UnitPrice = decimal.Parse(product2.Attribute("UnitPrice").Value),
     }
};
Dim products As XElement = XElement.Load("ProductList.xml")
Dim productQuery = From product In products.Descendants("Product")
Select New With {.ProductName = product.Attribute("ProductName").Value,
                 .QuantityPerUnit = product.Attribute("QuantityPerUnit").Value,
                 .UnitPrice = Decimal.Parse(product.Attribute("UnitPrice").Value),
                 .Temp = From product2 In products.Descendants("Product")
                 Select New With {.ProductName = product2.Attribute("ProductName").Value,
                                  .QuantityPerUnit = product2.Attribute("QuantityPerUnit").Value,
                                  .UnitPrice = Decimal.Parse(product2.Attribute("UnitPrice").Value)
        }
    }

The LINQ creates the object hierarchy from the query and then loads the object hierarchy into a LayoutData instance using the Add method.

DocumentLayout layoutReport = new DocumentLayout(@"..\SimpleReport.dlex");
LayoutData layoutData = new LayoutData();
layoutData.Add("Products", productQuery);
Document document = layoutReport.Layout(layoutData);
Dim layoutReport As DocumentLayout = New DocumentLayout("..\SimpleReport.dlex")
Dim layoutData As LayoutData = New LayoutData()
layoutData.Add("Products", productQuery)
Dim document As Document = layoutReport.Layout(layoutData)

Nested Data

For nested data, you can use sub-reports to create a report with multiple data groupings. For example, the following DLEX template contains a sub-report.

<?xml version="1.0"?>
<document xsi:schemaLocation="http://www.dynamicpdf.com http://www.dynamicpdf.com/schemas/DLEX12.xsd" version="1.2" xmlns="http://www.dynamicpdf.com" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" id="Document1" author="" keywords="" title="" subject="">
  <report dataName="ProductsByCategory" id="Report1">
    ...
    <subReport dataName="Products" id="SubReport1" x="0" y="18" width="512">
    ...
	</subReport>
    ...
	</report>
</document>

The sub-report DLEX elements match the categories in the data, for example, the following code example contains a list of categories where each category contains a list of products.

private static List<Category> productsByCategory = new List<Category>()
{
...
    
	new Category { Name = "Beverages", Products = new List<Product>() {
                new Product { ProductID = 1, ProductName = "Chai"},
                new Product { ProductID = 2, ProductName = "Chang"}
	}},
	new Category { Name = "Condiments", Products = new List<Product>() {
                new Product { ProductID = 3, ProductName = "Aniseed Syrup"},
                new Product { ProductID = 4, ProductName = "Chef Anton's Cajun Seasoning"}
	} },
};
Private Shared productsByCategory As List(Of Category) = New List(Of Category)() From 
{
...
    
	  New Category With {.Name = "Beverages", .Products = New List(Of Product)() From {
                New Product With {.ProductID = 1, .ProductName = "Chai"},
                New Product With {.ProductID = 2, .ProductName = "Chang"},              
            }},
        New Category With {.Name = "Condiments", .Products = New List(Of Product)() From {
                New Product With {.ProductID = 3, .ProductName = "Aniseed Syrup"},
                New Product With {.ProductID = 4, .ProductName = "Chef Anton's Cajun Seasoning"},             
            }},
};

That data might also come from a JSON document, as the following example illustrates an array of categories, and each sub-category contains a list of items.

 [
  {
    "Name": "Beverages",
    "Products": [
      {
        "ProductID": 1,
        "Names": "Chai"
      },
      {
        "ProductID": 2,
        "Name": "Chang"
      }
    ]
  },
  {
    "Name": "Condiments",
    "Products": [
      {
        "ProductID": 3,
        "Name": "Aniseed Syrup"
      },
      {
        "ProductID": 4,
        "Name": "Chef Anton's Cajun Seasoning"
      }
    ]
  }
]

You would then write the code to apply the DLEX template to the layout data no differently than you would a non-nested dataset. The sub-report logic is all contained within the data and the DLEX template.

// Create the document's layout from a DLEX template
DocumentLayout layoutReport = new DocumentLayout(Util.GetResourcePath("DLEX/SimpleSubReport.dlex"));
LayoutData layoutData = new LayoutData();
layoutData.Add("ProductsByCategory", SimpleSubReportExampleData.ProductsByCategory);
// Layout the document and set it's properties
Document document = layoutReport.Layout(layoutData);
document.Author = "DynamicPDF ReportWriter";
document.Title = "Simple SubReport Example";

// Outputs the document to a file
document.Draw(outputPdfPath);
' Create the document's layout from a DLEX template
Dim layoutReport As DocumentLayout = New DocumentLayout(Util.GetResourcePath("DLEX/SimpleReportWithCoverPage.dlex"))
Dim layoutData As LayoutData = New LayoutData()
layoutData.Add("ProductsByCategory", SimpleReportWithCoverPageExampleData.ProductsByCategory)
' Layout the document and set it's properties
Dim document As Document = layoutReport.Layout(layoutData)
document.Author = "DynamicPDF ReportWriter"
document.Title = "Simple Report Example"

' Outputs the document to a file
document.Draw(outputPdfPath)

Using Layout Data from a Database

Use the DataReaderReportData or DataTableReportData classes to use a database to obtain layout data for a report. An example is shown below. As with the other layout data types, the layout data database column names must match the dataName fields in the DLEX template.

// Create a database connection
string connectionString = "Data Source=(local);Initial Catalog=Northwind;Integrated Security=true";
SqlConnection connection = new SqlConnection(connectionString);

Document document = null;
using (connection)
{
    // Create the document's layout from a DLEX template
    DocumentLayout layoutReport = new DocumentLayout("SimpleReportWithCoverPage.dlex");

    // Query the database
    string sql = "SELECT ProductName, QuantityPerUnit, UnitPrice FROM Products";
    SqlCommand command = new SqlCommand(sql, connection);
    connection.Open();
    SqlDataReader reader = command.ExecuteReader();

    // Specify the data.
    NameValueLayoutData layoutData = new NameValueLayoutData();
    layoutData.Add("ReportCreatedFor", "Alex Smith");
    layoutData.Add("Products", new DataReaderReportData(connection, reader));

    // Layout the document
    document = layoutReport.Layout(layoutData);
}

// Save the PDF to a file               
document.Draw(outputFilePath);
 ' Create a database connection
Dim  MyConnectionString As String 
Set MyConnectionString  = "Data Source=(local);Initial Catalog=Northwind;Integrated Security=true"
Dim Myconnection As SqlConnection = New SqlConnection(connectionString)

Dim MyDocument  As Document = Nothing

 ' Create the document's layout from a DLEX template
 Dim MyLayoutReport As DocumentLayout = New DocumentLayout("SimpleReportWithCoverPage.dlex")

 ' Query the database
 Dim MySql As String 
 Set MySql = "SELECT ProductName, QuantityPerUnit, UnitPrice FROM Products"
 Dim MyCommand As SqlCommand = New SqlCommand(sql, connection)
 Myconnection.Open()
 Dim MyReader As SqlDataReader = MyCommand.ExecuteReader()

 ' Specify the data.
 Dim MylayoutData As NameValueLayoutData = New NameValueLayoutData()
 MylayoutData.Add("ReportCreatedFor", "Alex Smith")
 MylayoutData.Add("Products", New DataReaderReportData(connection, reader))

 ' Layout the document
 MyDocument = MyLayoutReport.Layout(MylayoutData)
 Myconnection.Close()
                        
 ' Save the PDF to a file               
 MyDocument.Draw(outputFilePath);

In this topic