Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

printDialog page range not working with DataGridView

I have searched EXTENSIVELY on a solution to this issue but have not been able to find anything that can be used with my current code.

I have a form application that performs a query against a SQL Server table and populates a dataGridView with the rows. I then have a button to print the dataGridView to a printer. I've gotten so far as to embed an override of the printPreviewDialog print button with a printDialog, and I've also gotten the "print page range" field unblocked, but giving a page range still prints all of the pages of a dataGridView. For example if my dataGridView is going to print 20 pages, and I select pages 2-4 in the range, I still get all 20 pages.

I've seen some examples with the DocumentPaginator but I'm not using a document I'm printing directly from the dataGridView and at this point I'm stuck.

Below is all of my code for printing the dataGridView.

private void printActivityReportToolStripMenuItem_Click(object sender, EventArgs e)
        {



     PrintPreviewDialog objPPdialog = new PrintPreviewDialog();
     objPPdialog.Document = printDocument1;

     ToolStrip ts = new ToolStrip();
     ts.Name = "wrongToolStrip";
     foreach (Control ctl in objPPdialog.Controls)
     {
         if (ctl.Name.Equals("toolStrip1"))
         {
             ts = ctl as ToolStrip;
             break;
         }
     }
     ToolStripButton printButton = new ToolStripButton();
     foreach (ToolStripItem tsi in ts.Items)
     {
         if (tsi.Name.Equals("printToolStripButton"))
         {
             printButton = tsi as ToolStripButton;
         }
     }

     printButton.Click += new EventHandler(this.SelectPrinterAfterPreview);

     ts.Items.Remove(printButton);
     ToolStripButton b = new ToolStripButton();
     b.ImageIndex = printButton.ImageIndex;
     b.Visible = true;
     ts.Items.Insert(0, b);
     b.Click += new EventHandler(this.SelectPrinterAfterPreview);

     printDocument1.DefaultPageSettings.Landscape = true;
     //((ToolStripButton)((ToolStrip)objPPdialog.Controls[1]).Items[0]).
     objPPdialog.ShowDialog();

    }

    private void SelectPrinterAfterPreview(object sender, EventArgs e)
    {
        PrintDialog printDialog = new PrintDialog();
        printDialog.Document = printDocument1;
        printDocument1.DefaultPageSettings.Landscape = true;

        printDialog.AllowSomePages = true;
        printDialog.UseEXDialog = true;
        if (DialogResult.OK == printDialog.ShowDialog())
        {
            printDocument1.DocumentName = "Activity Report";
            printDocument1.Print();
        }
    }

    private void printDocument1_BeginPrint(object sender, System.Drawing.Printing.PrintEventArgs e)
    {


        try
        {
            strFormat = new StringFormat();
            strFormat.Alignment = StringAlignment.Near;
            strFormat.LineAlignment = StringAlignment.Center;
            strFormat.Trimming = StringTrimming.EllipsisCharacter;

            arrColumnLefts.Clear();
            arrColumnWidths.Clear();
            iCellHeight = 0;
            iRow = 0;
            bFirstPage = true;
            bNewPage = true;

            // Calculating Total Widths
            iTotalWidth = 0;
            foreach (DataGridViewColumn dgvGridCol in dataGridView1.Columns)
            {
                iTotalWidth += dgvGridCol.Width;
            }
        }
        catch (Exception ex)
        {
            MessageBox.Show(ex.Message, "Error", MessageBoxButtons.OK, MessageBoxIcon.Error);
        }
    }

    private void printDocument1_PrintPage(object sender,
System.Drawing.Printing.PrintPageEventArgs e)
    {
        try
        {
            //Set the left margin
            int iLeftMargin = e.MarginBounds.Left;
            //Set the top margin
            int iTopMargin = e.MarginBounds.Top;
            //Whether more pages have to print or not
            bool bMorePagesToPrint = false;
            int iTmpWidth = 0;

            //For the first page to print set the cell width and header height
            if (bFirstPage)
            {
                foreach (DataGridViewColumn GridCol in dataGridView1.Columns)
                {
                    iTmpWidth = (int)(Math.Floor((double)((double)GridCol.Width /
                        (double)iTotalWidth * (double)iTotalWidth *
                        ((double)e.MarginBounds.Width / (double)iTotalWidth))));

                    iHeaderHeight = (int)(e.Graphics.MeasureString(GridCol.HeaderText,
                        GridCol.InheritedStyle.Font, iTmpWidth).Height) + 11;

                    // Save width and height of headers
                    arrColumnLefts.Add(iLeftMargin);
                    arrColumnWidths.Add(iTmpWidth);
                    iLeftMargin += iTmpWidth;
                }
            }
            //Loop till all the grid rows not get printed
            while (iRow <= dataGridView1.Rows.Count - 1)
            {
                DataGridViewRow GridRow = dataGridView1.Rows[iRow];
                //Set the cell height
                iCellHeight = GridRow.Height;
                int iCount = 0;
                //Check whether the current page settings allows more rows to print
                if (iTopMargin + iCellHeight >= e.MarginBounds.Height + e.MarginBounds.Top)
                {
                    bNewPage = true;
                    bFirstPage = false;
                    bMorePagesToPrint = true;
                    break;
                }
                else
                {
                    if (bNewPage)
                    {
                        //Draw Header
                        e.Graphics.DrawString("Activity Report for "+txtBoxUsername.Text+"",
                            new Font(dataGridView1.Font, FontStyle.Bold),
                            Brushes.Black, e.MarginBounds.Left,
                            e.MarginBounds.Top - e.Graphics.MeasureString("Activity Report for " + txtBoxUsername.Text + "",
                            new Font(dataGridView1.Font, FontStyle.Bold),
                            e.MarginBounds.Width).Height - 13);

                        String strDate = DateTime.Now.ToLongDateString() + " " +
                            DateTime.Now.ToShortTimeString();
                        //Draw Date
                        e.Graphics.DrawString(strDate,
                            new Font(dataGridView1.Font, FontStyle.Bold), Brushes.Black,
                            e.MarginBounds.Left +
                            (e.MarginBounds.Width - e.Graphics.MeasureString(strDate,
                            new Font(dataGridView1.Font, FontStyle.Bold),
                            e.MarginBounds.Width).Width),
                            e.MarginBounds.Top - e.Graphics.MeasureString("Activity Report for " + txtBoxUsername.Text + "",
                            new Font(new Font(dataGridView1.Font, FontStyle.Bold),
                            FontStyle.Bold), e.MarginBounds.Width).Height - 13);

                        //Draw Columns                 
                        iTopMargin = e.MarginBounds.Top;
                        foreach (DataGridViewColumn GridCol in dataGridView1.Columns)
                        {
                            e.Graphics.FillRectangle(new SolidBrush(Color.LightGray),
                                new Rectangle((int)arrColumnLefts[iCount], iTopMargin,
                                (int)arrColumnWidths[iCount], iHeaderHeight));

                            e.Graphics.DrawRectangle(Pens.Black,
                                new Rectangle((int)arrColumnLefts[iCount], iTopMargin,
                                (int)arrColumnWidths[iCount], iHeaderHeight));

                            e.Graphics.DrawString(GridCol.HeaderText,
                                GridCol.InheritedStyle.Font,
                                new SolidBrush(GridCol.InheritedStyle.ForeColor),
                                new RectangleF((int)arrColumnLefts[iCount], iTopMargin,
                                (int)arrColumnWidths[iCount], iHeaderHeight), strFormat);
                            iCount++;
                        }
                        bNewPage = false;
                        iTopMargin += iHeaderHeight;
                    }
                    iCount = 0;
                    //Draw Columns Contents                
                    foreach (DataGridViewCell Cel in GridRow.Cells)
                    {
                        if (Cel.Value != null)
                        {
                            Font font = new Font("Arial", 7);
                            e.Graphics.DrawString(Cel.Value.ToString(),font,new SolidBrush(Cel.InheritedStyle.ForeColor),new RectangleF((int)arrColumnLefts[iCount],(float)iTopMargin,(int)arrColumnWidths[iCount], (float)iCellHeight),strFormat);
                        }
                        //Drawing Cells Borders 
                        e.Graphics.DrawRectangle(Pens.Black,
                            new Rectangle((int)arrColumnLefts[iCount], iTopMargin,
                            (int)arrColumnWidths[iCount], iCellHeight));
                        iCount++;
                    }
                }
                iRow++;
                iTopMargin += iCellHeight;
            }
            //If more lines exist, print another page.
            if (bMorePagesToPrint)
                e.HasMorePages = true;
            else
                e.HasMorePages = false;
        }
        catch (Exception exc)
        {
            MessageBox.Show(exc.Message, "Error", MessageBoxButtons.OK,
               MessageBoxIcon.Error);
        }
    }
