0

I have a windows forms project written in C#. The main form has a TabControl on it and there is a requirement for one of the users to be able to print one of the TabPages. The form is very long and I use a vertical scroll bar. The whole of the form needs to be able to be printed.

I have tried using the DrawToBitmap method to convert to a bitmap first, but this will only include the portion of the form that the user can see. Some other solutions I have tried involve screen capturing, which has the same issue.

How can I print out, or get an image, of the whole of the tab page, including the parts the user only sees when they scroll down?

Khamill
  • 51
  • 7
Paul Richards
  • 1,181
  • 1
  • 10
  • 29
  • Not sure about your case, but in general print outs of transaction is more about the information of the transaction than about the graphical image. That said, consider generating a report with all desired information. – NoChance Jun 18 '15 at 15:30
  • @EmmadKareem this printout is full of images, as well as textual information. I am going to have to do it as a PDF report. – Paul Richards Jun 18 '15 at 15:37

1 Answers1

2

This is rather simple for any control including TabControls and TabPages but not Forms.

All you need to do is enlarge the relevant controls enough to show all their content. (They don't have to be actually visible on screen.)

Here is an example:

tabControl1.Height = 10080;
tabPage2.Height = 10050;
dataGridView1.Height = 10000;

dataGridView1.Rows.Add(3000);
for (int i = 0; i < dataGridView1.Rows.Count; i++)  dataGridView1[0, i].Value = i;

using (Bitmap bmp = new Bitmap(tabControl1.Width , tabControl1.Height ))
{
    tabControl1.DrawToBitmap(bmp, tabControl1.ClientRectangle);
    bmp.Save("D:\\xxxx.png", ImageFormat.Png);
}

This saves the full content of the DataGridView, the TabPage and the TabControl..

Note: that this will not work with forms, which can't much exceed the screen dimensions..

Update: Here is code that saves a form with vertical scrolling by patching several bitmaps together. It can, of course be expanded to include horizontal scrolling as well. I have coded a similar solution for larger Panels here.

static void saveLargeForm(Form form, string fileName)
{
    // yes it may take a while
    form.Cursor = Cursors.WaitCursor;

    // allocate target bitmap and a buffer bitmap
    Bitmap target = new Bitmap(form.DisplayRectangle.Width, form.DisplayRectangle.Height);
    Bitmap buffer = new Bitmap(form.Width, form.Height);
    // the vertical pointer
    int y = 0;
    var vsc = form.VerticalScroll;
    vsc.Value = 0;
    form.AutoScrollPosition = new Point(0, 0);
    // the scroll amount
    int l = vsc.LargeChange;

    Rectangle srcRect = ClientBounds(form);
    Rectangle destRect = Rectangle.Empty;
    bool done = false;

    // we'll draw onto the large bitmap with G
    using (Graphics G = Graphics.FromImage(target))
    {
        while (!done)
        {
            destRect = new Rectangle(0, y, srcRect.Width, srcRect.Height);
            form.DrawToBitmap(buffer, new Rectangle(0, 0, form.Width, form.Height));
            G.DrawImage(buffer, destRect, srcRect, GraphicsUnit.Pixel);   
            int v = vsc.Value;
            vsc.Value = vsc.Value + l;
            form.AutoScrollPosition = new Point(form.AutoScrollPosition.X, vsc.Value + l);
            int delta = vsc.Value - v;
            done = delta < l;
            y += delta;
        }
        destRect = new Rectangle(0, y, srcRect.Width, srcRect.Height);
        form.DrawToBitmap(buffer, new Rectangle(0, 0, form.Width, form.Height));
        G.DrawImage(buffer, destRect, srcRect, GraphicsUnit.Pixel);
    }
    // write result to disc and clean up
    target.Save(fileName, System.Drawing.Imaging.ImageFormat.Png);
    target.Dispose();   
    buffer.Dispose();      
    GC.Collect();          // not sure why, but it helped
    form.Cursor = Cursors.Default;    
}

It makes use of a helper function to determine the the net size of the virtual client rectangle, ie excluding borders, title and scrollbar:

static Rectangle ClientBounds(Form f)
{
    Rectangle rc = f.ClientRectangle;
    Rectangle rb = f.Bounds;
    int sw = SystemInformation.VerticalScrollBarWidth;
    var vsc = f.VerticalScroll;
    int bw = (rb.Width - rc.Width - (vsc.Visible ? sw : 0) ) / 2;
    int th = (rb.Height - rc.Height) - bw * 2;
    return new Rectangle(bw, th + bw, rc.Width, rc.Height );
}
Community
  • 1
  • 1
TaW
  • 53,122
  • 8
  • 69
  • 111
  • thanks but this didn't work for me. The height of the tab control and tab page won't change. Even when I assign them in the code `Height =` if you put a breakpoint in afterwards you can see the assignment has had no effect. The image which results is still truncated at the bottom of the visible screen area. – Paul Richards Jun 19 '15 at 07:47
  • That sounds very strange. I works fine here. Where do you call the code from? - The other option I know of involves patching pieces of scrolled parts together. This maens tha you need to control the scolling precisely.. – TaW Jun 19 '15 at 08:54
  • See my update for a solution, that includes the full (vertically scrolled) display height of the form! – TaW Jun 20 '15 at 07:58
  • I get an exception when I run this `An unhandled exception of type 'System.ArgumentOutOfRangeException' occurred in System.Windows.Forms.dll Additional information: Value of '110' is not valid for 'Value'. 'Value' should be between 'minimum' and 'maximum'.` The problem is in the line `vsc.Value = vsc.Value + l` – Paul Richards Jun 22 '15 at 12:27
  • Hm, can you tell me the sizes involved? Where/when did you call the code? May you should change `vsc.Value = vsc.Value + l;` to `vsc.Value = Math.Min(vsc.Maximum, vsc.Value + l);`.. – TaW Jun 22 '15 at 13:10
  • thanks, I've tried this out in a "hello world" example and now it doesn't error - I'll test it in my production code tomorrow and see if it works :) – Paul Richards Jun 22 '15 at 15:58
  • 1
    It works, thanks! I have made some small changes. 1) The edit @TaW suggested in the above comment. 2) The methods can taker any ScrollableControl as a parameter, it does not have to be a Form. 3) I cache the scroll position at the start of the code and reset it to that at the end. – Paul Richards Jun 23 '15 at 08:14