如何使用新线程更新ObservableCollection并从另一个UserControl访问其项目?

时间:2012-03-27 21:03:21

标签: c# wpf multithreading observablecollection

我正在开发一个关于使用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的自定义项传递给另一个UserControl进行渲染,但是我得到了一个异常:

  

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

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

1 个答案:

答案 0 :(得分:2)

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

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

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

编辑:如果我理解了您的应用程序的整个想法,我仍然不能100%确定,但是做了一个小样本,演示了如何使用线程和UI元素。

我制作了Window(这是您的UserControl),其中包含ButtonListBox。单击Button启动一个线程并处理一些项目。在我的例子中,它只是将一些文本添加到列表中,我添加了Thread.Sleep(1000)来模拟许多东西的处理。准备好文本后,它将被添加到ObservableCollection,这必须由UI线程(Dispatcher)完成。没有任何东西可以阻止用户界面,但这种添加方式非常快。您也可以同时启动多个线程。

这是Window的代码隐藏(Window本身只包含ButtonListBox):

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)));
        }
    }
}