我试图在另一个线程上渲染画布。这是我的尝试:
// returns path to exported image
private string exportToImage(double width, double height, Visual visual)
{
var filename = string.Format(@"{0}.png", Guid.NewGuid());
var tempFile = Path.Combine(tempDir, filename);
Rect rect = new Rect(0, 0, width, height);
RenderTargetBitmap rtb = new RenderTargetBitmap((int)rect.Right,
(int)rect.Bottom, 96d, 96d, System.Windows.Media.PixelFormats.Default);
Thread RENDER_THREAD = new Thread(() =>
{
this.Dispatcher.Invoke((Action)(() =>
{
rtb.Render(visual);
}));
});
RENDER_THREAD.Start();
//endcode as PNG
BitmapEncoder pngEncoder = new PngBitmapEncoder();
pngEncoder.Frames.Add(BitmapFrame.Create(rtb));
//save to memory stream
System.IO.MemoryStream ms = new System.IO.MemoryStream();
pngEncoder.Save(ms);
ms.Close();
System.IO.File.WriteAllBytes(tempFile, ms.ToArray());
return tempFile;
}
我没有得到任何错误,但渲染的结果是一个空图像。主线程中的rtb
实例和新线程是否相同?
修改: 这是我最近的尝试。
我在MouseUp事件上创建线程:
var curLayer = GetItemsPanel((canvasDataBinding.ItemContainerGenerator.ContainerFromIndex(Binding_LayersListView.SelectedIndex)));
Thread RASTERIZE_THREAD = new Thread(() => { exportToImage(curLayer.ActualWidth, curLayer.ActualHeight, curLayer); });
RASTERIZE_THREAD.Start();
这是新线程使用的方法:
void exportToImage(double width, double height, Visual visual)
{
var filename = string.Format(@"{0}.png", Guid.NewGuid());
var tempFile = Path.Combine(tempDir, filename);
Rect rect = new Rect(0, 0, width, height);
RenderTargetBitmap rtb = new RenderTargetBitmap((int)rect.Right,
(int)rect.Bottom, 96d, 96d, System.Windows.Media.PixelFormats.Default);
this.Dispatcher.Invoke(new Action(() =>
{
rtb.Render(visual);
}));
//endcode as PNG
BitmapEncoder pngEncoder = new PngBitmapEncoder();
pngEncoder.Frames.Add(BitmapFrame.Create(rtb));
//save to memory stream
System.IO.MemoryStream ms = new System.IO.MemoryStream();
pngEncoder.Save(ms);
ms.Close();
System.IO.File.WriteAllBytes(tempFile, ms.ToArray());
}
那么为什么它告诉我调用线程无法访问此对象,因为rtb.Render(visual)
上有另一个线程拥有它?我没有从其他任何地方调用exportToImage,那么为什么Dispatcher与我创建的线程无关?
编辑: 我需要在Dispatcher.Invoke()中创建RebderTargetBitmap。
答案 0 :(得分:3)
这是一个代码,如何在线程中呈现相同的内容并使其成为异步
整个想法是
此代码不依赖于Dispatcher,因此可以运行完整的异步
void exportToImage(double width, double height, Visual visual)
{
//this line can not be inside a thread because different thread owns the visual
string visualData = XamlWriter.Save(visual);
Thread t = new Thread(() =>
{
var filename = string.Format(@"{0}.png", Guid.NewGuid());
var tempFile = System.IO.Path.Combine("c:\\", filename);
Rect rect = new Rect(0, 0, width, height);
//create a host control to host the visual
ContentControl cc = new ContentControl();
cc.Content = XamlReader.Parse(visualData);
//call Arrange to let control perform layout (important)
cc.Arrange(rect);
RenderTargetBitmap rtb = new RenderTargetBitmap((int)rect.Right,
(int)rect.Bottom, 96d, 96d, System.Windows.Media.PixelFormats.Default);
rtb.Render(cc);
//endcode as PNG
BitmapEncoder pngEncoder = new PngBitmapEncoder();
pngEncoder.Frames.Add(BitmapFrame.Create(rtb));
//save to memory stream
System.IO.MemoryStream ms = new System.IO.MemoryStream();
pngEncoder.Save(ms);
ms.Close();
System.IO.File.WriteAllBytes(tempFile, ms.ToArray());
});
//STA is necessary for XamlReader.Parse, and proper rendering
t.SetApartmentState(ApartmentState.STA);
t.Start();
}
我已成功测试此代码以呈现一些测试视觉效果。但结果可能会因您的使用方式而异。作为一个猜测绑定&光栅图像可能存在一些问题,但在我的测试中,它的效果就像一个魅力。