我想使用多线程将所有UserControl
同时保存为图像,想在屏幕上显示动画视图,但出现错误:
调用线程无法访问此对象,因为不同的 线程拥有它。
这是我的代码:
private void OnScreenShotAllCommandExecuted(object parameter)
{
string filePath = @"D:ScreenShot";
IList<UserControl> userControls = LayoutRoot.Children.OfType<UserControl>().ToList();
List<Thread> tasks = new List<Thread>();
AnimationView animationView = new AnimationView();
animationView.Show();
foreach (UserControl userControl in userControls)
{
string viewName = userControl.Name;
string fileName = userControl.Name + ".png";
if (viewName != null)
{
Thread thread = new Thread(() =>
{
Thread.Sleep(1000);
SaveView(userControl, fileName, filePath);
ThreadHasFinished();
});
tasks.Add(thread);
thread.SetApartmentState(ApartmentState.STA);
thread.Start();
}
}
}
private void ThreadHasFinished()
{
Interlocked.Increment(ref finishedControls);
Dispatcher.BeginInvoke((Action)(() =>
{
animationView.Close();
}));
}
public static void SaveView(UserControl userControl, string fileName, string destFolder)
{
Rect bounds = VisualTreeHelper.GetDescendantBounds(userControl);
int width = (int)view.RenderSize.Width;
int height = (int)view.RenderSize.Height;
RenderTargetBitmap rtb = new RenderTargetBitmap(width, height, 96, 96, PixelFormats.Pbgra32);
DrawingVisual dv = new DrawingVisual();
using (DrawingContext ctx = dv.RenderOpen())
{
VisualBrush vb = new VisualBrush(userControl);
ctx.DrawRectangle(vb, new Pen(Brushes.Blue, 2), new Rect(new Point(0, 100), size));
}
rtb.Render(dv);
PngBitmapEncoder png = new PngBitmapEncoder();
png.Frames.Add(BitmapFrame.Create(rtb));
using (Stream fileStream = new FileStream(string.Format("{0}\{1}.png", destFolder, fileName), FileMode.Create))
{
png.Save(fileStream);
}
}
错误
调用线程无法访问此对象,因为不同的 线程拥有它。
在此行上
Rect bounds = VisualTreeHelper.GetDescendantBounds(userControl);
Thread thread = new Thread(() =>
{
Thread.Sleep(1000);
SaveView(userControl, fileName, filePath);
ThreadHasFinished();
});
在上面的代码中,您正在调用SaveView(userControl, fileName, filePath)
这是错误的,因为主线程上的 WPF 更新 UI。
对于响应式UI,我建议您在这种情况下使用BackgroundWorker 而不是直接创建线程。您将在主线程上RunWorkerCompleted
事件,因此您可以更新其中的任何UIElement。您可以在DoWork
事件下运行昂贵的东西。DoWork 在线程池中的后台线程上运行。
许多UI框架(包括WPF和Windows Forms)的一般设计原则是线程相关性。具体而言,创建 UI 元素的线程是唯一可以与之交互的线程。
如果您尝试使用多个不同的线程直接与 UI 元素进行交互,那么您的设计从根本上是有缺陷的。
请注意,不直接与用户控件交互的任何内容都可以在不同的线程上运行。磁盘 I/O 等,例如
若要在工作线程和 UI 线程之间进行封送(99% 的基本应用程序恰好只有一个 UI 线程),请使用工作线程中的元素调度程序。调度程序将允许您将请求排队到 ui 线程并获取其结果。阅读 MSDN 以了解更多详细信息。