like image 914
Christopher Bruce Avatar asked May 07 '14 18:05

Christopher Bruce


2 Answers

You are setting a fairly inflexible limitation by insisting a solution works with your existing code. Which was written completely without considering starting from a page other than 1. Not actually that hard to fix, you just have to figure out at what row in the datagrid you start, based on the start page number.

But what you ask for is possible. You can trick your existing code by creating a new PrintDocument that simply fakes the printing device context for the pages you want to skip. It won't know any better than it is actually printing them. Add a new class to your project and paste the code shown below. Build. Drop the new component from the top of the toolbox, replacing your existing PrintDocument. Set the PageFrom and PageTo properties in your code, based on the PrintDialog.PrinterSettings.PageFrom/To property values.

using System;
using System.Windows.Forms;
using System.Drawing;
using System.Drawing.Printing;
using System.Drawing.Imaging;

class PagedPrintDocument : PrintDocument {
    public int PageFrom { get; set; }
    public int PageTo { get; set; }
    public int Page { get; private set; }

    protected override void OnBeginPrint(PrintEventArgs e) {
        Page = 0;
        if (PageTo < PageFrom) e.Cancel = true;
        base.OnBeginPrint(e);
    }

    protected override void OnPrintPage(PrintPageEventArgs e) {
        while (++Page < PageFrom) {
            // Fake the pages that need to be skipped by printing them to a Metafile
            IntPtr hDev = e.Graphics.GetHdc();
            try {
                using (var mf = new Metafile(hDev, e.PageBounds))
                using (var gr = Graphics.FromImage(mf)) {
                    var ppe = new PrintPageEventArgs(gr, e.MarginBounds, e.PageBounds, e.PageSettings);
                    base.OnPrintPage(ppe);
                }
            }
            finally {
                e.Graphics.ReleaseHdc(hDev);
            }

        }

        // Print the real page
        base.OnPrintPage(e);

        // No need to continue past PageTo
        if (PageTo > 0 && Page >= PageTo) e.HasMorePages = false;
    }
}
like image 68
Hans Passant Avatar answered Oct 12 '22 22:10

