使用多线程将用户控件另存为图像,错误是:调用线程无法访问此对象,因为其他线程拥有它



我想使用多线程将所有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 以了解更多详细信息。

最新更新