#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. Это оказалось действительно полезным — спасибо