Print a Control

A while ago i discovered the DrawToBitmap method on the Control class. The availability of this method makes it relatively easy to implement a PrintPageEventHandler for the PrintDocument class. Here is an example implementation that prints a DataGridView:

private void buttonPrint_Click(object sender, EventArgs e)
{
 this.printDocument1.Print();
}

void printDocument1_BeginPrint(object sender, PrintEventArgs e)
{
 this.currentPage = 0;
}

private void printDocument1_PrintPage(object sender, System.Drawing.Printing.PrintPageEventArgs e)
{
 Size oldSize = this.dataGridView1.Size;
 this.dataGridView1.Height = Math.Max(this.dataGridView1.Height, this.dataGridView1.PreferredSize.Height);
 this.dataGridView1.Width = Math.Max(this.dataGridView1.Width, this.dataGridView1.PreferredSize.Width);

 int requiredPagesForWidth = ((int)this.dataGridView1.Width / e.MarginBounds.Width) + 1;
 int requiredPagesForHeight = ((int)this.dataGridView1.Height / e.MarginBounds.Height) + 1;
 int requiredPages = requiredPagesForWidth * requiredPagesForHeight;
 e.HasMorePages = (this.currentPage < requiredPages - 1);

 int posX = ((int)this.currentPage % requiredPagesForWidth) * e.MarginBounds.Width;
 int posY = ((int)this.currentPage / requiredPagesForWidth) * e.MarginBounds.Height;

 Graphics graphics = e.Graphics;
 Bitmap bitmap = new Bitmap(this.dataGridView1.Width, this.dataGridView1.Height);
 this.dataGridView1.DrawToBitmap(bitmap, this.dataGridView1.Bounds);
 graphics.DrawImage(bitmap, new Rectangle(e.MarginBounds.X, e.MarginBounds.Y, e.MarginBounds.Width, e.MarginBounds.Height), new Rectangle(posX, posY, e.MarginBounds.Width, e.MarginBounds.Height), GraphicsUnit.Pixel);

 this.dataGridView1.Size = oldSize;
 ++this.currentPage;
}

Now that you understand the main idea, let’s wrap it in a class and make it reusable: ResizedControlPrintPageEventHandler. Using this class is as simple as:

// Initialise a controlPrintPageEventHandler and register the PrintPage method...
ResizedControlPrintPageEventHandler resizedControlPrintPageEventHandler = new ResizedControlPrintPageEventHandler(this.dataGridView1);
this.printDocument1.PrintPage += resizedControlPrintPageEventHandler.PrintPage;

// Print the control
private void buttonPrint_Click(object sender, EventArgs e)
{
 this.printDocument1.Print();
}

// Give the user a preview
private void buttonPreview_Click(object sender, EventArgs e)
{
 this.printPreviewDialog1.Show();
}

Here are a couple of screenshots:

Screenshot of demo application that has a datagridview with scrollbars.
Screenshot of print preview. Notice that the scrollbars are gone.
Screenshot of the print document.

Feel free to download the class and demo application: ControlPrintPageEventHandler.zip.

Leave a comment ?

