Using Events

Use Layout Elements when requiring specialized event-driven behavior. Two common uses of events for creating reports are the PlaceHolder event handler and the DocumentLayout class's ReportDataRequired event handler. The PlaceHolder class dynamically places items such as barcodes or images on a document while the ReportDataRequired dynamically adds data to a document.

PlaceHolder

The PlaceHolder class adds items such as barcodes or images to a details section of a report based on the current layout data. For example, the following code demonstrates how to add an event handler using the LaidOut event to dynamically place a barcode.

public void ReportExample()
{
    // Create the document's layout from a DLEX template
    DocumentLayout layout = new DocumentLayout("Placeholder.dlex");

    //  Retrieve the place holder and attach the event handler
    PlaceHolder barcodePlaceHolder = (PlaceHolder)layout.GetElementById("BarcodePlaceholder");
    barcodePlaceHolder.LaidOut += new PlaceHolderLaidOutEventHandler(BarcodePlaceHolder_LaidOut);

    // Specify the data to be used
    LayoutData layoutData = new LayoutData();
    layoutData.Add("PreparedFor", "Alex Smith");
    layoutData.Add("Orders", listOfOrders);

    // Layout the document and save the PDF
    Document document = layout.Layout(layoutData);
    document.Draw(outputFilePath);
}

public void BarcodePlaceHolder_LaidOut(object sender, PlaceHolderLaidOutEventArgs e)
{
    Code128 barcode = new Code128(e.LayoutWriter.Data["OrderID"].ToString(), 0, 0, e.ContentArea.Height);
    barcode.X += (e.ContentArea.Width - barcode.GetSymbolWidth()) / 2;
    barcode.ShowText = false;
    e.ContentArea.Add(barcode);
}
Public Sub void PlaceHolderExample()
    ' Create the document's layout from a DLEX template
    Dim MyLayout As DocumentLayout = New DocumentLayout("Placeholder.dlex")

    ' Retrieve the place holder And attach the event handler
    Dim MyBarcodePlaceHolder As PlaceHolder = MyLayout.GetElementById("BarcodePlaceholder")
    AddHandler MyBarcodePlaceHolder.LaidOut, AddressOf BarcodePlaceholder_LaidOut

    ' Specify the data to be used
    Dim MyLayoutData As LayoutData = New LayoutData()
    MyLayoutData.Add("PreparedFor", "Alex Smith")
    MyLayoutData.Add("Orders", MyListOfOrders)

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

Private Sub BarcodePlaceholder_LaidOut(ByVal sender As Object, ByVal e As PlaceHolderLaidOutEventArgs)
    Dim MyBarcode As Code128 = New Code128(e.LayoutWriter.Data("OrderID").ToString(), 0, 0, e.ContentArea.Height)
    MyBarcode.X += (e.ContentArea.Width - MyBarcode.GetSymbolWidth()) / 2
    MyBarcode.ShowText = False
    e.ContentArea.Add(MyBarcode)
End Sub

The sample code first retrieves the layout element with the id of "BarcodePlaceholder" as a PlaceHolder. It then attaches the PlaceHolder_LaidOut callback function to the LaidOut event. Then, while the document is being created, or "laid out", the event is called and the code in the fired event creates the barcode and places it in the correct document location.

ReportDataRequired

The second way to use events in a report is by using the ReportDataRequired event handler. When using this event handler, every time a report, or sub-report element is parsed in a DLEX file, the event is triggered and executes a callback function.

The following code assigns a callback named LayoutReport_ReportDataRequireed to the ReportDataRequired event.

DocumentLayout layoutReport = new DocumentLayout(@"..\..\SubReportWithData.dlex");
layoutReport.ReportDataRequired += LayoutReport_ReportDataRequired;

The ReportDataRequired event requires a callback function, when defining that function, it must have the same signature as the ReportDataRequired event's ReportDataRequiredEventHandler.

public delegate void ReportDataRequiredEventHandler(object sender, ReportDataRequiredEventArgs args);

For example, you might assign the following callback to the event.

private static void LayoutReport_ReportDataRequired(object sender, ReportDataRequiredEventArgs args)

This callback passes the sender sender and ReportDataRequiredEventArgs as parameters. The ReportDataRequiredEventArgs arguments consist of the DataProviderStack, ReportData, and ElementId, and DataName.

ReportDataRequired and SQL Reports

Use the ReportDataRequired event handler combined with SQL to create data-driven reports and sub-reports. Understanding how to create an event-driven report can be tricky, so let's consider an example in its entirety.

DLEX File

First, consider the following DLEX layout template.

