Home > OS >  Tricky issue with nested loop printing using C# and PrintDocument for WinForms application
Tricky issue with nested loop printing using C# and PrintDocument for WinForms application

Time:12-10

I am working on a function in my C# WinForms application that lets me print warehouse pick orders to a PrintDocument, like so:

Sample pick order

I am using the following c# code to generate this:

        private void pdPick_PrintPage(object sender, PrintPageEventArgs ev) {

            pdPick.DefaultPageSettings.Landscape = true;
            pdPick.DefaultPageSettings.Margins = new Margins ( 50, 50, 50, 50 );
            pdPick.PrinterSettings.DefaultPageSettings.Margins = new Margins ( 50, 50, 50, 50 );

            float yPos;
            count = 0;
            int leftMargin = (int)ev.MarginBounds.Left;
            int rightMargin = (int)ev.MarginBounds.Right;   
            int topMargin = (int)ev.MarginBounds.Top;
            int bottomMargin = (int)ev.MarginBounds.Bottom;
            float linesPerPage;
            bool moreRecs = true;
            int lPos;

            // If this is the first page, print a report header
            if (pageCounter == 1) {
                /* PRINT A REPORT HEADER HERE */
            }
            else {      // if this is a subsequent page, print an abbreviated header
                /* PRINT A PAGE HEADER HERE */
            }
            linesPerPage = ( ev.MarginBounds.Height - yPos ) / ( printFont.GetHeight ( ev.Graphics )   3 );

            // Cycle through items
            while ( curCount < boxCount && count < linesPerPage ) {
                // PRINT ITEM DETAIL INFO FIRST...

                // Now we cycle through the pick bins for the item...

                foreach (PickItem record in pickItems) {
                                    /*
                     * 
                     * The issue is that I need to check inside this loop to see if I hit the bottom of the page, 
                     * and if so, I need to start a new page and continue printing whatever remains inside this loop.
                     * 
                     * 
                     */
                }
                
                yPos  = 10;
                count  ;
                curCount  ;
                moreRecs = count < boxItems.Count;
                if ( moreRecs ) {
                    pageCounter  ;
                    ev.HasMorePages = true;
                }
                else {
            
                    pageCounter = 1;
                    curCount = 0;
                    yPos  = headerFont.Height   10;
                    ev.HasMorePages = false;
                    return;
                }
            } 
        }

EDIT I stripped out the printing details to clarify the issue and address comments about refactoring.

The problem is this: As you can see from the code, there are two loops to the print sequence. The first loop obtains a list of products in the pick order, from which I print the title, UPC code, and total units to pick for that item. Inside that loop is another loop where I fetch the list of locations to pick the item from, so there could be multiple locations from which product needs to be retrieved to complete that line item. Now, everything works great as long as I don't go beyond one page. The challenge is this. Obviously I need to do a page break if I have more products that will fit within one page. The problem is, I need to check where I am in the page inside the second loop too, and if so, I need to start a new page and pick up where I left off. For example, with the third product in the list as shown in the included screenshot, if I print the first bin location and then run out of page room, I need to eject the page, print a new page header, then resume printing the rest of the bin locations for that product, then continue with any remaining products in the order.

From everything I can find, I can't simply do a ev.HasMorePages=true; inside that inner loop, because that would cause the entire print sequence to begin again, and that isn't what I want - it would just result in an infinite loop. I have not been able to find any examples or SO posts on printing to a PrintDocument with nested loops.

Can anyone help me understand how to resolve this?

CodePudding user response:

It won't be a tricky issue if you have the PickItem objects of each group queued for printing. In this case, all what you need to do:

  1. Print the current group and enqueue its items in a class field of type Queue<PickItem>.
  2. Loop the queue to print each item and dequeue it from the collection while the Y position remains within the MarginBounds.
  3. While drawing the groups and items, request a new page if the Y position plus the fixed/variable line height (your lines/rows calculations for the groups and items) exceeds the e.MarginBounds.Bottom value.

Example

public partial class SomeForm : Form
{
    private readonly Queue<PickItem> pickItemsQueue;
    private int pageNumber;
    private int curGroup;

    public SomeForm()
    {
        InitializeComponent();
    
        pickItemsQueue = new Queue<PickItem>();
        pdPick.DefaultPageSettings.Landscape = true;
        pdPick.DefaultPageSettings.Margins = new Margins(50, 50, 50, 50);
        pdPick.PrinterSettings.DefaultPageSettings.Margins = new Margins(50, 50, 50, 50);
    }

    private void PrintCaller()
    {
        pageNumber = 0;
        curGroup = 0;
        pickItemsQueue.Clear();
        using (var d = new PrintPreviewDialog())
        {
            d.Document = pdPick;
            d.ShowDialog(this);
        }
    }

    private void pdPick_PrintPage(object sender, PrintPageEventArgs e)
    {
        var lineHeight = (int)Font.GetHeight(e.Graphics)   10;
        int ypos = e.MarginBounds.Y;

        pageNumber  ;

        // If this is the first page, print a report header
        if (pageNumber == 1) {
            /* PRINT A REPORT HEADER HERE */
        }
        else {      
            // if this is a subsequent page, print an abbreviated header
        }

        while (curGroup < groups.Count)
        {
            if (ypos   lineHeight > e.MarginBounds.Bottom)
            {
                e.HasMorePages = true;
                return;
            }                

            if (!pickItemsQueue.Any())
            {
                // PRINT ITEM DETAIL INFO FIRST...
                // Adjust the ypos as you like...
                ypos  = lineHeight;

                // Create a new queue or add the pickItems to the current instance...
                pickItemsQueue = new Queue<PickItem>(pickItems);
            }

            while (pickItemsQueue.Count > 0)
            {
                if (ypos   lineHeight > e.MarginBounds.Bottom)
                {
                    e.HasMorePages = true;
                    return;
                }

                var item = pickItemsQueue.Dequeue();

                // Print it and update the ypos...

                ypos  = lineHeight;
            }

            curGroup  ;
        }
    }
}
  • Related