Publishing Dynamic Files - PDF/XPS Creation Using the Infragistics Document Library

So you want to publish your data to a PDF.  You've come to the right place to learn about how to not only generate these documents but to manipulate and style them as well.  Before we begin, let's familiarize ourselves with the object that the PDF is going to be generated from.  In our toolset, we provide a code library to generate these reports called the Infragistics Document Engine.  This engine contains all the elements to create and fill a Report object which can then be published to either PDF or XPS, Microsoft's XML paper specification, through a simple method call.  Also, since this is a code library, this can be used in both Web and Win platforms. 

Explanation regarding content :

Since this can be done on both platforms, the post will be explained through the ASP.NET point of view.  Almost all of the code is identical in a Windows Forms environment.  The general outline will be an explanation of content, followed by commented code, and then a screenshot of the result.

Exporting in 2ish seconds

Here at Infragistics, we try to make things easy as possible while at the same time providing rich customization options.  You can export your grids to our reports without even looking at the Document Engine API.  To accomplish this, you will want to drag a UltraWebGridDocumentExporter on to your form where you have your UltraWebGrid that is bound to some data.  You will also want to drag on some sort of UI element like a button to initiate the export of the grid's data.  Once you have those components on your design surface, handle the clicking of the button and export the data like so:

UltraWebGridDocumentExporter1.Export(UltraWebGrid1);

Generated PDF:

image

There are some customization options you have off the exporter component.  I suggest taking a look at the members of the UltraWebGridDocumentExporter if you want to modify things like Margin, Orientation, or AutoResizing.

The Infragistics Document Library

So you now have a document that you can pass around.  This was created from one line of code but it involves many different elements that are nested inside of each other.  Here is a link to the elements so you will at least be aware of the cl***I will be talking about.

Essentially, you have elements that layout the content and have certain restrictions on what can be placed inside of them.  The basic hierarchy is Report -> Section -> Layout -> Content.  Table that shows what can be placed inside of what.

Basically, you create a report then add a section.  You can add content like text and images to the section or if you need a more complex layout, use additional layout elements like grids and flows. 

The grid and table is essentially very similar with the main difference being the grid requires columns.

The band and group is essentially the same thing with the main different being the group does not have headers, footers, and dividers.

The container element allows you to inject xml.

The flow element allows you to have content that continues from one column to another (think newspapers).

The site element allow you to have complete control over positioning (think absolute positioning through css).

The rest of the elements are pretty self explanatory. Chain (keeps things together), Gap (creates gaps), Rotator (rotates content), Rule (creates a rule), Stretcher (fills the rest of the page).

You also have Trees, Lists, and Text to display content. 

Finally, you can also add a Canvas to almost any element to start drawing circles and other primitives.

Common Changes

You may need the following using statements for the code samples below:

using Infragistics.Documents.Report;

using Infragistics.Documents.Report.Section;

using Infragistics.Documents.Report.Text;

using Infragistics.Documents.Graphics;

using Infragistics.Documents.Report.Table;

using Infragistics.Documents.Report.Grid;

using Infragistics.Documents.Report.Band;

using Infragistics.Documents.Report.Flow;

using System.Xml;

Adding headers and footers

Let's make some customization so it doesn't look like just a table of data.  To do that, we are going to work with the elements described above.  Off the UltraWebGridDocumentExporter object, the export method has a few overloads, one of which allows you to export to a section.  To take advantage of this overload, you want to create a the document then add the section.  You then make any customization you need and then export the grid to the specified section. 

Code for creating a header and page numbers:

//creates a report object

Report r = new Report();

//adds a section to add content to

ISection s1 = r.AddSection();

//sets properties on the section

//to add page numbers

s1.PageNumbering.SkipFirst = true;

s1.PageNumbering.OffsetY = -18;

s1.PageNumbering.Template = "Page [Page #] of [TotalPages]";

//adds a header to the PDF

ISectionHeader s1header = s1.AddHeader();

s1header.Height = 20;

s1header.Repeat = false;

