【问题标题】:How to Update ObservableCollection with a new thread and access its items from another UserControl?如何使用新线程更新 ObservableCollection 并从另一个 UserControl 访问其项目?
【发布时间】:2012-03-27 21:03:21
【问题描述】:

我正在做一个关于用 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 页面,用户不需要等待加载所有页面,这可以通过新线程来实现。

【问题讨论】:

    标签: c# wpf multithreading observablecollection


    【解决方案1】:

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

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

    BeginInvoke是异步调用,不会阻塞下面代码的执行。

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

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

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

    【讨论】:

    • @Chris Shain - 你能解释一下吗?
    • 但这并没有解决我的问题,加载所有内容需要太长时间。
    • 不确定我是否遇到了您的问题。当然,加载 pdf 文件需要时间,但您的 UI 线程只会阻止渲染您创建的图像。 pdf 的加载不会阻塞 UI。
    • @MatthiasG 抱歉误读了答案 - 这应该可以,但我要补充一点,您可能需要先在 UI 线程上创建 observableCollection
    • 您能否添加更多代码,尤其是如何将 ObservableCollection 与 UserControl 结合使用?我可以调整我的代码示例。
    猜你喜欢
    • 2017-01-04
    • 2015-08-22
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2020-07-13
    相关资源
    最近更新 更多