如何使用新线程更新可观察集合并从另一个用户控件访问其项

本文关键字:另一个 合并 用户 控件 访问 集合 观察 新线程 何使用 线程 更新 | 更新日期: 2023-09-27 18:29:07

我正在做一个关于 C# 语言 PDF 渲染的项目。我将PDF文件的每一页转换为图像,并通过以下代码将其添加到具有新线程的ObservableCollection

  ThreadStart myThreadDelegate = new ThreadStart(DoWork);
  myThread = new Thread(myThreadDelegate);
  myThread.SetApartmentState(ApartmentState.STA);
  void DoWork()
    {
        for (int i = 0; i < pdfFile.Pages.Count; i++)
        {
            PdfPage page=pdfFile.LoadPage(i);
            myObservableCollection[i]=page;
        }
    }

然后将 myObservableCollection 的自定义项传递给另一个用户控件进行渲染,但我得到了一个异常:

调用线程无法访问此对象,因为不同的 线程拥有它。

我知道如果我使用 UI 线程,我的问题可以解决,但我想在后台加载 pdf 页面,用户不会等待加载所有页面,这可以通过新线程实现。

如何使用新线程更新可观察集合并从另一个用户控件访问其项

您可以使用线程,但必须使用Dispatcher来访问 UI 元素。只有将物料传递到UserControl的部件必须由调度员完成。

Application.Current.Dispatcher.BeginInvoke(new Action(() => AddItem()));

BeginInvoke 是一个异步调用,不会阻止以下代码的执行。

编辑:我仍然不能 100% 确定我是否解开了您的应用程序的整个想法,但制作了一个小示例来演示如何使用线程和 UI 元素。

我做了一个Window(这将是你的UserControl(,其中包含一个Button和一个ListBox。单击Button时,将启动线程并处理某些项目。就我而言,它只是将一些文本添加到列表中,我添加了Thread.Sleep(1000)来模拟大量内容的处理。当文本准备好时,它将被添加到ObservableCollection中,这必须由UI线程(Dispatcher(完成。没有什么阻止 UI,但这种添加和这完成得非常快。您也可以同时启动多个线程。

这是Window的代码隐藏(它本身Window只包含一个Button和一个ListBox(:

public partial class MainWindow : Window
{
    private ObservableCollection<string> textList;
    public MainWindow()
    {
        textList = new ObservableCollection<string>();
        InitializeComponent();
        btnStartWork.Click += BtnStartWorkClick;
        lstTextList.ItemsSource = textList;
    }
    private void BtnStartWorkClick(object sender, RoutedEventArgs e)
    {
        Thread myThread;
        ThreadStart myThreadDelegate = DoWork;
        myThread = new Thread(myThreadDelegate);
        myThread.SetApartmentState(ApartmentState.STA);
        myThread.Start();
    }
    private void DoWork()
    {
        for (int i = 0; i < 5; i++)
        {
            string text = string.Format("Text {0}", i);
            // block the thread (but not the UI)
            Thread.Sleep(1000);
            // use the dispatcher to add the item to the list, which will block the UI, but just for a very short time
            Application.Current.Dispatcher.BeginInvoke(new Action(() => textList.Add(text)));
        }
    }
}