//adds text to the header area

IText s1headertitle = s1header.AddText(0, 0);

s1headertitle.AddContent("Sung's Awesome PDF");

s1headertitle.Alignment = TextAlignment.Center;

IText s1headertimestamp = s1header.AddText(0, 0);

s1headertimestamp.AddContent("Produced: ");

s1headertimestamp.AddDateTime("MM/dd/yyyy hh:mm:ss");

s1headertimestamp.Alignment = TextAlignment.Right;

//exports the grid to the section

this.UltraWebGridDocumentExporter1.Export(this.UltraWebGrid1, s1);

 

 

 

PDF with Header:

image

Exact view of rows

Now I want to export the data based on the current view.  Very similar to a screen print, say you only wanted to export the rows that are current expanded.  To accomplish this you will want to handle the InitializeRow event of the UltraWebGridDocumentExporter and see if the row is not expanded.  If not, skip the exporting of its children through a simple property setting.

if (!e.Row.Expanded)

{

    e.SkipDescendants = true;

}

Exported PDF after only expanding the 2nd row on the ASPX page:

image

Formatting Rows or Cells

Now let's add styling to certain rows.  Maybe the end user is interested in the Orders that are associated with a specific Employee.  The following uses the InitializeRow event as above and formats the UltraWebGrid's row.  This style is then translated to the PDF when it is created.

Code for styling the row:

//orders table

if (e.Row.Band.Index == 1)

{

    //if the cell value in the employeeid column

    //is 1

    if ((int)(e.Row.Cells.FromKey("EmployeeID").Value)==1)

    {

        //set the style on the row

        //any style set on the row or cell gets applied

        //to the pdf

        e.Row.Style.BackColor = System.Drawing.Color.Pink;

    }

}

Pink rows in the orders table that have an Employee ID of 1:

image

Adding Hyperlinks to your document

Besides displaying information, you may want users to be able to quickly go to a url by clicking on an element inside the PDF.  To accomplish this, you will want to handle the CellExporting event so that you can create your own content for the appropriate cell.  After you find the cell you want to modify, make use the hyperlink feature that is available off the IText element.  You may also want to style it differently so that the end user knows it can be clicked on.

Code to replace plain text with a hyperlink in the CustomerID column:

//if the CustomerID column

//and its on the first level

if (e.GridCell.Column.Key == "CustomerID" & e.GridCell.Band.Index == 0)

{

    //style for the hyperlink

    Infragistics.Documents.Report.Text.Style bluelink = new Infragistics.Documents.Report.Text.Style(new Font("Aria", 8, FontStyle.Underline), Brushes.Blue);

    //adds the text with the hyperlink

    e.ReportCell.AddText().AddContent(e.GridCell.Value.ToString(), bluelink, new Hyperlink(this.Request.Url.ToString() + @"?ID=" + e.GridCell.Value.ToString()));

    //cancels the creation of the normal output

    //for this cell

    e.Cancel = true;

}

 

Links in PDF:

image

ASP.NET page is shown when clicking on the "ANATR" hyperlink:

image

 

Explanation of PDF generated in the Portfolio Manager Sample

There is a great example of PDF customization in our samples browser called the Portfolio Manager written by Tony Lombardo.  You can find the generated PDF here

Screenshot of Portfolio Manager PDF:

image

Now that is a great looking pdf.  The code is available when you download and install the samples on your machine but I thought I should explain how this is done.  There are several events that you want to take advantage of when using our exporter.  As we did before this sample makes use of a Export"ing" as opposed to an Export"ed" event.  These are the events you want to handle when you want to create your own content for a given region.  This following code segment is taken from the RowExporting event off the UltraWebGridDocumentExporter.  It creates a chart and p***a reference to the current section and the chart it wants exported to the user defined BuildTable method.

Code in the RowExporting Event:

//if we are exporting the first band/level

if (e.GridRow.Band.Index == 0)