<?xml version="1.0"?>
<document version="1.2" id="Document1" author="" keywords="" title="" subject="" 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">
	<page id="Page1">
		<recordBox font="Helvetica" fontSize="12" id="RecordBox6" x="412" y="0" width="100" height="14" dataFormat="" dataName="SUM(Amount,MainReport,SubReport)"/>
	</page>
	<report id="MainReport">
		<template id="Template1"/>
		<header height="50" id="Header1"/>
		<detail height="71.5" id="Detail1">
			<subReport dataName="Test" id="SubReport" x="0" y="0" width="512">
				<header height="15" id="Header2">
					<label font="Helvetica" fontSize="12" id="Label1" x="0" y="0" width="60.1" height="14" text="Customer:"/>
					<recordBox font="Helvetica" fontSize="12" id="RecordBox1" x="60" y="0" width="452" height="14" dataFormat="" dataName="CompanyName"/>
				</header>
				<detail height="15" id="Detail2">
					<recordBox font="Helvetica" fontSize="12" id="RecordBox3" x="412" y="0" width="100" height="14" align="right" dataFormat="" dataName="Amount"/>
					<recordBox font="Helvetica" fontSize="12" id="RecordBox2" x="0" y="0" width="308" height="14" dataFormat="" dataName="ProductName"/>
					<recordArea font="Helvetica" fontSize="12" id="RecordArea1" x="310" y="0" width="100" height="14">
						<text>|#Amount#|</text>
					</recordArea>
				</detail>
				<footer height="15" id="Footer2">
					<recordBox font="Helvetica" fontSize="12" id="RecordBox4" x="412" y="0" width="100" height="14" align="right" dataFormat="" dataName="SUM(Amount)"/>
					<recordArea font="Helvetica" fontSize="12" id="RecordArea2" x="310" y="-0.5" width="100" height="14">
						<text>|#SUM(Amount)#|</text>
					</recordArea>
				</footer>
			</subReport>
		</detail>
		<footer height="50" id="Footer1">
			<recordBox font="Helvetica" fontSize="12" id="RecordBox5" x="412" y="0" width="100" height="14" dataFormat="" dataName="SUM(Amount,MainReport,SubReport)"/>
		</footer>
	</report>
</document>

The DLEX template consists of a report with the id of "MainReport" and a nested sub-report with the id "SubReport". The main report is a company listing while the sub-report is a record listing of the company's products and cost.

Report Document

Now consider the method that creates the DocumentLayout. It assigns a callback function named LayoutReport_ReportDataRequired to the ReportDataRequired event. This event is fired and the callback invoked for each occurrence of a report and subReport element in a DLEX template. In the example above, the event is fired twice, once for the report and once for the sub-report.

private static Document SubReportWithData()
{
  // Create the document's layout from a DLEX template
  DocumentLayout layoutReport = new DocumentLayout(@"..\..\SubReportWithData.dlex");
  layoutReport.ReportDataRequired += LayoutReport_ReportDataRequired;

 // Specify the data.
 LayoutData layoutData = new LayoutData();
 //layoutData.Add("OrderLessThan", 10252);
 layoutData.Add("OrderLessThan", 10258);
    
 // Layout the document and save the PDF
 Document document = layoutReport.Layout(layoutData);
   return document;
 }

Event Handler

Now consider the callback implementation method. This callback is where the dynamic processing occurs that replaces the records in the DLEX template with data obtained from an SQL query.

private static void LayoutReport_ReportDataRequired(object sender, ReportDataRequiredEventArgs args) {
  if (args.ElementId == "MainReport") // The ID of the report
  {
     string connectionString = "Data Source=(local);Initial Catalog=Northwind;Integrated Security=true";
     string sqlString =
      "SELECT OrderID, o.CustomerID, c.CompanyName, o.OrderDate " +
      "FROM   Orders o " +
      "JOIN	Customers c ON o.CustomerID = c.CustomerID " +
      "WHERE  o.OrderID < " + args.Data["OrderLessThan"];

     SqlConnection connection = new SqlConnection(connectionString);
     SqlCommand command = new SqlCommand(sqlString, connection);
     connection.Open();
     SqlDataReader reader = command.ExecuteReader();
     args.ReportData = new DataReaderReportData(connection, reader);
  }
  else if (args.ElementId == "SubReport")
  {
    string connectionString = "Data Source=(local);Initial Catalog=Northwind;Integrated Security=true";
    string sqlString =
      "SELECT ProductName, Amount = Quantity * od.UnitPrice " +
      "FROM   [Northwind].[dbo].[Order Details] od " +
      "JOIN   Products p ON od.ProductID = p.ProductID " +
      "WHERE  OrderID = " + args.Data["OrderID"]; 

    SqlConnection connection = new SqlConnection(connectionString);
    SqlCommand command = new SqlCommand(sqlString, connection);
    connection.Open();
    SqlDataReader reader = command.ExecuteReader();
    args.ReportData = new DataReaderReportData(connection, reader);
   }
}

The record processing is straight-forward. Each record from the SQL dataset gets replaced by a record element in the DLEX file. However, recall in this example, the event handler is fired twice. The first time fired, it processes the main report by querying the data source for customer data. The second time fired, it processes the sub-report data. Taken together, a complete PDF is created by applying the data from the two datasets to the DLEX template.

In this topic