20 Comments.

  1. Hi there, I got this to work in a test application quite well. I was wondering if you have seen anything like this in vb.net?

  2. Hi

    I don’t know where to use this and how. I am assuming that I have a declare a class of the same name. I tried that but your link to ControlPrintPageEventHandler does not work.

    // Initialise a controlPrintPageEventHandler and register the PrintPage method…
    ResizedControlPrintPageEventHandler resizedControlPrintPageEventHandler = new ResizedControlPrintPageEventHandler(this.dataGridView1);
    this.printDocument1.PrintPage += resizedControlPrintPageEventHandler.PrintPage;

    Please help.

    thanks,
    -Raj

  3. Noobie-Newbie

    Thanks for your help on the following thread
    http://forums.microsoft.com/MSDN/ShowPost.aspx?PostID=1630670&SiteID=1

    I have a Question to ask. If I want to print a landscape instead of Portrait, how do i do it.

    Thanks

  4. Noobie-Newbie

    I have solve the problem.
    just add pageSetupDialog

    Anyway, Once again thanks for your help

  5. Hello, I want to use the print algorithm in this article in my application, I want to confirm with you whether it is free to use. I mean it is no copyrighted code? Thanks

  6. Hello,

    All the code you find on my blog is completely free to use…

  7. Hey, is there a way to make it Landscape without a page setup dialog? I wanted to do it in the code so it would be automatically like that. I tried to set it by putting:

    printDocument1.DefaultPageSettings.Landscape = true;

    in the button click event. Then I add this document to a printpreview dialog, but it is always portrait. I hope this is enough information for you to help, let me know if need anything else from me.

    Thanks!

  8. I’m not quite sure in which direction you want to rotate.. I haven’t changed the Landscape, simply added a call to RotateFlip in the PrintPage method (You can experiment yourself with the available RotateFlipTypes)..

    public virtual void PrintPage(object sender, PrintPageEventArgs e)
    {
     if (this.currentPage == 0)
     {
      // the only addition was the call to RotateFlip
      this.bitmap = new Bitmap(this.control.Width, this.control.Height);
      this.control.DrawToBitmap(this.bitmap, this.control.Bounds);
      this.bitmap.RotateFlip(RotateFlipType.Rotate270FlipNone);
    
      // if i calculate it after that the image has been rotated, i don't have to change code :) )
      this.requiredPagesForWidth = ((int)this.bitmap.Width / e.MarginBounds.Width) + 1;
      this.requiredPagesForHeight = ((int)this.bitmap.Height / e.MarginBounds.Height) + 1;
      this.lastPage = (this.requiredPagesForWidth * this.requiredPagesForHeight) - 1;
     }
    
     e.HasMorePages = (this.currentPage < this.lastPage);
    
     int posX = ((int)this.currentPage % this.requiredPagesForWidth) * e.MarginBounds.Width;
     int posY = ((int)this.currentPage / this.requiredPagesForWidth) * e.MarginBounds.Height;
    
     e.Graphics.DrawImage(this.bitmap, new Rectangle(e.MarginBounds.X, e.MarginBounds.Y, e.MarginBounds.Width, e.MarginBounds.Height), new Rectangle(posX, posY, e.MarginBounds.Width, e.MarginBounds.Height), GraphicsUnit.Pixel);
    
     ++this.currentPage;
     if (this.currentPage > this.lastPage)
     {
      this.currentPage = 0;
     }
    }

    Hoping to help.

  9. Thanks, I got it all figured out on the landscape thing. Now, I have another question. I have a control with three datagrids. Is there an easy way to use these classes to print them with so much room in between so that they all appear?

    Thank you

  10. There is always a way to get things done. My first idea would be to calculate the sum of these DGV’s their width and height, create a giant bitmap containing the 3 DGV bitmaps, and print… But the implementation is up to you this time ;)

  11. Alright, thanks!

  12. Superb article.
    I have seen many codes to print the datagridview but you have solved the problem in least of the codes…
    gr8888888888888

  13. Hello i would like to ask if there is any code for datagridview to excel in vb.net!
    thanks

  14. RE: Print Control Code
    Hi Tim, I having trouble getting data in my grid to show in preview or print. No data appears in the report. I’m new at this stuff. Here is my code, Can you help me out?
    DateTime fromdt = dateTimePicker1.Value;
    DateTime todt = dateTimePicker2.Value;
    string offname = comboBox1.Text;

    var db = new dispatchDataContext();
    var q = db.SP_DispatchOfficerLogSearch(fromdt, todt, offname);
    dataGridView1.DataSource = q.ToList();
    // Alter Datagrid Heading
    dataGridView1.Columns[0].HeaderText = “Call Log Time”;
    dataGridView1.Columns[1].HeaderText = “Officer Name”;
    dataGridView1.Columns[2].HeaderText = “Description”;
    dataGridView1.Columns[3].HeaderText = “Quick Note”;
    dataGridView1.Columns[0].Width = 117;
    dataGridView1.Columns[1].Width = 170;
    dataGridView1.Columns[2].Width = 180;
    dataGridView1.Columns[3].Width = 240;
    this.comboBox1.SelectedIndex = -1;
    ResizedControlPrintPageEventHandler resizedControlPrintPageEventHandler = new ResizedControlPrintPageEventHandler(this.dataGridView1);
    this.printDocument1.PrintPage += resizedControlPrintPageEventHandler.PrintPage;

  15. I can imagine potential issues when DataBinding hasn’t completed.. In order to figure that out, you should handle the DataBindingComplete event on the DataGridView. Here is a possible solution:

    public partial class Form1 : Form
    {
     private ManualResetEvent mre;
    
     public Form1()
     {
      InitializeComponent();
      this.dataGridView1.DataBindingComplete += new DataGridViewBindingCompleteEventHandler(dataGridView1_DataBindingComplete);
     }
    
     void dataGridView1_DataBindingComplete(object sender, DataGridViewBindingCompleteEventArgs e)
     {
      this.mre.Set();
     }
    
     private void button1_Click(object sender, EventArgs e)
     {
      List<string> names = new List<string>();
      names.Add("tim");
      names.Add("jeff");
      this.mre = new ManualResetEvent(false);
      this.dataGridView1.DataSource = names;
    
      ThreadPool.QueueUserWorkItem(this.PrintAsSoonAsDataBindingHasCompleted);
     }
    
     private void PrintAsSoonAsDataBindingHasCompleted(object state)
     {
      this.mre.WaitOne();
      ResizedControlPrintPageEventHandler resizedControlPrintPageEventHandler = new ResizedControlPrintPageEventHandler(this.dataGridView1);
      this.printDocument1.PrintPage += resizedControlPrintPageEventHandler.PrintPage;
      this.printDocument1.Print();
     }
    }
  16. Naresh Jakher

    Hi,

    I am using your code in one of my application to print the datagridview and it works gr8, But when the datagridsize is more than the size of my screen it doesnt print all the columns. I have a poup window displaying the datagrid and window’s Windowstate Property = FormWindowState.Maximized.

  17. form1 does not contain a definition for current page

  18. The code that is available for download simply works as is. If you copy-pasted the code from this blog post you’ll have to add a definition for currentPage yourself.

Leave a Comment


NOTE - You can use these HTML tags and attributes:
<a href="" title=""> <abbr title=""> <acronym title=""> <b> <blockquote cite=""> <cite> <code> <del datetime=""> <em> <i> <q cite=""> <strike> <strong>

Trackbacks and Pingbacks: