Printing

G

Guest

I have an application that prints documents that it creates. It uses what I
believe is a standard .NET way of doing so, like this:

PrintDocument pd = new PrintDocument();
pd.PrintPage += new PrintPageEventHandler
(this.pd_PrintPage);
PrintDialog PrintDialog1 = new PrintDialog();
PrintDialog1.Document = pd;

DialogResult result = PrintDialog1.ShowDialog();
if (result == DialogResult.OK)
{
this.streamToPrint = new StreamReader(FileName);
pd.Print();
this.streamToPrint.Close();
}

I have written a pd_PrintPage method that does the actual graphics commands
to print each page. This all works fine.

Where I have encountered a problem is in implementing selective page
printing. I have been able to activate the page range in the dialog like this:

PrintDialog1.PrinterSettings.FromPage=1;
PrintDialog1.PrinterSettings.ToPage=100;
PrintDialog1.AllowSomePages=true;

I had hoped that this was all I needed to do, since the printing routine
clearly knows what page it is printing as it displays the page number while
doing so, and generally pages the document properly. But it always prints all
the pages regardless of what the users selects. I have this in my page print
method, which allows me to determine whether this page should be printed or
not:

PrinterSettings PrSe=ev.PageSettings.PrinterSettings;
if(PrSe.PrintRange==PrintRange.SomePages&&
(PrSe.FromPage>PageNumber||PrSe.ToPage<PageNumber))
// do not print this page.

But I can not figure out a way to tell Windows not to print the page. If I
simply do not execute the contents of the print page routine, I still get a
blank page. How do I not print the page entirely? Alternatively, is there an
example somewhere of using the AllowSomePages and FromPage and ToPage
members? Thanks.
 
W

Walter Wang [MSFT]

Hi Richard,

In the event of PrintPage, a blank page is already created for you. Please
use following code to test this AllowSomePages feature:

Create a simple WinForm application, add a Button to the default Form,
handle its Click event:

const int TOTAL_PAGES = 10;
int _pageNumber;
int _maxPageNumber;

private void button1_Click(object sender, EventArgs e)
{
PrintDocument _doc = new PrintDocument();
_doc.PrintPage += new PrintPageEventHandler(_doc_PrintPage);
_doc.PrinterSettings.FromPage = 1;
_doc.PrinterSettings.ToPage = TOTAL_PAGES;
_doc.PrinterSettings.MinimumPage = 1;
_doc.PrinterSettings.MaximumPage = TOTAL_PAGES;
PrintDialog pd = new PrintDialog();
pd.AllowSomePages = true;
pd.Document = _doc;
if (pd.ShowDialog() == DialogResult.OK)
{
if (pd.PrinterSettings.PrintRange == PrintRange.SomePages)
{
_pageNumber = _doc.PrinterSettings.FromPage;
_maxPageNumber = _doc.PrinterSettings.ToPage;
}
else
{
_pageNumber = 1;
_maxPageNumber = TOTAL_PAGES;
}
_doc.Print();
}
}

void _doc_PrintPage(object sender, PrintPageEventArgs e)
{
e.Graphics.DrawString("Page " + _pageNumber.ToString(), new
Font("Verdana", 35), Brushes.Black, new Point(10, 10));
_pageNumber++;
e.HasMorePages = _pageNumber <= _maxPageNumber;
}

Please let me know whether or not this works for you.

Sincerely,
Walter Wang ([email protected], remove 'online.')
Microsoft Online Community Support

==================================================
Get notification to my posts through email? Please refer to
http://msdn.microsoft.com/subscriptions/managednewsgroups/default.aspx#notif
ications. If you are using Outlook Express, please make sure you clear the
check box "Tools/Options/Read: Get 300 headers at a time" to see your reply
promptly.

Note: The MSDN Managed Newsgroup support offering is for non-urgent issues
where an initial response from the community or a Microsoft Support
Engineer within 1 business day is acceptable. Please note that each follow
up response may take approximately 2 business days as the support
professional working with you may need further investigation to reach the
most efficient resolution. The offering is not appropriate for situations
that require urgent, real-time or phone-based interactions or complex
project analysis and dump analysis issues. Issues of this nature are best
handled working with a dedicated Microsoft Support Engineer by contacting
Microsoft Customer Support Services (CSS) at
http://msdn.microsoft.com/subscriptions/support/default.aspx.
==================================================

This posting is provided "AS IS" with no warranties, and confers no rights.
 
G

Guest

Thank you for your response. With your solution, if I ask for pages 3-5, I
will indeed get pages numbered 3, 4 and 5. The problem is that I am printing
actual content, and I need the content from page 3, but not the content from
1 or 2. What I do not understand is how I can tell the print page command to
start at page 3. My print page routine contains this:

private void pd_PrintPage(object sender, PrintPageEventArgs ev)
{

while (count < linesPerPage &&
((line = streamToPrint.ReadLine()) != null))
{
ev.Graphics.DrawString(line, printFont, Brushes.Black,
leftMargin, yPos, new StringFormat());
}

I need a way to tell it to get the stream content for the third page, not
the first page. I could attempt to figure out where the page breaks are and
read through the whole stream to get there, but it seemed to me that since
Windows knows what is on what page, there had to be a better way? Thanks.
 
G

Guest

Thanks Walter, that is very helpful, just what I was looking for. Thanks.


Walter Wang said:
Hi,

In WinForm's printing architecture, it's the user's responsibility to do
the pagination:

1) System fires the event PrintPage
2) User process the event, prints content to the Graphics object; at the
end of the event, user need to set the flag PrintPageEventArgs.HasMorePages
to notify the system whether or not there're remaining pages.
3) System checks the flag, if it's True, go to step 1).

If user wants to print some pages, the settings are returned in the
PrinterSettings.PrintRange, FromPage, ToPage, etc. But they have nothing to
do with how the PrintPage event is raised and handled. That's why you need
to save the FromPage and ToPage as member variables and use them in the
event later.

From your code, I assume what you're doing is to print a multi-page text
file like following example:

[1] How to: Print a Multi-Page Text File in Windows Forms
http://msdn2.microsoft.com/en-us/library/cwbe712d.aspx

However, the example above doesn't allow user selectively print some pages
using AllowSomePages property.

Let's implement this feature now.

First, to let user know exactly the total pages count, we need to create a
custom PrintController which computes the page count dynamically.

class PageCountPrintController : PreviewPrintController
{
int pageCount = 0;

public override void OnStartPrint(PrintDocument document,
PrintEventArgs e)
{
base.OnStartPrint(document, e);
this.pageCount = 0;
}

public override System.Drawing.Graphics OnStartPage(PrintDocument
document, PrintPageEventArgs e)
{
// Increment page count
++this.pageCount;
return base.OnStartPage(document, e);
}

public int PageCount
{
get { return this.pageCount; }
}

// Helper method to simplify client code
public static int GetPageCount(PrintDocument document)
{
// Must have a print document to generate page count
if (document == null)
throw new ArgumentNullException("PrintDocument must be
set.");

// Substitute this PrintController to cause a Print to initiate
the
// count, which means that OnStartPrint and OnStartPage are
called
// as the PrintDocument prints
PrintController existingController = document.PrintController;
PageCountPrintController controller = new
PageCountPrintController();
document.PrintController = controller;
document.Print();
document.PrintController = existingController;
return controller.PageCount;
}
}

Also, we can use this "pre-processing" phase to get which text should be
printed on which page.

void printDocument1_PrintPage(object sender, PrintPageEventArgs e)
{

if (_isPreProcessing)
{
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);

// Save the text for the page
_pages.Add(_stringToPrint.Substring(0, charactersOnPage));
// 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);
if (!e.HasMorePages) _stringToPrint = null;
}
else
{
// Draws the string within the bounds of the page
e.Graphics.DrawString(_pages[_pageNumber-1], this.Font,
Brushes.Black,
e.MarginBounds, StringFormat.GenericTypographic);
_pageNumber++;
e.HasMorePages = _pageNumber <= _maxPageNumber;
if (!e.HasMorePages) _pages = null;
}
}

private void button1_Click(object sender, EventArgs e)
{
ReadFile();

// Get the total page count
_isPreProcessing = true;
_pages = new List<string>();
_totalPages =
PageCountPrintController.GetPageCount(_printDocument1);
_isPreProcessing = false;

_printDocument1.PrinterSettings.FromPage = 1;
_printDocument1.PrinterSettings.ToPage = _totalPages;
_printDocument1.PrinterSettings.MinimumPage = 1;
_printDocument1.PrinterSettings.MaximumPage = _totalPages;
PrintDialog pd = new PrintDialog();
pd.AllowSomePages = true;
pd.Document = _printDocument1;
if (pd.ShowDialog() == DialogResult.OK)
{
if (pd.PrinterSettings.PrintRange == PrintRange.SomePages)
{
_pageNumber = _printDocument1.PrinterSettings.FromPage;
_maxPageNumber = _printDocument1.PrinterSettings.ToPage;
}
else
{
_pageNumber = 1;
_maxPageNumber = _totalPages;
}
_printDocument1.Print();
}
}


I've created a complete working project for you to test it. It will print
"c:\testpage.txt". You can use the "Page Setup..." button to change the
margins and you will notice the total page count changes accordingly when
you click "Print..." to bring up the print dialog.

Note: you will have to use Outlook Express to download the attachment
 

Ask a Question

Want to reply to this thread or ask your own question?

You'll need to choose a username for the site, which only take a couple of moments. After that, you can post your question and our members will help you out.

Ask a Question

Top