Jaime Rodriguez On Windows Phone, Windows Presentation Foundation, Silverlight and Windows 7
Quite often, I run into the “RenderTargeBitmap gave me an empty image” or “RenderTargetBitmap did not render my visual”.. Hint: this happens a lot to people putting stuff into StackPanels.
The problem is that renderTargetBitmap is working at the Visual layer to render the visual and to do the rendering, it looks at the local properties of the visual (as expected). Layout happens at a different (higher) layer in the platform but often ‘parents’ to your visual will apply offsets or transforms to your visual to position it, since the parent is setting these properties on your actual visual, these properties will be picked up by RenderTargetBitmap logic. The best (and simplest) workaround I have seen came from Adam Smith ( WPF team lead and reliable WPF know it all). It simply wraps the Visual into a VisualBrush that it then draws into a DrawingContext.
private static BitmapSource CaptureScreen(Visual target, double dpiX, double dpiY)
if (target == null)
Rect bounds = VisualTreeHelper.GetDescendantBounds(target);
RenderTargetBitmap rtb = new RenderTargetBitmap((int)(bounds.Width * dpiX / 96.0),
(int)(bounds.Height * dpiY / 96.0),
DrawingVisual dv = new DrawingVisual();
using (DrawingContext ctx = dv.RenderOpen())
VisualBrush vb = new VisualBrush(target);
ctx.DrawRectangle(vb, null, new Rect(new Point(), bounds.Size));
I like that workaround the best, and that is the one I use most of the time. I know other people who workaround this same issue by wrapping the visual they are going to RTB in a Border. This abstracts any transforms that were being applied by the original parent, and now RTB works well. I obviously don’t like this one as much since it ‘imposes’ on my Visual Trees. Still, it works, so it is a choice.
Here is a screenshot of a sample demonstrating the issue and the solutions:
The first column is the original images and buttons. They are all in a StackPanel. The top 3 items in this panels are not wrapped inside a border, the latter 3 are wrapped in a border.
The second column, shows what you would get if you just use RenderTargetBitmap. Notice that the button and second image are missing; the reason is cause the StackPanel is offsetting them and RTB is picking that up. Do notice that even with no special workaround, the ones wrapped in the border worked OK.
The third column shows all the items rendered with the workaround above. Note that it works for all items, including the ones that had border.
The source code for this small sample (including the workaround) is here. While I am into RenderTargetBitmap, a reminder to look at this KB article (and or QFE). In 3.5 SP1, we fixed a few leaks for RenderTargetBitmap, but there is still one open; it has to do with doing RenderTargetBitmap of a 3D scene that has VisualBrushes inside the scene.
It's always pain to remember how to do this. WPF may have got a DP that convert the current representation of the control to a bitmap, but not like a Freezable implemtation, insead render itselft as a bitmap forever.
Luis, thanks for the feedback. Will pass it along to product team.