Как вывести изображение элемента управления WPF в буфер обмена

#wpf #clipboard #bitmapsource

#wpf #буфер обмена #bitmapsource

Вопрос:

У меня есть следующий код, который принимает элемент управления (как визуальный объект), использует визуальную кисть для преобразования элемента управления в 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;           
}
 

Комментарии:

1. Операция с буфером обмена должна выполняться в основном потоке приложения. Нет необходимости создавать новый поток пользовательского интерфейса для операции набора буфера обмена.

2. Если у вас есть фоновый поток, вы должны, по крайней мере, убедиться, что RenderTargetBitmap доступен из этого потока (или из любого потока, отличного от того, в котором он был создан), вызвав его метод Freeze() .

Ответ №1:

После просмотра вашего кода я бы ожидал, что в вашем делегате потока () будет создано исключение перекрестного потока CopyToClipBoard(BitmapSource):void . BitmapSource является DispatcherObject и создается в другом потоке диспетчера (в основном потоке), чем он обрабатывается. Вы не можете передавать DispatcherObject экземпляры между потоками. За исключением того, что экземпляр расширяется Freezable и Freezable.Freeze() был вызван в этом экземпляре.

Это означает, что для передачи bmp ссылки на поток, который обрабатывает буфер обмена (или любой другой поток, отличный от связанного потока в целом), вы должны вызвать bmp.Freeze() before .

Я предполагаю, что исключение проглатывается, 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);
}
 

Комментарии:

1. Замораживание BitmapSource сделало свое дело. Я использую поток, потому что это не в основном потоке приложения. На самом деле я запускаю программное обеспечение в режиме отладки, но по какой-то причине VS2019 не перехватывал исключение — поэтому я предположил (всегда ошибка!), Что что-то передается в буфер обмена — только в недружественном формате.

2. Вы должны включить по крайней мере все исключения CLR: нажмите Ctrl Alt E и установите соответствующие флажки. По умолчанию VS не будет прерываться для всех исключений.

3. Это оказалось действительно полезным — спасибо