WinUI RenderAsync method doesn't work for making screenshots of WebView2
Description
In WinUI you can call RenderTargetBitmap.RenderAsync(element) to render a screenshot of an element to a bitmap, and then you can save that bitmap somewhere (for example, as a PNG on disk).
When there's a WebView2 in the control tree, the contents of the WebView2 are completely blank, though everything else in the image looks fine.
Version SDK: Windows App SDK 1.1.5 Runtime: 105.0.1343.33 Framework: WinUI OS: Win11
Repro Steps
Create a new empty WinUI3 app and put this code in MainWindow.xaml.cs:
public sealed partial class MainWindow : Window
{
WebView2 _wv;
public MainWindow()
{
var b = new Button { Content = new TextBlock { Text = "Do screenshot" } };
b.Click += OnScreenshotClick;
_wv = new WebView2 { Source = new Uri("https://bing.com"), Height=400, Width=400 };
var sp = new StackPanel() { Orientation = Orientation.Vertical };
sp.Children.Add(b);
sp.Children.Add(_wv);
Content = sp;
}
private async void OnScreenshotClick(object sender, RoutedEventArgs e)
{
var rootPath = Environment.GetFolderPath(Environment.SpecialFolder.MyDocuments);
await CaptureAsync(Content, Path.Combine(rootPath, "WindowContent.png"));
await CaptureAsync(_wv, Path.Combine(rootPath, "WebView.png"));
var messageDialog = new MessageDialog($"Screenshots saved to: {rootPath}");
messageDialog.Commands.Add(new UICommand("OK"));
InitializeWithWindow.Initialize(messageDialog, WindowNative.GetWindowHandle(this));
await messageDialog.ShowAsync();
}
public static async Task CaptureAsync(UIElement element, string destination)
{
var bmp = new RenderTargetBitmap();
await bmp.RenderAsync(element);
// get the view information first
var width = bmp.PixelWidth;
var height = bmp.PixelHeight;
// then potentially move to a different thread
var pixels = await bmp.GetPixelsAsync();
using FileStream fileStream = new FileStream(destination, FileMode.Create);
var ms = fileStream.AsRandomAccessStream();
var encoder = await BitmapEncoder.CreateAsync(BitmapEncoder.PngEncoderId, ms);
encoder.SetPixelData(BitmapPixelFormat.Bgra8, BitmapAlphaMode.Ignore, (uint)width, (uint)height, 96, 96, pixels.ToArray());
await encoder.FlushAsync();
}
}
And then run the app, and click the button in the app.
On my machine it produces two screenshots:
- Just the WebView2 (blank rectangle):

- The whole app's main window's contents, which has content, except for the blank WebView2 (valid screenshot, except bottom right is blank gray rectangle):

Originally reported in .NET MAUI: https://github.com/dotnet/maui/issues/9718
@Eilon The WebView2 provides a "CapturePreviewAsync" function which will create a PNG of the web contents. Sounds like the WinUI control would need to incorporate that (or some other mechanism) for RenderAsync. I'm opening a bug for this issue, and we'll ask the WinUI team to take a first look. Thanks!
I do believe that this is an MAUI issue, but since that one has been closed and locked, I will post my workaround here:
I have implemented this for Windows, Android and iOS:
public async Task<byte[]> GetScreenshot()
{
#if WINDOWS
using var ms = new MemoryStream();
var webview = Handler.PlatformView as WebView2;
await webview.CoreWebView2.CapturePreviewAsync(CoreWebView2CapturePreviewImageFormat.Jpeg, ms.AsRandomAccessStream());
return ms.ToArray();
#elif ANDROID
using var ms = new MemoryStream();
var webview = (Android.Webkit.WebView)Handler.PlatformView!;
var bitmap = Bitmap.CreateBitmap(webview.Width, webview.Height, Bitmap.Config.Argb8888!)!;
var canvas = new Canvas(bitmap);
webview.Draw(canvas);
await bitmap.CompressAsync(Bitmap.CompressFormat.Jpeg, 90, ms);
return ms.ToArray();
#elif IOS
var webview = (WKWebView)Handler.PlatformView!;
UIGraphics.BeginImageContextWithOptions(webview.Bounds.Size, true, 0);
webview.DrawViewHierarchy(webview.Bounds, true);
var jpegData = UIGraphics.GetImageFromCurrentImageContext().AsJPEG();
byte[] dataBytes = new byte[jpegData.Length];
System.Runtime.InteropServices.Marshal.Copy(jpegData.Bytes, dataBytes, 0, Convert.ToInt32(jpegData.Length));
UIGraphics.EndImageContext();
return dataBytes;
#endif
return null;
}
Here are the necessary usings:
#if IOS
using UIKit;
using WebKit;
#endif
#if ANDROID
using Android.Graphics;
using Path = System.IO.Path;
#endif
#if WINDOWS
using Microsoft.UI.Xaml.Controls;
using Microsoft.Web.WebView2.Core;
#endif
If you would like to use this now you can make your own class that inherits from BlazorWebView and add the above method to it.
Any progress on this?