Question

How to tell if a Windows Form has painted itself COMPLETELY

Asked by: shifty_mc

Hi,

I'm trying to capture the screen of a Form which contains a browser, which works fine if I do it manually (say once you press a button or something).

The problem is how to do this programatically as the browser is opening a web page which could take a little while, and the DownloadComplete event seems to fire too early (ie it just captures a blank screen - presumably a millisecond or so before the screen properly paints.

I've tried other events, both in the form and the contained browser - none of which seem to be fired at just the right moment when the screen displays the information. Thread.Sleep() doesn't work either as it delays everything.

Is there an event I've missed which will go at the right time? Or a workaround using threads?

I've also looked at intercepting WM_PAINT messages, but couldn't get it working.

Any ideas? Cheers.

This Question has been solved and asker verified All Experts Exchange premium technology solutions are available to subscription members.

Subscribe now for full access to Experts Exchange and get

Instant Access to this Solution

  • Plus...
  • 30 Day FREE access, no risk, no obligation
  • Collaborate with the world's top tech experts
  • Unlimited access to our exclusive solution database
  • Never be left without tech help again

Subscribe Now

Asked On
2004-03-31 at 19:08:03ID20939542
Topic

C# Programming Language

Participating Experts
4
Points
200
Comments
21

Trusted by hundreds of thousands everyday for fast, accurate and reliable tech support.

  • "The time we save is the biggest benefit of Experts Exchange to Warner Bros. What could take multiple guys 2 hours or more each to find is accessed in around 15 minutes on Experts Exchange." Mike Kapnisakis, Warner Bros.
  • "Our team likes having a resource that is more secure than just using Google and most experts using this service really know their stuff. It's nice to look here first versus using Google." Dayna Sellner, Lockheed Martin
  • "Anytime that I've been stumped with a problem, 9 out of 10 times Experts Exchange has either the accepted solution or an open discussion of the potential solution to the problem." Kenny Red, eBay Inc.

See what Experts Exchange can do for you.

Got a question?

We've got the answer.

Experts Exchange has been collecting answers to technology questions since 1996…3 million and counting! If you have a question, chances are we already have your answer.

Screenshot of Experts Exchange Knowledgebase

Need individual assistance?

Our experts are ready to help.

If you can't find the exact answer you're looking for, ask our exclusive community of 50,000 experts. You’ll get a personalized answer from a trusted professional.

Screenshot of Experts Exchange Knowledgebase

Want to learn from the best?

Read articles from industry experts.

Thousands of free tech tips, tricks, how-to’s and tutorials are available in our peer reviewed articles section. See for yourself how smart our experts are, no login required.

Screenshot of an Article

Working on a long term project?

Store your work and research.

Save solutions to your questions, answers you’ve discovered through searching plus helpful articles in your personal knowledgebase for easy future access.

Screenshot of Experts Exchange Knowledgebase

Access the answers to your technology questions today.

Subscribe Now

30-day free trial. Register in 60 seconds.

What Makes Experts Exchange Unique?

Members of the expert community talk about why the experience at Experts Exchange is different than what you will find anywhere else.

Trusted by the world's most respected brands.

image of each brand's logo

Faithfully serving IT professionals since 1996.

Experts Exchange Logo

Try it out and discover for yourself.

Subscribe Now

30-day free trial. Register in 60 seconds.

Related Solutions

  1. WM_PAINT in modeless dialog boxes question
    How do I keep my modeless dialog box from repainting itself? I need to keep it from repainting during a move operation and I'm setting a flag during WM_ENTERSIZEMOVE and clearing it during WM_EXITSIZEMOVE but there seems to be no way for me to intercept WM_PAINT's. I've tried...
  2. WM_PAINT
    How can I get WM_PAINT to work? I was trying a little experiment that didn't quite work... can anyone shed any light? void CExpDlg::OnOK() { HDC mydc = ::GetDC(this->m_hWnd); ::LineTo(mydc,100,100); HWND hw = ::FindWindow(NULL,"My Computer"); if(hw == NU...
  3. Window painting
    How will I know window (some one on desktop) is painting or now window isn't painted ??? Please explain and I want some source code. Sorry I'm beginner in english. Please tell me if tou don't understand my question.
  4. Paint a TWinControl
    Is it possible to paint a TWinControl which is hidden ? I have a form (or Panel) with some WinControls. I would like to go through all Controls and paint them in a special Panel. But if the WinControls (or its Parent) is hidden the PaintTo doesn't work. (For Edits i get only ...

Free Tech Articles

  1. WARNING: 5 Reasons why you should NEVER fix a computer for free.
    It is in our nature to love the puzzle. We are obsessed. The lot of us. We love puzzles. We love the challenge. We thrive on finding the answer. We hate disarray. It bothers us deep in our soul. W...
  2. SCCM OSD Basic troubleshooting
    SCCM 2007 OSD is a fantastic way to deploy operating systems, however, like most things SCCM issues can sometimes be difficult to resolve due to the sheer volume of logs to sift through and the dispe...
  3. Migrate Small Business Server 2003 to Exchange 2010 and Windows 2008 R2
    This guide is intended to provide step by step instructions on how to migrate from Small Business Server 2003 to Windows 2008 R2 with Exchange 2010. For this migration to work you will need the fo...
  4. Create a Win7 Gadget
    This article shows you how to create a simple "Gadget" -- a sort of mini-application supported by Windows 7 and Vista. Gadgets can be dropped anywhere on the desktop to provide instant information, ...
  5. Outlook continually prompting for username and password
    There have been a lot of questions recently regarding Outlook prompting for a username and password whilst using Exchange 2007. There are a few reasons why this would happen and I will try to cover t...
  6. Backup Exchange 2010 Information Store using Windows Backup
    There seems to be quite a lot of confusion around the ability to backup Exchange 2010 using the built in Windows Backup feature. This stems from the omission of this feature prior to Exchange 2007 s...

Cloud Class Webinars

  1. Avoiding Bugs in Microsoft Access
    Alison Balter takes and in-depth look at avoiding bugs in Access. In this webinar you will learn about using the immediate window to debug your applications, invoking the debugger, using breakpoints to troubleshoot, stepping through code, setting the next statement to execute, ...
  2. Top 10 Best New Features in Visio 2010
    Scott Helmers gives live demonstrations of the top 10 new features in Visio 2010. This webinar will teach you how to create compelling diagrams by adding shapes to the page with a single click, linking the shapes in a diagram to data in Excel (or SQL Server, or SharePoint), ...
  3. IT Consultant Business Secrets Revealed
    Michael Munger, Experts Exchange tech pro and IT consultant, pulls back the curtain on his very successful businesses and answers question on every IT consultant and business owner should know about. He shares secrets on what he did to solve the 5 most common problems in IT, ...
  4. Disaster Recovery and Business Continuity
    Quest CTO, Mike Billon, gives an overview of the steps involved in building a dunamic disaster recovery plan. Through case studies and an examination of software/hardware tooles for monitoring and testing, you'll gain a better understandin of where you are, where you want ...
  5. Organize Your Visio Diagrams with Containers and Lists
    Scott Helmers uses cross functional flowcharts, wireframe diagrams, data graphic legends and seating charts to teach you: how to ustilize all three new structured diagram components in Visio 2010, the best practices for organizeing shapes in previous version of Visio, how to organize ...
  6. How to Us Objects, Properties, Events and Methods in Microsoft Access
    Alison Dalter gives an in-depbth look at objects, properties, events and methods in Microsoft Access. In this webinar you will learn about using the object browser, referring to objects, working with properties and methods, working with object variables, understanding the ...

Join the Community

Give a Little. Get a Lot.

Join the community of experts here and help other tech pros by answering question in your area of expertise. You can earn FREE access to all Experts Exchange's premium features and resources.

Join the Community

Answers

 

by: eternal_21Posted on 2004-03-31 at 19:10:51ID: 10729295

What browser component are you using?

 

by: shifty_mcPosted on 2004-03-31 at 19:27:23ID: 10729348

Ummm...

private AxSHDocVw.AxWebBrowser axWebBrowser1;

that answer your question? It's just the 'Microsoft Web Browser' that you can add to the toolbox in Visual Studio.

 

by: eternal_21Posted on 2004-03-31 at 22:26:56ID: 10729931

Instead of using the Navigate method, and the DownloadComplete event, try using the Navigate2 method and the NavigateComplete2 event:

// Code to opent the website:

  object o = new object();
  object url = "http://www.yahoo.com/";
  this.axWebBrowser1.NavigateComplete2 += new AxSHDocVw.DWebBrowserEvents2_NavigateComplete2EventHandler(this.axWebBrowser1_NavigateComplete2);
  this.axWebBrowser1.Navigate2(ref url, ref o, ref o, ref o, ref o);

// NavigateComplete2 Event Handler:

  private void axWebBrowser1_NavigateComplete2(object sender, AxSHDocVw.DWebBrowserEvents2_NavigateComplete2Event e)
  {
    System.Threading.Thread.Sleep(250);
    Application.DoEvents();
    // Get your screen shot here
  }

 

by: shifty_mcPosted on 2004-04-01 at 05:12:54ID: 10731661

Ok, I changed from Navigate to Navigate2 and DownloadComplete to NavigateComplete2 but still no joy :-(

If it helps, I've overriden wnproc in order to log the events, and this is the result (the messages are ints - I've just spent a little time looking to find out which ones are which with no luck - I'll post this and carry on - don't know if I'm looking in all the wrong places- they must be out there somewhere!)

Loading Browser                                       (in Form_Load)
Navigating to file://C:\webpagetest.html    (still in Form_Load and calling Navigate2() method)
Download complete event firing!
Event firing: 24
Event firing: 70
Event firing: 70
Event firing: 28
Event firing: 134
Event firing: 6
Event firing: 133
Event firing: 20
Event firing: 71
Event firing: 5
Event firing: 3
Event firing: 127
Event firing: 127
Event firing: 127
NavigateComplete2 Event Firing!!
Sleeping Thread
Back from sleeping thread
Calling Application.DoEvents()
Event firing: 15
Back from Application.DoEvents()
Calling capturescreen method
Calling SendMessage
Event firing: 791
Event firing: 175
Event firing: 20
Event firing: 792
Back from SendMessage
Saving... test.bmp
Back from capturescreen method
Event firing: 132
Event firing: 132
Event firing: 32
Event firing: 49721
Event firing: 512
Event firing: 675
Event firing: 134
Event firing: 6
Event firing: 28
Event firing: 70
Event firing: 28
Event firing: 134
Event firing: 6
Event firing: 134
Event firing: 6
Event firing: 28

The Thread.Sleep() bit you mentioned still holds up everything as it did with DownloadComplete. The NavigateComplete2 is fired later than DownloadComplete as you can see, which is good.  Also, the capture screen method works by using the line
SendMessage(hWnd, Win32.WM_PRINT, hDCMem, new IntPtr(Win32.PRF_CHILDREN | Win32.PRF_CLIENT | Win32.PRF_ERASEBKGND | Win32.PRF_NONCLIENT | Win32.PRF_OWNED));
which is obviously creating a PRINT event - so don't know if this is messing things up.

Thanks

 

by: eternal_21Posted on 2004-04-01 at 07:06:48ID: 10732612

I *did* test this code, at first it didn't work.  Then I added the Application.DoEvents() after the call to Thread.Sleep.  It worked fine for me.

Try this for your form capture?

    [System.Runtime.InteropServices.DllImport("gdi32.dll")] private static extern bool BitBlt(IntPtr hdcDest, int nXDest, int nYDest, int nWidth, int nHeight, IntPtr hdcSrc, int nXSrc, int nYSrc, System.Int32 dwRop);

    static Bitmap CaptureForm(System.Windows.Forms.Form form)
    {
      Graphics g1 = form.CreateGraphics();
      Bitmap bitmap = new Bitmap(form.ClientRectangle.Width, form.ClientRectangle.Height, g1);
      Graphics g2 = Graphics.FromImage(bitmap);
      IntPtr dc1 = g1.GetHdc();
      IntPtr dc2 = g2.GetHdc();
      BitBlt(dc2, 0, 0, form.ClientRectangle.Width, form.ClientRectangle.Height, dc1, 0, 0, 13369376);
      g1.ReleaseHdc(dc1);
      g2.ReleaseHdc(dc2);
      return bitmap;
    }

And here was my code:

  private void Form1_Load(object sender, System.EventArgs e)
  {
    object o = new object();
    object url = "http://www.yahoo.com/";
    this.axWebBrowser1.NavigateComplete2 += new AxSHDocVw.DWebBrowserEvents2_NavigateComplete2EventHandler(this.axWebBrowser1_NavigateComplete2);
    this.axWebBrowser1.Navigate2(ref url, ref o, ref o, ref o, ref o);
  }

  void axWebBrowser1_NavigateComplete2(object sender, AxSHDocVw.DWebBrowserEvents2_NavigateComplete2Event e)
  {
    System.Threading.Thread.Sleep(250);
    Application.DoEvents();
    System.Drawing.Bitmap bitmap;
    bitmap = CaptureForm(this);
    bitmap.Save("c:\\test.bmp", System.Drawing.Imaging.ImageFormat.Bmp);
  }

 

by: eternal_21Posted on 2004-04-01 at 07:08:07ID: 10732620

By the way, how did you log the form events using WndProc?  (Just curious, I have been trying to do that for a while).

 

by: eternal_21Posted on 2004-04-01 at 07:27:21ID: 10732763

Also, it looks like "Event 15" is significant, don't you think?

 

by: eternal_21Posted on 2004-04-01 at 07:27:48ID: 10732768

Can you post that trace again WITHOUT the DoEvents, and see where that Event 15 is sitting?

 

by: shifty_mcPosted on 2004-04-01 at 07:39:47ID: 10732870

yeah, I saw that but couldn't work out what it was, hold on a minute - I've just tried what you said and am organising in order to post back....

 

by: shifty_mcPosted on 2004-04-01 at 07:52:39ID: 10732966

Right,

Unfortunately I can't use BitBlt because I'm capturing a screen that might not be visible, so I have to use SendMessage (as I said before I know it works because I've tried implementing it by clicking a button when the form is half off the screen and it's fine).
The whole code for the method I use is...

public Bitmap GrabWindow(IntPtr hWnd)
{
      Class1.Log("Capturing Browser Screen...");
      IntPtr hBmp;
      Bitmap MyBitmap=null;

      IntPtr hDCMem = Win32.CreateCompatibleDC((IntPtr)null);
      Rectangle rect = new Rectangle(this.Left,this.Top, this.Width,this.Height);
      IntPtr hDC = Win32.GetWindowDC(hWnd);
      hBmp = Win32.CreateCompatibleBitmap(hDC, rect.Width, rect.Height);
      Win32.ReleaseDC(hWnd, hDC);
      Graphics grfxScreen = Graphics.FromHwnd(hWnd);
      MyBitmap = new Bitmap((int) this.Width, (int) this.Height, grfxScreen);
      Graphics grfxBitmap = Graphics.FromImage(MyBitmap);
      IntPtr hdcScreen = grfxScreen.GetHdc();
      IntPtr hdcBitmap = grfxBitmap.GetHdc();
      IntPtr hOld = Win32.SelectObject(hDCMem, hBmp);
      Class1.Log("Calling SendMessage");
      Win32.SendMessage(hWnd, Win32.WM_PRINT, hDCMem, new IntPtr(Win32.PRF_CHILDREN | Win32.PRF_CLIENT | Win32.PRF_ERASEBKGND | Win32.PRF_NONCLIENT | Win32.PRF_OWNED));
      Class1.Log("Back from SendMessage");
      Win32.SelectObject(hDCMem, hOld);
      Win32.DeleteObject(hDCMem);
      MyBitmap = Bitmap.FromHbitmap(hBmp);

      Class1.Log("Saving... test.bmp");                  
      MyBitmap.Save("C:\\test.bmp");
      return MyBitmap;                  
}

(The Win32 class is just where I stored all the dllimport stuff)
I realise there's a lot of calls using these dllimports, but I've tried it with c# graphics and just couldn't get it to work - no idea why - it just returned a solid black bitmap.

Other than that the code's pretty identical. The Class1.Log stuff is just a quick method I created to write anything I want to my log.txt file, and to log the events I just used...

protected override void WndProc(ref Message m)
{
      Class1.Log("Event firing: " + m.Msg);
      base.WndProc(ref m);
}

And finally, I just ran it without the DoEvents and got the log...

Loading Browser
Navigating to file://C:\webpagetest.html
Event firing: 24
Event firing: 70
Event firing: 70
Event firing: 28
Event firing: 134
Event firing: 6
Event firing: 133
Event firing: 20
Event firing: 71
Event firing: 5
Event firing: 3
Event firing: 127
Event firing: 127
Event firing: 127
NavigateComplete2 Event Firing!!
Sleeping Thread
Back from sleeping thread
Calling capturescreen method
Capturing Browser Screen...
Calling SendMessage
Event firing: 791
Event firing: 175
Event firing: 20
Event firing: 792
Back from SendMessage
Saving... test.bmp
Back from capturescreen method
Event firing: 15
Event firing: 134
Event firing: 6
Event firing: 28
Event firing: 70
Event firing: 28
Event firing: 134
Event firing: 6
Event firing: 134
Event firing: 6
Event firing: 28
Event firing: 70
Event firing: 133
Event firing: 20
Event firing: 71
Event firing: 28
Event firing: 134
Event firing: 6
Event firing: 15
Event firing: 132
Event firing: 132
Event firing: 32
Event firing: 49721
Event firing: 512
Event firing: 675
Event firing: 132
Event firing: 132
Event firing: 32
Event firing: 49721
Event firing: 512
Event firing: 675
Event firing: 134
Event firing: 6
Event firing: 28
Event firing: 70
Event firing: 28
Event firing: 134
Event firing: 6
Event firing: 134
Event firing: 6
Event firing: 28

 

by: shifty_mcPosted on 2004-04-01 at 07:55:04ID: 10732981

Oh, and I call it from NavigateComplete2 using
image = GrabWindow(this.Handle);

 

by: shifty_mcPosted on 2004-04-01 at 08:08:23ID: 10733105

I believe the 15 event is WM_PAINT, but I'm not exactly sure what that means - surely it should work as in the original log post which includes the DoEvents, because it paints before it captures the screen?

Phew, I don't know. This is a long mission!

 

by: shifty_mcPosted on 2004-04-01 at 08:12:43ID: 10733141

I found a webpage that says there are some problems with NavigateComplete2 firing too early, and explains how to fix it in C++ using BEGIN_DISPATCH_MAP, but I have no idea how this would work in C#, or indeed if this is the actual problem, but here it is anyway...
http://www.codeguru.com/Cpp/I-N/ieprogram/comments.php/c4403/?thread=52026

 

by: eternal_21Posted on 2004-04-01 at 12:17:02ID: 10735408

I think there are other "events" going on when we call Application.DoEvents that are not generating Windows messages. I tried creating a WebBrowser control (inheriting from AxSHDocVw.AxWebBrowser), and monitoring WndProc calls, but was unable to come up with anything consistent.  Any messages related to paint are sent multiple time for a given refresh of a web page - there is no "Totally Painted" message.

 

by: eternal_21Posted on 2004-04-01 at 12:25:56ID: 10735494

I have also found that this method does not work very well (at least on my system) with the AxWebBrowser control.

What is it exactly you are trying to do, generate images of web pages?

 

by: shifty_mcPosted on 2004-04-01 at 13:53:31ID: 10736292

Yeah, that's exactly it - generate an image of a webpage.

Ok, I've finally got (after 3 solid days out of my life) what I think is a feasible workaround. It's actually extremely simple - I swear I've gone back more times than I've gone forwards, but this is all a learning process for me so I guess it's good in the long run.

The answer is a combination of the events DocumentComplete and Application.Idle.

When DocumentComplete fires and returns the url that you initially specified (it might fire a few times) you can capture the screen. The advantage of this over NavigateComplete2 is that the final DocumentComplete returns the originally specified url, so you know what to check for, whereas this url is returned in the first NavigateComplete2 (assuming these events are fired a few times - which happened quite often to me with more complicated sites).

And then to combat the delay problem - ie if you tried to capture straightaway, there is a lag between the event firing and the screen being suitable for capture - the Application.Idle event can be used. I only create this when the final DocumentComplete is received, and then I can use...

private void OnIdle( object sender, System.EventArgs e)
{
      CountIdles++;
      if (CountIdles==10)
            image = GrabWindow();
}

This basically just waits a little bit, which I can do because the event is called regularly, not just once.
And finally I got the result I wanted. It's still not perfect, and as I said - the code is horrible, but I don't care any more!

If anyone comes up with a better solution, feel free to point me in the right direction.

eternal_21 - thanks for your help, I'll award the points to you for your time,

Cheers.

 

by: JMoon5FTMPosted on 2004-04-01 at 18:26:57ID: 10737738

Glad you got everything to work out!

For future reference, this site defines the values of many window messages:

http://cvs.sourceforge.net/viewcvs.py/nsis/NSIS/Include/WinMessages.nsh?rev=1.2

So, 15 (0xF - the table is in hex) is indeed WM_PAINT.

:)

 

by: carlblanchardPosted on 2004-05-11 at 03:08:45ID: 11038860

shifty_mc, Hi it looks like youve done just what im trying to do,

Save a webpage as a single image on the harddrive, being in mind webpages have different heights, therefore being able to capture the entire page and not just visable window...

I was wondering if you could post the code for your WIN32 class, as im having trouble in working certain elemements of your code out.

Look forward to hearing from you shortly


Carl

 

by: derKromePosted on 2004-05-20 at 13:16:49ID: 11121302

shifty, if you could post tips on the Win32 class you're using, it would greatly help me out as well.  

you can email me too: derkrome AT yahoo DOT com

cheers!

 

by: derKromePosted on 2004-05-20 at 18:49:01ID: 11123289

After some searching, I found a good Win32 .net library that exposes all of the needed defines & functions (thecodeproject.com/csharp/Win32.asp).

My only difficulty now is that some elements inside the web-control are not being grabbed.  For example, an html file is fine, but if you navigate to an Office file, the capture is all white.

ideas?

 

by: shifty_mcPosted on 2004-05-21 at 07:33:06ID: 11127047

Hi,

Sorry I didn't reply before - just wasn't checking my emails properly.

I think the class you found was the exact one I was using - I'm not that experienced a programmer and a lot of my code was amalgamations of other people's.

I'm afraid I only got to where you are now and stopped - I didn't need it for any other purpose other than capturing html.  I believe the problem is because this method isn't the greatest in terms of functionality - it just 'happens to work', if you see what I mean, and doesn't actually deal with the ins and outs. In other words I don't think there's an easy answer, but then I don't know for sure,and people were telling me for ages you couldn't do it with html, so who knows.

In my travels I found a very thorough article on this exact issue on codeproject.com (in fact the only article I found in about a month of doing this and it was published about 2 days after I'd got mine working - typicle).  It's in C++ and details how to do this 'properly' (which appears to be a very big issue indeed - I certainly couldn't understand it (but admittedly didn't look too much into it). However, I don't know if it would be of any use to the specific issue of opening office documents.

http://www.codeproject.com/internet/htmlimagecapture.asp

Hope that helps, good luck

20120131-EE-VQP-002

3 Ways to Join

30-Day Free Trial

The Experts

98% positive feedback on 31,087 answers since March 2000. angeliii is a Microsoft Most Valuable Professional for his work with MS SQL Server & Develoment.

He has also proven his knowledge of Visual Basic Programming, PHP Scripting and Oracle Databases.

The Experts

97% positive feedback on 10,752 answers since July 2000. lrmoore has more than 18 years experience in the networking industry.

The six-time Mircosoft MVPs specialties include firewalls, virtual private networking, and network management.

Testimonials

"...and excellent source for support... Kind of like having your very own IT dept." Electriciansnet

Testimonials

"I was apprehensive at signing up at first. However... it has already made my life as an IT administrator much easier." JaCrews

Testimonials

"WOW! You guys have great, active, and knowledgeable people on here." moore50

Business Clients

Business Clients

In the Press

"If you’ve got a question... Experts Exchange can supply an answer.”

In the Press

"...an invaluable aid for both IT professionals and those who require tech support."

In the Press

"where IT professionals provide quick answers on just about any topic"

Business Account Plans

Loading Advertisement...