{

    //table used to provide data to the chart

    DataTable tempTable = new DataTable();

    tempTable.Columns.Add("AccountType",typeof(System.String));

    tempTable.Columns.Add("Balance",typeof(System.Decimal));

    //generate data for the chart

    for (int i = 0; i < e.GridRow.Rows.Count; i++)

    {

        tempTable.Rows.Add(new object[] { " "+e.GridRow.Rows[i].Cells.FromKey("AccountType").Value, e.GridRow.Rows[i].Cells.FromKey("Balance").Value });

    }

    //instantiates and sets properties on the chart

    Infragistics.WebUI.UltraWebChart.UltraChart chart = new Infragistics.WebUI.UltraWebChart.UltraChart("chart");

    chart.ChartType = Infragistics.UltraChart.Shared.Styles.ChartType.PieChart3D;

    chart.ColorModel.ModelStyle = Infragistics.UltraChart.Shared.Styles.ColorModels.CustomLinear;

    chart.DataSource = tempTable;

    chart.Height = Unit.Pixel(230);

    chart.Width = Unit.Pixel(420);

    chart.TitleBottom.Extent = 0;

    chart.TitleTop.Text = "Current Asset Allocation";

    chart.TitleTop.HorizontalAlign = StringAlignment.Center;

    chart.TitleTop.Font = new Font("Verdana", 15);

    chart.TitleTop.FontColor = Color.Blue;

    chart.TitleTop.Margins.Top = 10;

    chart.Transform3D.Scale = 100;

    chart.DataBind();

    chart.Legend.Margins.Top = 40;

    chart.Legend.Visible = true;

    chart.Legend.Location = Infragistics.UltraChart.Shared.Styles.LegendLocation.Right;

    chart.Legend.SpanPercentage = 30;

    chart.Legend.BorderThickness = 0;

    chart.Border.Thickness = 0;

    //calls a method to create the layout for each record

    //passing in the section/region of the pdf, grid row information

    //and the chart we just created

    BuildTable(((ISection)e.ContainingTable.Parent),e.GridRow,chart);

}

//sets a booleon so that it does not create the

//default layout of each row in the PDF

e.Cancel = true;

Here is the BuildTable method that creates a Grid element for each row.  You will see the use of alternating styles, RowSpan for the last column, and formatting through padding, margin, and size:

private void BuildTable(Infragistics.Documents.Report.Section.ISection section,Infragistics.WebUI.UltraWebGrid.UltraGridRow row, Infragistics.WebUI.UltraWebChart.UltraChart chart)

{

    //creates a grid element to layout the contents of each row

    Infragistics.Documents.Report.Grid.IGrid grid = section.AddGrid();

    //sets some layout properties on the object

    grid.Borders.Corners.All= new Infragistics.Documents.Report.Corner(Infragistics.Documents.Graphics.Pens.DodgerBlue, Infragistics.Documents.Utils.Converter.PixelsToPoints(10));

    grid.Borders.All = new Infragistics.Documents.Report.Border(Infragistics.Documents.Graphics.Pens.DodgerBlue);

    grid.Margins.All = Infragistics.Documents.Utils.Converter.PixelsToPoints(10);

    grid.Paddings.All = Infragistics.Documents.Utils.Converter.PixelsToPoints(10);

    //creates 3 columns for laying out the grid

    IGridColumn labels=grid.AddColumn();

    IGridColumn data = grid.AddColumn();

    IGridColumn chartCol = grid.AddColumn();

    //sets the width of each column

    labels.Width = new Infragistics.Documents.Report.RelativeWidth(15);

    data.Width = new Infragistics.Documents.Report.RelativeWidth(25);

    chartCol.Width = new Infragistics.Documents.Report.RelativeWidth(60);

    //cell count is used to designate the amount

    //of rows created

    int rows = row.Cells.Count;

    //two styles are created to simulate an alternate style

    Infragistics.Documents.Report.Background background1 = new Infragistics.Documents.Report.Background(new Infragistics.Documents.Graphics.LinearGradientBrush(new Infragistics.Documents.Graphics.Color(Color.WhiteSmoke), new Infragistics.Documents.Graphics.Color(Color.Gainsboro), 2f));

    Infragistics.Documents.Report.Background background2 = new Infragistics.Documents.Report.Background(new Infragistics.Documents.Graphics.LinearGradientBrush(new Infragistics.Documents.Graphics.Color(Color.Gainsboro), new Infragistics.Documents.Graphics.Color(Color.WhiteSmoke), 2f));

    for (int i = 0; i < rows; i++)

    {

        //creates a new row on the layout grid for each cell value

        IGridRow irow = grid.AddRow();

        //adds a cell for the label

        //and adds a IText

        IGridCell cell = irow.AddCell();

        cell.Paddings.All = Infragistics.Documents.Utils.Converter.PixelsToPoints(3);

        cell.AddText().AddContent(row.Cells[i].Column.Header.Caption);

        //alternates the style applied

        if (i % 2 == 0)

        {

            cell.Background = background1;

        }

        //adds another cell for displaying

        //the value

        cell = irow.AddCell();

        cell.Paddings.All = Infragistics.Documents.Utils.Converter.PixelsToPoints(3);

        cell.AddText().AddContent(row.Cells[i] != null && row.Cells[i].Value != null ? row.Cells[i].Value.ToString() : "");

        //alternate style again

        if (i % 2 == 0)

        {

            cell.Background = background2;

        }

        //final cell for displaying the chart

        cell = irow.AddCell();

        cell.Paddings.All = Infragistics.Documents.Utils.Converter.PixelsToPoints(10);

        cell.Alignment.Horizontal = Infragistics.Documents.Report.Alignment.Right;

        //creates a canvas element that the chart

        //can render an image to

        Infragistics.Documents.Report.ICanvas canvas = cell.AddCanvas();

        canvas.Borders.Corners.All = new Infragistics.Documents.Report.Corner(Infragistics.Documents.Graphics.Pens.DimGray, Infragistics.Documents.Utils.Converter.PixelsToPoints(5));

        canvas.Borders.All=new Infragistics.Documents.Report.Border(Infragistics.Documents.Graphics.Pens.DimGray);

        canvas.Height = new Infragistics.Documents.Report.FixedHeight(Infragistics.Documents.Utils.Converter.PixelsToPoints(307f));

        canvas.Width=new Infragistics.Documents.Report.FixedWidth(Infragistics.Documents.Utils.Converter.PixelsToPoints(560f));

        //only creates a chart once for each row

        if(i==0)chart.RenderPdfFriendlyGraphics(canvas.CreateGraphics());

        cell.RowSpan = rows;

    }

}

PDF from scratch

Up till now we worked with the exporter component to generate PDF files.  We are now going to use a lot of what we went over to create a PDF from scratch.  You will need a reference to the Infragistics.Document assembly.  All this code is executed in a button click and is broken up into 3 segments.  The first segment deals with creating the report and a section.  We then add a header, define some styles and add a grid to layout the first level of the header.  I chose to use a grid and the ability to span columns so that I didn't have to define widths.  I also use some alignment and some styles on the IText elements.

Code for part 1:

//creates the report

Report r = new Report();

//creates the section

ISection sect1 = r.AddSection();

//creates a non repeating header

ISectionHeader sect1Head = sect1.AddHeader();

sect1Head.Repeat = false;

sect1Head.Height = 80;

 

//styles used in the header

Infragistics.Documents.Report.Text.Style headerstyle = new Infragistics.Documents.Report.Text.Style(new Font("Old English Text MT", 32), Brushes.Black);

Infragistics.Documents.Report.Text.Style headerSmall = new Infragistics.Documents.Report.Text.Style(new Font("Arial", 8), Brushes.Black);

Infragistics.Documents.Report.Text.Style headerTiny = new Infragistics.Documents.Report.Text.Style(new Font("Arial", 6), Brushes.Black);

 

//adds a group to the header to help layout contents

IGroup sect1HeadGroup = sect1Head.AddGroup(0, 0);

//using grid to layout first set of elements