Hans Passant


You are almost there. Setting printDialog.AllowSomePages = true; only enables the page selection controls in the print dialog. The changed print range has to be used while printing the DataGridView.

Have class members to store the page range and print page count.

int fromPage=0;
int toPage=0;
int pageCount=0;

Get the changed page range after the PrintDialog is accepted.

if (DialogResult.OK == printDialog.ShowDialog())
{
    fromPage = printDialog.PrinterSettings.FromPage;
    toPage = printDialog.PrinterSettings.ToPage;
    printDocument1.DocumentName = "Activity Report";
    printDocument1.Print();
}

Use the page range in the PrintPage method

private void printDocument1_PrintPage(object sender, System.Drawing.Printing.PrintPageEventArgs e)
{
        pageCount++;
        if(pageCount >= fromPage)
        {
            // print your grid
        }
        if (bMorePagesToPrint && pageCount <= toPage)
            e.HasMorePages = true;
        else
            e.HasMorePages = false;    
}

If you want to use the page range for PrintPreviewDialog, change the PrinterSettings property of the PrintDocument before setting the Document property.

PrintPreviewDialog objPPdialog = new PrintPreviewDialog();
fromPage = 2;
toPage = 6;
objPPdialog.Document = printDocument1;
like image 28
Junaith Avatar answered Oct 12 '22 22:10

Junaith