Coder6841
Coder6841

Reputation: 1221

MSDN's multi-page printing example doesn't appear to work correctly

I'm following MSDN's "how to" document on printing multiple pages: How to: Print a Multi-Page Text File in Windows Forms

I've turned the example on that page into a project (tried Visual Studio 2010 and 2012) and found that it works as expected when printing a small amount of pages, but when printing a large amount (9 or so pages) it starts rendering the beginning pages as blank (page one and two are blank, the next 15 are correct, etc.).

Can anyone confirm this behaviour? I don't see what could be causing this and there are no exceptions thrown.

Here is the section of code that I believe contains the issue:

private void printDocument1_PrintPage(object sender, PrintPageEventArgs e)
{
    int charactersOnPage = 0;
    int linesPerPage = 0;

    // Sets the value of charactersOnPage to the number of characters 
    // of stringToPrint that will fit within the bounds of the page.
    e.Graphics.MeasureString(stringToPrint, this.Font,
        e.MarginBounds.Size, StringFormat.GenericTypographic,
        out charactersOnPage, out linesPerPage);

    // Draws the string within the bounds of the page
    e.Graphics.DrawString(stringToPrint, this.Font, Brushes.Black,
        e.MarginBounds, StringFormat.GenericTypographic);

    // Remove the portion of the string that has been printed.
    stringToPrint = stringToPrint.Substring(charactersOnPage);

    // Check to see if more pages are to be printed.
    e.HasMorePages = (stringToPrint.Length > 0);
}

I don't believe any of the datatypes are being overflowed. I've tried this with different printers but each have the same result.

Note: I've tried .NET Framework 4 and 4.5 with the same result.

Upvotes: 1

Views: 952

Answers (2)

user2980505
user2980505

Reputation: 21

This is old, but another solution that appears to work is to extract the string to be printed. I think the MSDN example may be failing due to the initial strings being garbage collected before the printing is complete.

private void printDocument1_PrintPage(object sender, PrintPageEventArgs e) {

     int charactersOnPage = 0;
     int linesPerPage = 0;

     // Sets the value of charactersOnPage to the number of characters
     // of stringToPrint that will fit within the bounds of the page.
     e.Graphics.MeasureString(stringToPrint, this.Font,
         e.MarginBounds.Size, StringFormat.GenericTypographic,
         out charactersOnPage, out linesPerPage);
 
     // Draws the string within the bounds of the page
     string tmp = stringToPrint.Substring(0, charactersOnPage);
     e.Graphics.DrawString(tmp, this.Font, Brushes.Black,
         e.MarginBounds, StringFormat.GenericTypographic);
 
     // Remove the portion of the string that has been printed.
     stringToPrint = stringToPrint.Substring(charactersOnPage);
 
     // Check to see if more pages are to be printed.
     e.HasMorePages = (stringToPrint.Length > 0); 
}

I think this preserves the reference until the operation is completed.

Upvotes: 0

Coder6841
Coder6841

Reputation: 1221

It seems that the MSDN example does not work as it should. It may be due to the fact that the MarginBounds Rectangle is integer-based as opposed to float-based. Keeping track of the Y-position as a float and using this value within the MeasureString and DrawString methods resolves the issue. I found this by examining a different MSDN printing example.

Here is the relevant code:

private void pd_PrintPage(object sender, PrintPageEventArgs ev)
{
    float linesPerPage = 0;
    float yPos = 0;
    int count = 0;
    float leftMargin = ev.MarginBounds.Left;
    float topMargin = ev.MarginBounds.Top;
    string line = null;

    // Calculate the number of lines per page.
    linesPerPage = ev.MarginBounds.Height /
       printFont.GetHeight(ev.Graphics);

    // Print each line of the file.
    while (count < linesPerPage &&
       ((line = streamToPrint.ReadLine()) != null))
    {
        yPos = topMargin + (count *
           printFont.GetHeight(ev.Graphics));
        ev.Graphics.DrawString(line, printFont, Brushes.Black,
           leftMargin, yPos, new StringFormat());
        count++;
    }

    // If more lines exist, print another page.
    if (line != null)
        ev.HasMorePages = true;
    else
        ev.HasMorePages = false;
}

This does not account for word wrapping like the previous example but that can be implemented with relative ease.

Upvotes: 1

Related Questions