Introduction
Now that we have dealt with the major issues, we will start expanding our application with some more advanced features. In Part XI we will use the tools provided in the MigraDoc library to create a universal output document that can be rendered to both a printer or a PDF file. MigraDoc is a powerful, and better yet, FREE library for creating documents. The library downloads and full documentations can be found at
Official PDFSharp & MigraDoc Website
MigraDoc - Creating A Document
MigraDoc and PDFSharp are both fairly extensive libraries with many features. I encourage you to explore it's features, as others not covered here could be of benefit to you. For our use, we will need two of the MigraDoc libraries, MigraDoc.Rendering
and MigraDoc.DocumentObjectModel
, as well as the PDFSharp
library. You can either get them from the source package with this article, or you can go to the site above and download the latest. You will have to build them to get the library dll files needed for referencing in your project. I would suggest just using the libraries I've included with the source.
The heart of our document output will be the MigraDoc.DocumentObjectModel.Document
. In our PurchaseOrderForm
class, let's create a method that will create and return this document object
public Document GetDocument()
{
return new Document();
}
This method will be the singular method for retrieving an output document. Whether we are printing, or outputting a PDF file, we will only be using this method to generate the Document
object. Once we get the document object, we can then render it however we like. In this case we will use both a PDF renderer and a printer renderer. So let's create our document. The GetDocument()
method is going to get pretty lengthy, so let's handle it is sections, each in a #region
tag. I'll place some commenting in the code sections to give an idea of what things are for. First let's create the Document Setup #region
. If you're unfamiliar with #region
, it's a way to compartmentalize your code in Visual Studio, as you can collapse a #region
of code that is enclosed between a #region
and #endregion
tag, and #region
tags can be nested. They are a good way to organize your code. We are going to be placing a number of things onto our document. We are aiming to have it look as near to our UI document representation as we can. That means we are going to put the logo, the three addresses, a box with the PO number, and the items. We will be forming tables to hold our data, and an Image to hold our logo. You'll see how each of the items below are used as we go. We will be printing 15 items per page. Here is our Document Setup #region
#region Document Setup
int itemsPerPage = 15;
int pageNumber = 1;
int totalPages = 0;
int countThisPage = 0;
foreach (Item it in _purchaseOrder.Items)
{
if (countThisPage >= itemsPerPage) countThisPage = 0;
if (countThisPage == 0) totalPages++;
countThisPage++;
}
String pageString;
String logoFilename = "truckLogo.png";
Document document = new Document();
MigraDoc.DocumentObjectModel.Shapes.Image image;
TextFrame txtFrame;
Paragraph paragraph;
TopPosition top;
LeftPosition left;
Table table;
Row row;
Column column;
Cell cell;
Unit leftMargin = new Unit(25.0);
Unit rightMargin = new Unit(70.0);
Unit topMargin = new Unit(12.0);
Unit bottomMargin = new Unit(6.0);
document.Info.Title = "Purchase Order " + _purchaseOrder.PurchaseOrderNumber;
document.Info.Comment = "Ordered from - " + _purchaseOrder.Vendor.Address.CompanyName;
document.Info.Author = "MDI Purchasing, Inc";
document.DefaultPageSetup.PageFormat = PageFormat.Letter;
document.DefaultPageSetup.Orientation = MigraDoc.DocumentObjectModel.Orientation.Landscape;
document.DefaultPageSetup.LeftMargin = leftMargin;
document.DefaultPageSetup.RightMargin = rightMargin;
document.DefaultPageSetup.TopMargin = topMargin;
document.DefaultPageSetup.BottomMargin = bottomMargin;
Style docstyle = document.Styles["Normal"];
docstyle.Font.Name = "Lucida Sans";
docstyle.Font.Size = 9.0;
#endregion
The process for putting text onto our document will flow as such: TextFrame -> Paragraph
or
TextFrame -> Table -> Cell -> Paragraph.
The Paragraph will hold the actual text String
. So after our document setup, we need to loop through our items, creating a new page for every 15 items, which is defined by itemsPerPage
. Since we've already calculated how many pages we need, we can simply use this to create our loop, we'll use a while
loop
while (pageNumber <= totalPages)
{
#region Create Page
#endregion
pageNumber++;
}
We will next our #region tags to make this method more organized, so that it makes more sense visually as you examine the code. So we will have several #region tags nested within the Create Page #region. First we need to add a page to our document. The page is a MigraDoc.DocumentObjectModel.Section
object. We will pass the document format specifics to the section. One nice thing about the MigraDoc library, as you add objects, the Add()
methods return the object that was added, so lets add a Section
to our document and define it's format and layout properties
#region Initialize New Section
Section docsection = document.AddSection();
docsection.PageSetup.PageFormat = document.DefaultPageSetup.PageFormat;
docsection.PageSetup.Orientation = document.DefaultPageSetup.Orientation;
docsection.PageSetup.LeftMargin = document.DefaultPageSetup.LeftMargin;
docsection.PageSetup.RightMargin = document.DefaultPageSetup.RightMargin;
docsection.PageSetup.TopMargin = document.DefaultPageSetup.TopMargin;
docsection.PageSetup.BottomMargin = document.DefaultPageSetup.BottomMargin;
#endregion
Now we have a page, so lets start adding our document components to it. First we will add our logo
#region Add Logo
image = docsection.AddImage(logoFilename);
image.Left = ShapePosition.Left;
image.Top = ShapePosition.Top;
image.RelativeHorizontal = RelativeHorizontal.Margin;
image.RelativeVertical = RelativeVertical.Margin;
image.ScaleHeight = 0.75;
image.ScaleWidth = 075;
#endregion
Let's stop here for a moment, because if you're like me, you like to see results as you work. Now we have an actual document with the logo on a page, so lets skip over and create the output methods to actual render our PDF and you can see it actually start working. Then we'll come back and finish filling in the rest of the document. So let's skip over to MDIForm
, and let's add a button to our tool strip, name it createPdfToolStripButton
, and drag it over beside the Print Preview tool stip button. Don't worry about it's icon right now, but set it's Enabled
property to False
Now double click the button to create its Click event handler. Here we will get the document from our active form, and render it as a PDF, and save it to our desktop, and then open it. Of course, if you haven't already, take a moment to install Adobe Reader. Here is what our event handler needs to look like
private void createPdfToolStripButton_Click(object sender, EventArgs e)
{
PurchaseOrderForm activeChildForm = this.ActiveMdiChild as PurchaseOrderForm;
MigraDoc.DocumentObjectModel.Document document = activeChildForm.GetDocument();
MigraDoc.Rendering.PdfDocumentRenderer pdfRender = new MigraDoc.Rendering.PdfDocumentRenderer();
pdfRender.Document = document;
pdfRender.RenderDocument();
if(pdfRender.PageCount > 0)
{
String fileName = Environment.GetFolderPath(Environment.SpecialFolder.Desktop) + "\\newDoc.pdf";
pdfRender.Save(fileName);
System.Diagnostics.Process.Start(fileName);
}
}
Lastly, we need to go to the OnMdiChildActivate method in MDIForm, and enable our button when appropriate
protected override void OnMdiChildActivate(object sender, EventArgs e)
{
....
createPdfToolStripButton.Enabled = false;
....
if (activeMdiChild != null)
{
....
createPdfToolStripButton.Enabled = true;
}
base.OnMdiChildActivate(e);
}
Now you can test this out, create a new form, and click the button to create the PDF and you'll get a single page document with the logo in the top left corner. Now as we add components to our page, you can test them out. At the end of the article we will cover rendering the document to a printer, so if you're in a hurry for that feel free to skip down to the bottom. Now let's move back to our GetDocument()
method in PurchaseOrderForm
and continue filling out our document. Next we'll add the box for our PurchaseOrderNumber
value. First, be sure that your project is in Debug mode, rather than release
In this next region of code you will see an #if DEBUG
block. If you aren't familiar with with this, it allows is to take an action that only compiles into our executable if we are in Debug
mode. Since we haven't provided a method for the user to set the PurchaseOrderNumber
property yet, we'll use this to set a test value to it that only happens in Debug mode. Here is the #region for adding the PurchaseOrderNumber frame to the document
#region Add PO Box
txtFrame = docsection.AddTextFrame();
txtFrame.Top = ShapePosition.Top;
txtFrame.RelativeVertical = RelativeVertical.Margin;
txtFrame.Left = ShapePosition.Left;
txtFrame.Left = txtFrame.Left.Position + 298.5;
txtFrame.RelativeHorizontal = RelativeHorizontal.Margin;
txtFrame.FillFormat.Color = Colors.LightGray;
txtFrame.LineFormat.Color = Colors.Black;
txtFrame.LineFormat.DashStyle = DashStyle.Solid;
txtFrame.LineFormat.Width = new Unit(0.5);
txtFrame.Width = new Unit(150.0);
txtFrame.Height = new Unit(15.0);
txtFrame.MarginTop = new Unit(2.5);
paragraph = txtFrame.AddParagraph();
paragraph.Format.Alignment = ParagraphAlignment.Center;
paragraph.Format.Font.Bold = true;
paragraph.AddText("Purchase Order Number");
top = txtFrame.Top.Position + 15.0;
txtFrame = docsection.AddTextFrame();
txtFrame.Top = top;
txtFrame.RelativeVertical = RelativeVertical.Line;
txtFrame.Left = ShapePosition.Left;
txtFrame.Left = txtFrame.Left.Position + 298.5;
txtFrame.RelativeHorizontal = RelativeHorizontal.Margin;
txtFrame.FillFormat.Color = Colors.White;
txtFrame.LineFormat.Color = Colors.Black;
txtFrame.LineFormat.DashStyle = DashStyle.Solid;
txtFrame.LineFormat.Width = new Unit(0.5);
txtFrame.Width = new Unit(150.0);
txtFrame.Height = new Unit(20.0);
txtFrame.MarginTop = new Unit(3.0);
paragraph = txtFrame.AddParagraph();
paragraph.Format.Alignment = ParagraphAlignment.Center;
paragraph.Format.Font.Bold = true;
paragraph.Format.Font.Size = 11.0;
#if DEBUG
_purchaseOrder.PurchaseOrderNumber = "0000123456";
#endif
paragraph.AddText(_purchaseOrder.PurchaseOrderNumber);
#endregion
Now you can test run your application again, try running it in Debug mode AND in Release mode, and you'll see that the test value only shows up in Debug mode. Using #if DEBUG can be helpful in testing functionality as you design it, just don't forget to switch to Release mode to build your live product.
Next, let's use the same process as above to add our Billing address to the document, except this time we will also be using a Table to more efficiently layout the address
#region Add Billing Address Box
txtFrame = docsection.AddTextFrame();
txtFrame.Top = ShapePosition.Top;
txtFrame.RelativeVertical = RelativeVertical.Margin;
txtFrame.Left = ShapePosition.Left;
txtFrame.Left = txtFrame.Left.Position + 457;
txtFrame.RelativeHorizontal = RelativeHorizontal.Margin;
txtFrame.FillFormat.Color = Colors.LightGray;
txtFrame.LineFormat.Color = Colors.Black;
txtFrame.LineFormat.DashStyle = DashStyle.Solid;
txtFrame.LineFormat.Width = new Unit(0.5);
txtFrame.Width = new Unit(290.0);
txtFrame.Height = new Unit(15.0);
txtFrame.MarginTop = new Unit(2.5);
paragraph = txtFrame.AddParagraph();
paragraph.Format.Alignment = ParagraphAlignment.Center;
paragraph.Format.Font.Bold = true;
paragraph.AddText("Billing Address");
top = txtFrame.Top.Position + 15.0;
txtFrame = docsection.AddTextFrame();
txtFrame.Top = top;
txtFrame.RelativeVertical = RelativeVertical.Margin;
txtFrame.Left = ShapePosition.Left;
txtFrame.Left = txtFrame.Left.Position + 457;
txtFrame.RelativeHorizontal = RelativeHorizontal.Margin;
txtFrame.FillFormat.Color = Colors.White;
txtFrame.LineFormat.Color = Colors.Black;
txtFrame.LineFormat.DashStyle = DashStyle.Solid;
txtFrame.LineFormat.Width = new Unit(0.5);
txtFrame.Width = new Unit(290.0);
txtFrame.Height = new Unit(70.0);
table = txtFrame.AddTable();
table.Format.Alignment = ParagraphAlignment.Left;
table.Format.LeftIndent = 3.0;
column = table.AddColumn(new Unit(70.0));
column.Format.Alignment = ParagraphAlignment.Left;
column = table.AddColumn(new Unit(220.0));
column.Format.Alignment = ParagraphAlignment.Left;
row = table.AddRow();
Unit billaddrowheight = new Unit(14.0);
row.Height = new Unit(billaddrowheight);
row.VerticalAlignment = VerticalAlignment.Center;
row.Cells[0].MergeRight = 1;
row.Cells[0].AddParagraph(_purchaseOrder.BillingAddress.CompanyName);
row = table.AddRow();
row.Height = new Unit(billaddrowheight);
row.VerticalAlignment = VerticalAlignment.Center;
row.Cells[0].MergeRight = 1;
row.Cells[0].AddParagraph(_purchaseOrder.BillingAddress.AddressLine1);
row = table.AddRow();
row.Height = new Unit(billaddrowheight);
row.VerticalAlignment = VerticalAlignment.Center;
row.Cells[0].MergeRight = 1;
row.Cells[0].AddParagraph(_purchaseOrder.BillingAddress.City + ", " +
_purchaseOrder.BillingAddress.State + " " +
_purchaseOrder.BillingAddress.ZipCode);
row = table.AddRow();
row.Height = new Unit(billaddrowheight);
row.VerticalAlignment = VerticalAlignment.Center;
row.Cells[0].AddParagraph("Phone:");
row.Cells[1].AddParagraph(_purchaseOrder.BillingAddress.PhoneNumber);
row = table.AddRow();
row.Height = new Unit(billaddrowheight);
row.VerticalAlignment = VerticalAlignment.Center;
row.Cells[0].AddParagraph("Fax:");
row.Cells[1].AddParagraph(_purchaseOrder.BillingAddress.FaxNumber);
table.SetEdge(0, 0, 2, 5, Edge.Box, MigraDoc.DocumentObjectModel.BorderStyle.None, 0.0);
#endregion
Next let's use the same process to add our Vendor address and Shipping address to the document
#region Add Vendor Address Box
txtFrame = docsection.AddTextFrame();
txtFrame.Top = top.Position + 80;
txtFrame.RelativeVertical = RelativeVertical.Margin;
txtFrame.Left = ShapePosition.Left;
txtFrame.RelativeHorizontal = RelativeHorizontal.Margin;
txtFrame.FillFormat.Color = Colors.LightGray;
txtFrame.LineFormat.Color = Colors.Black;
txtFrame.LineFormat.DashStyle = DashStyle.Solid;
txtFrame.LineFormat.Width = new Unit(0.5);
txtFrame.Width = new Unit(290.0);
txtFrame.Height = new Unit(15.0);
paragraph = txtFrame.AddParagraph();
paragraph.Format.Alignment = ParagraphAlignment.Center;
paragraph.Format.Font.Bold = true;
paragraph.AddText("Vendor Address");
top = txtFrame.Top.Position + 15.0;
txtFrame = docsection.AddTextFrame();
txtFrame.Top = top;
txtFrame.RelativeVertical = RelativeVertical.Margin;
txtFrame.Left = ShapePosition.Left;
txtFrame.RelativeHorizontal = RelativeHorizontal.Margin;
txtFrame.FillFormat.Color = Colors.White;
txtFrame.LineFormat.Color = Colors.Black;
txtFrame.LineFormat.DashStyle = DashStyle.Solid;
txtFrame.LineFormat.Width = new Unit(0.5);
txtFrame.Width = new Unit(290.0);
txtFrame.Height = new Unit(84.0);
table = txtFrame.AddTable();
table.Format.Alignment = ParagraphAlignment.Left;
table.Format.LeftIndent = 3.0;
column = table.AddColumn(new Unit(70.0));
column.Format.Alignment = ParagraphAlignment.Left;
column = table.AddColumn(new Unit(220.0));
column.Format.Alignment = ParagraphAlignment.Left;
row = table.AddRow();
Unit venaddrowheight = new Unit(14.0);
row.Height = new Unit(venaddrowheight);
row.VerticalAlignment = VerticalAlignment.Center;
row.Cells[0].MergeRight = 1;
row.Cells[0].AddParagraph(_purchaseOrder.VendorAddress.CompanyName);
row = table.AddRow();
row.Height = new Unit(venaddrowheight);
row.VerticalAlignment = VerticalAlignment.Center;
row.Cells[0].MergeRight = 1;
row.Cells[0].AddParagraph(_purchaseOrder.VendorAddress.AddressLine1);
row = table.AddRow();
row.Height = new Unit(venaddrowheight);
row.VerticalAlignment = VerticalAlignment.Center;
row.Cells[0].MergeRight = 1;
row.Cells[0].AddParagraph(_purchaseOrder.VendorAddress.City + ", " +
_purchaseOrder.VendorAddress.State + " " +
_purchaseOrder.VendorAddress.ZipCode);
row = table.AddRow();
row.Height = new Unit(venaddrowheight);
row.VerticalAlignment = VerticalAlignment.Center;
row.Cells[0].MergeRight = 1;
row = table.AddRow();
row.Height = new Unit(venaddrowheight);
row.VerticalAlignment = VerticalAlignment.Center;
row.Cells[0].AddParagraph("Phone:");
row.Cells[1].AddParagraph(_purchaseOrder.VendorAddress.PhoneNumber);
row = table.AddRow();
row.Height = new Unit(venaddrowheight);
row.VerticalAlignment = VerticalAlignment.Center;
row.Cells[0].AddParagraph("Fax:");
row.Cells[1].AddParagraph(_purchaseOrder.VendorAddress.FaxNumber);
table.SetEdge(0, 0, 2, 6, Edge.Box, MigraDoc.DocumentObjectModel.BorderStyle.None, 0.0);
#endregion
And the shipping address
#region Add Shipping Address Box
txtFrame = docsection.AddTextFrame();
txtFrame.Top = top.Position - 15;
txtFrame.RelativeVertical = RelativeVertical.Margin;
txtFrame.Left = ShapePosition.Left;
txtFrame.Left = txtFrame.Left.Position + 457;
txtFrame.RelativeHorizontal = RelativeHorizontal.Margin;
txtFrame.FillFormat.Color = Colors.LightGray;
txtFrame.LineFormat.Color = Colors.Black;
txtFrame.LineFormat.DashStyle = DashStyle.Solid;
txtFrame.LineFormat.Width = new Unit(0.5);
txtFrame.Width = new Unit(290.0);
txtFrame.Height = new Unit(15.0);
txtFrame.MarginTop = new Unit(2.5);
paragraph = txtFrame.AddParagraph();
paragraph.Format.Alignment = ParagraphAlignment.Center;
paragraph.Format.Font.Bold = true;
paragraph.AddText("Shipping Address");
top = txtFrame.Top.Position + 15.0;
txtFrame = docsection.AddTextFrame();
txtFrame.Top = top;
txtFrame.RelativeVertical = RelativeVertical.Margin;
txtFrame.Left = ShapePosition.Left;
txtFrame.Left = txtFrame.Left.Position + 457;
txtFrame.RelativeHorizontal = RelativeHorizontal.Margin;
txtFrame.FillFormat.Color = Colors.White;
txtFrame.LineFormat.Color = Colors.Black;
txtFrame.LineFormat.DashStyle = DashStyle.Solid;
txtFrame.LineFormat.Width = new Unit(0.5);
txtFrame.Width = new Unit(290.0);
txtFrame.Height = new Unit(84.0);
table = txtFrame.AddTable();
table.Format.Alignment = ParagraphAlignment.Left;
table.Format.LeftIndent = 3.0;
column = table.AddColumn(new Unit(70.0));
column.Format.Alignment = ParagraphAlignment.Left;
column = table.AddColumn(new Unit(220.0));
column.Format.Alignment = ParagraphAlignment.Left;
Unit shipaddrowheight = new Unit(14.0);
row = table.AddRow();
row.Height = new Unit(shipaddrowheight);
row.VerticalAlignment = VerticalAlignment.Center;
row.Cells[0].AddParagraph("Attention:");
row.Cells[1].AddParagraph(_purchaseOrder.ShippingAddress.Attention);
row = table.AddRow();
row.Height = new Unit(shipaddrowheight);
row.VerticalAlignment = VerticalAlignment.Center;
row.Cells[0].MergeRight = 1;
row.Cells[0].AddParagraph(_purchaseOrder.ShippingAddress.CompanyName);
row = table.AddRow();
row.Height = new Unit(shipaddrowheight);
row.VerticalAlignment = VerticalAlignment.Center;
row.Cells[0].MergeRight = 1;
row.Cells[0].AddParagraph(_purchaseOrder.ShippingAddress.AddressLine1);
row = table.AddRow();
row.Height = new Unit(shipaddrowheight);
row.VerticalAlignment = VerticalAlignment.Center;
row.Cells[0].MergeRight = 1;
row.Cells[0].AddParagraph(_purchaseOrder.ShippingAddress.AddressLine2);
row = table.AddRow();
row.Height = new Unit(shipaddrowheight);
row.VerticalAlignment = VerticalAlignment.Center;
row.Cells[0].MergeRight = 1;
row.Cells[0].AddParagraph(_purchaseOrder.ShippingAddress.City + ", " +
_purchaseOrder.ShippingAddress.State + " " +
_purchaseOrder.ShippingAddress.ZipCode);
row = table.AddRow();
row.Height = new Unit(shipaddrowheight);
row.VerticalAlignment = VerticalAlignment.Center;
row.Cells[0].AddParagraph("Reference:");
row.Cells[1].AddParagraph(_purchaseOrder.ShippingAddress.Reference);
table.SetEdge(0, 0, 2, 3, Edge.Box, MigraDoc.DocumentObjectModel.BorderStyle.None, 0.0);
#endregion
Now let's go back to our Item
class, and let's add a property to return the extended cost of the item
public double ExtendedCost { get { return _cost * _purchaseQuantity; } }
And lastly, let's add our items, again mostly the same way as we added our addresses, except we will do it in 2 steps. We don't want our table to be dynamic in appearance, we want it to be the same size on every page, so first we will create the items box itself, then we will go back and fill it with the items data. So first, let's build the item box
#region Add Items Table
txtFrame = docsection.AddTextFrame();
txtFrame.Left = ShapePosition.Left;
txtFrame.RelativeHorizontal = RelativeHorizontal.Margin;
txtFrame.Top = top.Position + 94;
txtFrame.RelativeVertical = RelativeVertical.Margin;
txtFrame.Width = document.DefaultPageSetup.PageHeight -
document.DefaultPageSetup.LeftMargin -
document.DefaultPageSetup.RightMargin;
txtFrame.Height = new Unit(287);
txtFrame.LineFormat.Color = Colors.Black;
txtFrame.LineFormat.Width = new Unit(0.5);
table = txtFrame.AddTable();
table.LeftPadding = 0;
table.RightPadding = 0;
Unit linecolwidth = new Unit(55);
Unit qtycolwidth = new Unit(55);
Unit pncolwidth = new Unit(105);
Unit vpncolwidth = new Unit(105);
Unit costcolwidth = new Unit(80);
Unit extcostwidth = new Unit(79);
Unit desccolwidth = new Unit(document.DefaultPageSetup.PageHeight -
document.DefaultPageSetup.LeftMargin -
document.DefaultPageSetup.RightMargin -
linecolwidth - qtycolwidth - pncolwidth -
vpncolwidth - costcolwidth - extcostwidth);
Unit desccolwidth1 = new Unit(desccolwidth / 2);
Unit desccolwidth2 = new Unit(desccolwidth / 2);
column = table.AddColumn(linecolwidth);
column = table.AddColumn(qtycolwidth);
column = table.AddColumn(pncolwidth);
column = table.AddColumn(vpncolwidth);
column = table.AddColumn(desccolwidth1);
column = table.AddColumn(desccolwidth2);
column = table.AddColumn(costcolwidth);
column = table.AddColumn(extcostwidth);
row = table.AddRow();
row.Height = new Unit(15);
row.Shading.Color = Colors.LightGray;
row.Format.Alignment = ParagraphAlignment.Center;
row.VerticalAlignment = VerticalAlignment.Center;
row.Format.Font.Bold = true;
row.Cells[0].MergeRight = 1;
row.Cells[0].AddParagraph("Buyer");
row.Cells[2].MergeRight = 1;
row.Cells[2].AddParagraph("Buyer Contact");
row.Cells[4].AddParagraph("Shipping Method");
row.Cells[5].AddParagraph("Freight Terms");
row.Cells[6].AddParagraph("Date");
row.Cells[7].AddParagraph("Page");
row = table.AddRow();
row.Format.LeftIndent = new Unit(2.0);
row.Format.Alignment = ParagraphAlignment.Center;
row.VerticalAlignment = VerticalAlignment.Center;
row.Height = new Unit(15);
row.Cells[0].MergeRight = 1;
row.Cells[0].AddParagraph("John Doe");
row.Cells[2].MergeRight = 1;
row.Cells[2].AddParagraph("john.dow@mdipurchasing.com");
row.Cells[4].Format.Alignment = ParagraphAlignment.Center;
row.Cells[4].AddParagraph("UPS Ground");
row.Cells[5].Format.Alignment = ParagraphAlignment.Center;
row.Cells[5].AddParagraph("Prepaid and Add");
row.Cells[6].Format.Alignment = ParagraphAlignment.Center;
row.Cells[6].AddParagraph(DateTime.Today.ToString("M/d/yyyy"));
row.Cells[7].Format.Alignment = ParagraphAlignment.Center;
pageString = String.Format("Page {0} of {1}", pageNumber, totalPages);
row.Cells[7].AddParagraph(pageString);
row = table.AddRow();
row.Height = new Unit(15);
row.Format.LeftIndent = new Unit(2.0);
row.VerticalAlignment = VerticalAlignment.Center;
row.Format.Alignment = ParagraphAlignment.Center;
row.Format.Font.Bold = true;
row.Shading.Color = Colors.LightGray;
row.Cells[0].AddParagraph("Line");
row.Cells[1].AddParagraph("Qty");
row.Cells[2].MergeRight = 1;
row.Cells[2].AddParagraph("Part");
row.Cells[4].MergeRight = 1;
row.Cells[4].AddParagraph("Description");
row.Cells[6].AddParagraph("Cost");
row.Cells[7].AddParagraph("Ext. Cost");
for (int i = 0; i < itemsPerPage; i++)
{
row = table.AddRow();
row.Height = new Unit(16.0);
row.VerticalAlignment = VerticalAlignment.Center;
row.Cells[2].MergeRight = 1;
row.Cells[4].MergeRight = 1;
}
table.SetEdge(0, 0, 8, 3, Edge.Interior, MigraDoc.DocumentObjectModel.BorderStyle.Single, 0.5);
table.SetEdge(0, 3, 8, itemsPerPage, Edge.Box, MigraDoc.DocumentObjectModel.BorderStyle.Single, 0.5);
table.SetEdge(0, 3, 8, itemsPerPage, Edge.Vertical, MigraDoc.DocumentObjectModel.BorderStyle.Single, 0.5);
#endregion
Now let's place our item data into the table
#region Add Items To Item Box
int itemstart = itemsPrinted;
int itemmax = 0;
for (int i = 0; (i < itemsPerPage && (itemmax + itemstart) < _purchaseOrder.Items.Count); i++)
{
if (i < itemsPerPage) itemmax++;
}
Item printItem;
for (int i = 0; i < itemsPerPage && itemsPrinted < (itemstart + itemmax); i++)
{
row = table.Rows[(i % itemsPerPage) + 3];
printItem = _purchaseOrder.Items[itemsPrinted];
row.Cells[0].Format.Alignment = ParagraphAlignment.Center;
row.Cells[0].AddParagraph((itemsPrinted + 1).ToString());
row.Cells[1].Format.Alignment = ParagraphAlignment.Center;
row.Cells[1].AddParagraph(printItem.PurchaseQuantity.ToString());
row.Cells[2].Format.Alignment = ParagraphAlignment.Left;
row.Cells[2].Format.LeftIndent = new Unit(4.0);
row.Cells[2].AddParagraph(printItem.ProductNumber);
row.Cells[4].Format.Alignment = ParagraphAlignment.Left;
row.Cells[4].Format.LeftIndent = new Unit(4.0);
row.Cells[4].AddParagraph(printItem.Description);
row.Cells[6].Format.Alignment = ParagraphAlignment.Right;
row.Cells[6].Format.RightIndent = new Unit(4.0);
row.Cells[6].AddParagraph(printItem.Cost.ToString("F02"));
row.Cells[7].Format.Alignment = ParagraphAlignment.Right;
row.Cells[7].Format.RightIndent = new Unit(4.0);
row.Cells[7].AddParagraph(printItem.ExtendedCost.ToString("F2"));
itemsPrinted++;
}
#endregion
Now, for one final portion of our document. Whenever we are on our last page, we want to output a box below our items for comments, instructions, our document grand total, and a signature. In our PurchaseOrder
class, let's add a readonly property to return our document total, which will be an aggregate of all of the items' ExtendedCost
, we'll call this property PurchaseOrderTotal
public double PurchaseOrderTotal
{
get
{
double total = 0;
foreach (Item i in Items) total += i.ExtendedCost;
return total;
}
}
Now we have everything we need to place our document "footer" so here's what that looks like
#region Add Comments Signatur And PO Total Box
if (pageNumber == totalPages)
{
top = txtFrame.Top.Position + txtFrame.Height;
txtFrame = docsection.AddTextFrame();
txtFrame.Top = top;
txtFrame.RelativeVertical = RelativeVertical.Margin;
txtFrame.Left = ShapePosition.Left;
txtFrame.RelativeHorizontal = RelativeHorizontal.Margin;
txtFrame.FillFormat.Color = Colors.LightGray;
txtFrame.LineFormat.Color = Colors.Black;
txtFrame.LineFormat.Width = new Unit(0.5);
txtFrame.Height = new Unit(15);
txtFrame.Width = new Unit(linecolwidth + qtycolwidth + pncolwidth +
vpncolwidth + desccolwidth + costcolwidth);
table = txtFrame.AddTable();
table.Format.Font.Bold = true;
table.LeftPadding = 0;
table.Format.LeftIndent = new Unit(4.0);
table.Format.RightIndent = new Unit(4.0);
column = table.AddColumn(new Unit(txtFrame.Width - costcolwidth));
column.Format.Alignment = ParagraphAlignment.Left;
column = table.AddColumn(new Unit(costcolwidth));
column.Format.Alignment = ParagraphAlignment.Right;
row = table.AddRow();
row.Height = new Unit(15);
row.VerticalAlignment = VerticalAlignment.Center;
row.Cells[0].AddParagraph("Comments");
row.Cells[1].AddParagraph("Total");
table.SetEdge(0, 0, 2, 1, Edge.Vertical, MigraDoc.DocumentObjectModel.BorderStyle.Single, 0.5);
left = txtFrame.Left.Position + txtFrame.Width;
txtFrame = docsection.AddTextFrame();
txtFrame.Top = top;
txtFrame.RelativeVertical = RelativeVertical.Margin;
txtFrame.Left = left;
txtFrame.RelativeHorizontal = RelativeHorizontal.Margin;
txtFrame.Height = new Unit(15);
txtFrame.Width = new Unit(extcostwidth);
txtFrame.LineFormat.Width = new Unit(0.5);
txtFrame.LineFormat.Color = Colors.Black;
table = txtFrame.AddTable();
table.Format.Alignment = ParagraphAlignment.Right;
column = table.AddColumn(new Unit(extcostwidth) - 0.5);
column.RightPadding = 0;
row = table.AddRow();
row.Height = new Unit(15);
row.VerticalAlignment = VerticalAlignment.Center;
row.Cells[0].AddParagraph(_purchaseOrder.PurchaseOrderTotal.ToString("F02"));
txtFrame = docsection.AddTextFrame();
txtFrame.Top = top.Position + 15;
txtFrame.RelativeVertical = RelativeVertical.Margin;
txtFrame.Height = new Unit(60);
txtFrame.Width = document.DefaultPageSetup.PageHeight - document.DefaultPageSetup.LeftMargin -
document.DefaultPageSetup.RightMargin - costcolwidth - extcostwidth;
txtFrame.Left = ShapePosition.Left;
txtFrame.RelativeHorizontal = RelativeHorizontal.Margin;
txtFrame.LineFormat.Color = Colors.Black;
txtFrame.LineFormat.Width = new Unit(0.5);
txtFrame.MarginTop = new Unit(4.0);
txtFrame.MarginBottom = new Unit(4.0);
txtFrame.MarginLeft = new Unit(4.0);
txtFrame.MarginRight = new Unit(4.0);
paragraph = txtFrame.AddParagraph();
paragraph.Format.Alignment = ParagraphAlignment.Left;
paragraph.Format.LineSpacing = new Unit(8.0);
paragraph.AddText("Sample comments for output testing");
left = txtFrame.Left.Position + txtFrame.Width;
txtFrame = docsection.AddTextFrame();
txtFrame.Top = top.Position + 15;
txtFrame.RelativeVertical = RelativeVertical.Margin;
txtFrame.Left = left;
txtFrame.RelativeHorizontal = RelativeHorizontal.Margin;
txtFrame.Width = new Unit(costcolwidth + extcostwidth);
txtFrame.Height = new Unit(60);
txtFrame.LineFormat.Width = new Unit(0.5);
txtFrame.LineFormat.Color = MigraDoc.DocumentObjectModel.Colors.Black;
txtFrame.MarginTop = new Unit(4.0);
txtFrame.MarginBottom = new Unit(4.0);
txtFrame.MarginLeft = new Unit(4.0);
txtFrame.MarginRight = new Unit(4.0);
paragraph = txtFrame.AddParagraph();
paragraph.Format.Alignment = ParagraphAlignment.Left;
paragraph.AddText("\nSignature ");
image = paragraph.AddImage("signature.png");
image.ScaleWidth = 0.5;
image.ScaleHeight = 0.5;
}
#endregion
The last 3 lines adds a signature file to the document, if you wanted to leave the document signature blank, and sign it manually, just leave thow last three lines off. Additionally you could sign a piece of paper, scan it and use it as your signature, but if you do you'll likely need to tinker with the scale so that it looks right. Just tweak image.ScaleHeight and image.ScaleWidth, though I suggest using the same value for both so that the image doesn't look skewed.
So that's it for the document, it's now complete. We've already handled rendering it to a PDF for test purposes, up above. Now let's handle rendering to a printer. We already created a print method way back to handle just a basic concept of GDI printing. Now we don't need that, though I will be leaving it in the source for reference but commented out. You can safely delete the printDoc_BeginPrint
and printDoc_PrintPage
methods. Your new printToolStripButton_Click
should look like
private void printToolStripButton_Click(object sender, EventArgs e)
{
PrintDialog printDialog = new PrintDialog();
if (printDialog.ShowDialog(this) == System.Windows.Forms.DialogResult.OK)
{
MigraDoc.DocumentObjectModel.Document document =
(this.ActiveMdiChild as PurchaseOrderForm).GetDocument();
MigraDoc.Rendering.DocumentRenderer renderer =
new MigraDoc.Rendering.DocumentRenderer(document);
renderer.PrepareDocument();
MigraDoc.Rendering.Printing.MigraDocPrintDocument printer =
new MigraDoc.Rendering.Printing.MigraDocPrintDocument(renderer);
printer.PrinterSettings = printDialog.PrinterSettings;
printer.DocumentName = document.Info.Title;
printer.Print();
}
}
This really only scratches the surface of what MigraDoc and PDFSharp can do. I hope it's a helpful addition to this study, giving you a way to easily add document output to your application.
Points of Interest
- MigraDoc and PDFSharp libraries
- PDF file output
- Printing
History
Keep a running update of any changes or improvements you've made here.
This member has not yet provided a Biography. Assume it's interesting and varied, and probably something to do with programming.