异步等待与Task同步的事件/操作,同时使用CefSharp从Web浏览器/网页获取屏幕截图

use*_*492 0 c# wpf screenshot async-await cefsharp

我想做的是以下几点:

我有一个CefSharp ChromiumWebBrowser(WPF控件),我想在该浏览器中截取网页的屏幕截图。屏幕ChromiumWebBrowser上没有获取屏幕截图的方法。但是我可以通过将事件处理程序附加到OnPaint浏览器的事件来获取渲染。这样,我得到的位图就是屏幕截图。该过程基于以下答案:https : //stackoverflow.com/a/54236602/2190492

现在,我正在创建一个类CefSharpScreenshotRecorder,该类负责拍摄屏幕截图。它应该接受浏览器实例,将事件处理程序附加到OnPaint事件,并获取位图。该过程的所有状态都应封装在CefSharpScreenshotRecorder该类中。我希望能够异步使用我的课程。因为我们必须等到OnPaint事件被触发。触发该事件(并调用事件处理程序)后,该事件处理程序中将提供一个位图。然后,该位图应该是最初调用的异步方法的结果(如CefSharpScreenshotRecorder.TakeScreenshot(...cefBrowserInstance...)。当然,所有事情都必须在不阻塞/滞后UI的情况下发生。

我对C#中的异步编程不是很熟悉。我遇到的问题是,我找不到找到一种可以等待的方法的方法,该方法仅在被调用时代表OnPaint事件处理程序返回。我什至不知道是否存在任何代码功能来创建此逻辑。

Bio*_*ode 5

可以使用来实现TaskCompletionSource。这样,您无需使用即可将同步(例如,事件驱动)代码包装到异步方法中Task.Run

class CefSharpScreenshotRecorder
{
  private TaskCompletionSource<System.Drawing.Bitmap> TaskCompletionSource { get; set; }

  public Task<System.Drawing.Bitmap> TakeScreenshotAsync(
    ChromiumWebBrowser browserInstance, 
    TaskCreationOptions optionalTaskCreationOptions = TaskCreationOptions.None)
  {
    this.TaskCompletionSource = new TaskCompletionSource<System.Drawing.Bitmap>(optionalTaskCreationOptions);

    browserInstance.Paint += GetScreenShotOnPaint;

    // Return Task instance to make this method awaitable
    return this.TaskCompletionSource.Task;
  }

  private void GetScreenShotOnPaint(object sender, PaintEventArgs e)
  { 
    (sender as ChromiumWebBrowser).Paint -= GetScreenShotOnPaint;

    System.Drawing.Bitmap newBitmap = new Bitmap(e.Width, e.Height, 4 * e.Width, PixelFormat.Format32bppPArgb, e.Buffer);

    // Optional: save the screenshot to the hard disk "MyPictures" folder
    var screenshotDestinationPath = Path.Combine(
      Environment.GetFolderPath(Environment.SpecialFolder.MyPictures), 
      "CefSharpBrowserScreenshot.png");
    newBitmap.Save(screenshotDestinationPath);

    // Create a copy of the bitmap, since the underlying buffer is reused by the library internals
    var bitmapCopy = new System.Drawing.Bitmap(newBitmap);

    // Set the Task.Status of the Task instance to 'RanToCompletion'
    // and return the result to the caller
    this.TaskCompletionSource.SetResult(bitmapCopy);
  }

  public BitmapImage ConvertToBitmapImage(System.Drawing.Bitmap bitmap)
  {
    using(var memoryStream = new MemoryStream())
    {
      bitmap.Save(memoryStream, ImageFormat.Png);
      memoryStream.Position = 0;

      BitmapImage bitmapImage = new BitmapImage();
      bitmapImage.BeginInit();
      bitmapImage.StreamSource = memoryStream;
      bitmapImage.CacheOption = BitmapCacheOption.OnLoad;
      bitmapImage.EndInit();
      bitmapImage.Freeze();
    }
  }
}
Run Code Online (Sandbox Code Playgroud)

使用示例(有效):

MainWindow.xaml

<Window>
  <StackPanel>
    <Button Click="TakeScreenshot_OnClick" Height="50" Content="Take Screenshot"/>
    <ChromiumWebBrowser x:Name="ChromiumWebBrowser"
                        Width="500"
                        Height="500"
                        Address="/sf/answers/4038694131/" />
    <Image x:Name="ScreenshotImage" />
  </StackPanel>
</Window>
Run Code Online (Sandbox Code Playgroud)

MainWindow.xaml.cs

private async void TakeScreenshot_OnClick(object sender, RoutedEventArgs e)
{
  var cefSharpScreenshotRecorder = new CefSharpScreenshotRecorder();
  System.Drawing.Bitmap bitmap = await cefSharpScreenshotRecorder.TakeScreenshotAsync(this.ChromiumWebBrowser);

  this.ScreenshotImage.Source = cefSharpScreenshotRecorder.ConvertToBitmapImage(bitmap);
}
Run Code Online (Sandbox Code Playgroud)

编辑

如果您只想从网页中获取快照,请查看CefSharp.OffScreen(可通过NuGet程序包管理器获得)。本ChromiumWebBrowser类暴露了一个ScreenshotAsync方法,它返回一个准备使用System.Drawing.Bitmap是GitHub上项目存储库中的示例。

例:

class CefSharpScreenshotRecorder
{
  private TaskCompletionSource<System.Drawing.Bitmap> TaskCompletionSource { get; set; }

  public async Task<System.Drawing.Bitmap> TakeScreenshotAsync(
    ChromiumWebBrowser browser, 
    string url, 
    TaskCreationOptions optionalTaskCreationOptions = TaskCreationOptions.None)
  {
    if (!string.IsNullOrEmpty(url))
    {
      throw new ArgumentException("Invalid URL", nameof(url));
    }

    this.TaskCompletionSource = new TaskCompletionSource<Bitmap>(optionalTaskCreationOptions);

    // Load the page. In the loaded event handler 
    // take the snapshot and return it asynchronously it to caller
    return await LoadPageAsync(browser, url);
  }

  private Task<System.Drawing.Bitmap> LoadPageAsync(IWebBrowser browser, string url)
  {
    browser.LoadingStateChanged += GetScreenShotOnLoadingStateChanged;

    browser.Load(url);

    // Return Task instance to make this method awaitable
    return this.TaskCompletionSource.Task;
  }

  private async void GetScreenShotOnLoadingStateChanged(object sender, LoadingStateChangedEventArgs e)
  { 
    browser.LoadingStateChanged -= GetScreenShotOnLoadingStateChanged;

    System.Drawing.Bitmap screenshot = await browser.ScreenshotAsync(true);

    // Set the Task.Status of the Task instance to 'RanToCompletion'
    // and return the result to the caller
    this.TaskCompletionSource.SetResult(screenshot);
  }
}
Run Code Online (Sandbox Code Playgroud)

用法示例:

public async Task CreateScreenShotAsync(ChromiumWebBrowser browserInstance, string url)
{
  var recorder = new CefSharpScreenshotRecorder();   
  System.Drawing.Bitmap screenshot = await recorder.TakeScreenshotAsync(browserInstance, url);
}
Run Code Online (Sandbox Code Playgroud)