Introduction
When I was learning .NET programming, I found printing difficult and tedious to learn. I grasped the fundamentals of printing quickly, but when it came to printing multiple pages and tabular printing, I often felt frustrated trying to understand and program through it. However, when I finally prepared my mind to master it, I realized that there is a little mathematical and logical trick that is involved in printing.
In this article, I will discuss the printing of tabular data in multiple pages. For this purpose, I am using my data source datagrid from database records in Microsoft SQL Server. However, my target is to be able to print any data source in tabular form. This can be done for data sources such as ListView
s. So keep in mind that our intention is to print in tabular form, not program databases in this article. In addition, I will also include the code for printing individual records from a database.
When I wrote the article, I kept in my mind that we can also make reports and do printing using the Report Manager of Visual Studio and/or third party applications such as Crystal Reports. However, when we try to print and make a report for an application that does not involve databases, it will be better if we know the printing paradigm. So in this article, I will mainly concentrate on the mathematical geometry of the page.
Page Geometry
During printing, the first thing to consider is the geometry of the page. Since printing is similar to graphics drawing, we have to consider the page as our canvas. We notice that all the printing functions are done using similar functions of printing.
A single page has height, width as well as the left, right, top, and bottom margins. First, we calculate the actual area of the page on which data can be printed.
int top, bottom, left, right;
left = psp.PageSettings.Margins.Top;
right = psp.PageSettings.Margins.Bottom;
top = psp.PageSettings.Margins.Left;
bottom = psp.PageSettings.Margins.Right;
pagewidth = printDocument2.DefaultPageSettings.PaperSize.Width
-printDocument1.DefaultPageSettings.Margins.Left -
printDocument1.DefaultPageSettings.Margins.Right;
pageheight = printDocument2.DefaultPageSettings.PaperSize.Height -
printDocument1.DefaultPageSettings.Margins.Top -
printDocument1.DefaultPageSettings.Margins.Bottom;
In this, we calculate the boundaries area of the page.
Calculate the number of maximum rows to be printed in a single page. This is used for locating the number of pages for the data in the records (datagrid
).
rpp = (int)( 0.98 * ((pagewidth -
(int)(e.Graphics.MeasureString(grv.Columns[1].HeaderText, fh).Height) )/
(int)(sm.Height +10)));
Actual Printing
Column Heading
The first thing to be printed is the data heading from the data grid.
int i;
for (i = 0; i < grv.Columns.Count; i++) {
e.Graphics.DrawString(grv.Columns[i].HeaderText, fh, Brushes.Black, new
PointF(left + sf.Width * i + 50, top + 50)); }
int j;
Next, the records or the rows are printed.
Data Rows
The data in the rows are printed sequentially through iteration.
for (j = currentrow; j < Math.Min (rpp+currentrow , grv.Rows.Count )-1; j++)
{ int k;
for (k = 0; k < grv.Rows[j].Cells.Count; k++)
{ e.Graphics.DrawString(grv.Rows[j].Cells[k].Value.ToString().ToUpper(), ff,
Brushes.Black, new PointF(left + sf.Width * k + 50, top + 50 + (j +
1-currentrow) * (sm.Height + 10))); }
sum += Convert.ToDouble(grv.Rows[j].Cells[grv.Rows[j].Cells.Count - 2].Value);
e.Graphics.DrawRectangle(Pens.Black, new Rectangle((int)(left + 50),
(int)(top + 50 + (j-currentrow ) * (10 + sm.Height)),
(int)(left + sf.Width * (i - 1)), (int)(sm.Height + 10))); }
Printing Multiple Pages
The important code snippet in printing multiple pages, if needed, starts from the form declaration of the static variable:
static int currentrow = 0;
It is important to declare it as static
, since every time the printing procedure is incremented, it is incremented by the total number of rows to print in a single page calculated previously (rpp
).
currentrow += rpp-1;
if (currentrow >= (grv.Rows.Count))
{
e.Graphics.DrawString("Total: ", ff,
Brushes.Blue, new PointF(left + 50 + 3 * (sm.Width + 10), top + 50 + rt.Height
+ 10)); e.Graphics.DrawString(sum.ToString("##,##.00 NKF"), ff,
Brushes.Red, new PointF(left + 50 + 4 * (sm.Width + 10), top + 50 + rt.Height +
10));
e.Graphics.DrawLine(Pens.Blue, left + 50 + 4 *
(sm.Width + 10), top + 50 + rt.Height + 10 + sm.Height, left + 50 + 4 *
(sm.Width + 10) + e.Graphics.MeasureString(sum.ToString("##,##.0
NKF"), ff).Width, top + 50 + rt.Height + 10 + sm.Height);
}
Final Code
if (currentrow >= grv.Rows.Count)
{
e.HasMorePages = false;
currentrow = 0;
}
else
{
e.HasMorePages = true;
}
During printing, when an additional page is added, the print page event handler is called automatically. This continues recursively until the whole data is printed.
Point of Interest
Finally, I found out that printing is also a lot of fun. In this article, I was mainly concerned about the printing capabilities, although the application involves simple database manipulation.
History
- 7th October, 2009: Initial version.