IGrid sect1HeadGroupGrid = sect1HeadGroup.AddGrid();

//using the gridpattern to style the grid

GridPattern gridPattern = new GridPattern();

gridPattern.Margins = new Margins(5);

gridPattern.Paddings = new Paddings(10);

sect1HeadGroupGrid.ApplyPattern(gridPattern);

//adds 5 columns, columns must be added for a grid element

sect1HeadGroupGrid.AddColumn();

sect1HeadGroupGrid.AddColumn();

sect1HeadGroupGrid.AddColumn();

sect1HeadGroupGrid.AddColumn();

sect1HeadGroupGrid.AddColumn();

//frist row of the grid in the header

IGridRow headergridrow1 = sect1HeadGroupGrid.AddRow();

IGridCell r1c1 = headergridrow1.AddCell();

IText r1c1text = r1c1.AddText();

r1c1text.AddContent("Everything you need to know about NetAdvantage");

r1c1.Borders = new Borders(new Pen(Colors.Black, DashStyle.Solid), 2);

IGridCell r1c2 = headergridrow1.AddCell();

r1c2.ColSpan = 3;

IText sect1HeadText = r1c2.AddText();

sect1HeadText.Alignment = TextAlignment.Center;

sect1HeadText.AddContent("The Sung Kim Times", headerstyle);

IGridCell r1c5 = headergridrow1.AddCell();

IText r1c5text = r1c5.AddText();

r1c5text.AddContent("NetAdvantage 9.1 Releasing Soon!");

//adds a rule after the grid

sect1HeadGroup.AddRule();

Code needed to export the PDF to the browser:

Response.AddHeader("Content-Disposition", "attachment; filename=" + "doc.pdf");

Response.AddHeader("Content-Transfer-Encoding", "binary");

r.Publish(Response.OutputStream, FileFormat.PDF);

Response.Flush();

Response.End();

Generated PDF w/ custom header

image

The 2nd part deals with adding an additional level of of the header.  I chose to use two separate grids so that I could use the rule element and split the content.  For the 2nd section, I do more of the same with adding text to cells with some styling.

Code for 2nd part:

//2nd grid for the 2nd row of content

IGrid sect1HeadGroupGrid2 = sect1HeadGroup.AddGrid();

sect1HeadGroupGrid2.AddColumn();

sect1HeadGroupGrid2.AddColumn();

sect1HeadGroupGrid2.AddColumn();

sect1HeadGroupGrid2.AddColumn();

sect1HeadGroupGrid2.AddColumn();

//adds text to various cells with some styles

IGridRow headergridrow2 = sect1HeadGroupGrid2.AddRow();

headergridrow2.AddCell().AddText().AddContent("VOL. I....No. 1", headerSmall);

headergridrow2.AddCell().AddText().AddContent("Copyright 2009 The Sung Kim Times", headerTiny);

headergridrow2.AddCell().AddText().AddContent("TUESDAY, JANUARY 27, 2009", headerSmall);

headergridrow2.AddCell();

headergridrow2.AddCell().AddText().AddContent("Printed in New Jersey, ONE DOLLAR", headerTiny);

//adds a rule to seperate content

sect1HeadGroup.AddRule();

sect1.PageMargins = new Margins(20);

Generated PDF w/ two Grids in the headers:

image

Now that the header is done, this last part creates the content for the newsletter.  Again, I create some styles and add a flow element to the section.  I chose to use the flow so that I could emulate the newsletter outline.  I load a rss feed into an XmlDocument and filter out the unnecessary data.  I then go through each article and create text elements for the pertinent data.  I chose to use the table element here so that I could keep content,author and published date, on the same line and also use the KeepSolid property to keep the title, author, and date all together (not split between columns/pages).  For the title of each article, I also used a hyperlink so that one could navigate to the actual article by clicking.  The description/article portion is relatively long so I add that as a separate IText element.  Also, I am using the AddRichContent method as opposed to AddContent method since the description element contains html.  If you were to use AddContent, you would have all the tags rendered in your pdf.

Code for the content of the newletter:

//styles used in the report

Infragistics.Documents.Report.Text.Style postTitle = new Infragistics.Documents.Report.Text.Style(new Font("Arial", 18, FontStyle.Underline), Brushes.Blue);

Infragistics.Documents.Report.Text.Style postDescrip = new Infragistics.Documents.Report.Text.Style(new Font("Arial", 10), Brushes.Black);

Infragistics.Documents.Report.Text.Style dateStyle = new Infragistics.Documents.Report.Text.Style(new Font("Aria", 8, FontStyle.Italic), Brushes.Black);

 

//flow used to layout content

IFlow flow = sect1.AddFlow();       

//adding two columns to the flow

IFlowColumn flowcolumn1 = flow.AddColumn();

flowcolumn1.Width = new RelativeWidth(50);

flowcolumn1.Margins.Top = 10;

flowcolumn1.Margins.Right = 10;

IFlowColumn flowcolumn2 = flow.AddColumn();

flowcolumn2.Width = new RelativeWidth(50);

flowcolumn2.Margins.Top = 10;

flowcolumn2.Margins.Left = 10;

//loading data from a rss feed

XmlDocument myfeed = new XmlDocument();

//use the following if you do not have a dropdownlist with urls

//myfeed.Load(@"http://blogs.infragistics.com/blogs/MainFeed.aspx?GroupID=15&Type=AllBlogs");

myfeed.Load(this.DropDownList1.SelectedItem.Value);

//selects the articles from the feed

XmlNodeList articles = myfeed.SelectNodes("rss/channel/item");

//goes through each article

foreach (XmlNode article in articles)

{

    //table used to keep the title and author together

    ITable table = flow.AddTable();

    //keeps content from splitting

    table.KeepSolid = true;

    ITableRow tabler0 = table.AddRow();

    ITableCell tabler0c0 = tabler0.AddCell();

    IText title = tabler0c0.AddText();

    title.AddContent(article["title"].InnerText, postTitle, new Hyperlink(article["link"].InnerText));

    ITableRow tabler1 = table.AddRow();

    ITableCell tabler1c1 = tabler1.AddCell();

    tabler1c1.Width = new FixedWidth(100);

    tabler1c1.AddText().AddContent("by: " + article["dc:creator"].InnerText);

    ITableCell tabler1c2 = tabler1.AddCell();

    tabler1c2.Width = new RelativeWidth(100);

    tabler1c2.AddText().AddContent("published: " + article["pubDate"].InnerText, dateStyle);           

    IText description = flow.AddText();

    description.Style = postDescrip;

    description.AddRichContent(article["description"].InnerText);

}

Final PDF with the header, two columns using the Flow element, and html content through the AddRichContent method:

image

 

Additional Comments

You can also export out latest and greatest WebDataGrid to PDF, XPS, and Excel.  Take a look at this post by our ASP.NET Product Manager Murtaza Abdeali

 

Sample uses the following:

-Infragistics NetAdvantage 2008 Volume 3 CLR 3.5

-Visual Studio 2008

Get full sample here. (388kb)

Just get the Newsletter Sample here. (7kb)


Comments  (4 )

fl_hawk
on Fri, Mar 20 2009 10:31 AM

Sung,

Thank you! I searched the online documentation for 2 days to find nothing close to this treasure trove of information! All I wanted to do was add a Header to my PDF export of a web grid.

Sincerely,

Pete

aak
on Mon, Apr 6 2009 10:30 AM

Sung, this is fantastic.  I too spent some time trying to find all the information you have consolidated into one presentation.  Infragistcis should pay you for this publication. :)

[Infragistics] Sung Kim
on Thu, May 7 2009 2:10 PM

@fl_hawk and akk

Thank you for your comments.  I decided to write this article from my own experience in learning the library.  Hopefully some of the outlining helped break the components and their uses down for you.

senthil
on Tue, Jul 14 2009 2:03 AM

its very help full for me

Add a Comment

Please Login or Register to add a comment.