如何将 WPF 控件的图像输出到剪贴板

How to output an image of a WPF control to the clipboard

我有以下代码获取一个控件(作为一个视觉对象),使用视觉画笔将控件绘制成一个 RenderTargetBitmap,然后可以将其保存到磁盘。这是成功的。

我想使用相同的代码将图像放入剪贴板。这似乎不起作用;尽管剪贴板接受数据,但它不接受数据是图像。 这显然是格式问题,但我不知道如何对其进行排序...

代码如下:-

public void CopyToClipBoard(Visual forDrawing)
{           
    RenderTargetBitmap bmp = ControlToImage(forDrawing, 96, 96);
    CopyToClipBoard(bmp);
}
private void CopyToClipBoard(BitmapSource bmp)
{
   Thread thread = new Thread(() =>
   {                
      Clipboard.SetImage(bmp);
   });
   thread.SetApartmentState(ApartmentState.STA); //Set the thread to STA
   thread.Start();
}
private static RenderTargetBitmap ControlToImage(Visual target, double dpiX, double dpiY)
{
   if (target == null)
   {
      return null;
   }
   // render control content
   Rect bounds = VisualTreeHelper.GetDescendantBounds(target);
   RenderTargetBitmap rtb = new RenderTargetBitmap((int)(bounds.Width * dpiX / 96.0),
                                                   (int)(bounds.Height * dpiY / 96.0),
                                                  dpiX, dpiY,
                                                  PixelFormats.Pbgra32);
   DrawingVisual dv = new DrawingVisual();
   using (DrawingContext ctx = dv.RenderOpen())
   {
       VisualBrush vb = new VisualBrush(target);
       ctx.DrawRectangle(vb, null, new Rect(new System.Windows.Point(), bounds.Size));
    }
    rtb.Render(dv);
    return rtb;           
}

查看您的代码后,我希望您的线程委托中会抛出一个跨线程异常 (CopyToClipBoard(BitmapSource):void)。 BitmapSource 是一个 DispatcherObject 并在与处理它的不同的调度程序线程(在主线程上)上创建。您不能在线程之间传递 DispatcherObject 个实例。除了实例扩展 Freezable 并且在此实例上调用了 Freezable.Freeze()

这意味着为了将 bmp 引用传递给处理剪贴板的线程(或一般关联线程以外的任何其他线程),您必须先调用 bmp.Freeze()

我假设异常被 Thread 吞没,并且您永远不会 运行 您的应用程序处于调试模式。这就是为什么您从未识别出异常。

如我之前所写,您无需生成新的 STA 线程即可访问剪贴板。我不推荐这个。

如果您继续使用专用线程,只需在将 Freezable 传递给线程之前将其冻结:

public void CopyToClipBoard(Visual forDrawing)
{           
    RenderTargetBitmap bmp = ControlToImage(forDrawing, 96, 96);
   
    // Freeze the freezable DispatcherObject so that it can be consumed by other threads
    bmp.Freeze();

    CopyToClipBoard(bmp);
}