I'm using code like this to make screen captures in a WPF application. In short:
var drawingvisual = new DrawingVisual();
using( DrawingContext context = drawingvisual.RenderOpen() )
{
context.DrawRectangle( new VisualBrush( fooElement ), null, new Rect( fooElementSize ) );
context.Close();
}
var r = new RenderTargetBitmap( ... );
r.Render( drawingvisual );
SaveBitmapToFile( r, .... );
The principle works just fine, when invoked on a mouse click for instance, but I'm running into problems when invoking it programmatically. The flow is:
- a background thread wants to update the UI and does so using
Application.Current.Dispatcher.Invoke
- background thread does other things (this takes only some mSec)
- background thread issues a command to same part of the UI it just updated, again using
Application.Current.Dispatcher.Invoke
This works ok on my development machine but on some user's machines this results in the saved image not containing all updates. So for instance when a TextBlock has the text 'running' and the first step above sets it to an empty string, the screen capture still displays 'running' for the TextBlock. Or if the first step consists of populating a Canvas with a bunch of elements, the screen capture just doesn't include them. Also things get worse when the element to render is hidden behind other windows etc.
Looking into this it seems the key problem is that WPF uses a separate rendering thread and only when that one has done its work the UI is really drawn and ready for capturing. Searching around yields some hits and even some 'solutions' revolving around...
Application.Current.Dispatcher.BeginInvoke( DispatcherPriority.ApplicationIdle,
new Action( () => { } ) ).Wait();
...but as far as I understand this just waits until the UI thread is idle which is no guarantee that the render thread has finished updating the UI part I want to capture? In any case, inserting this line in the background thread before the capture code makes no difference. What does seem to work, sometimes, is just blocking the background thread for 100mSec or so and then do the capture. That is only half a solution though.
Is there anything wrong in what I am doing, and what can be changed to make sure the captured image always contains the latest updates to the UI?