C# 如何防止BackgroundWorker阻塞UI线程?

C# 如何防止BackgroundWorker阻塞UI线程?,c#,wpf,multithreading,C#,Wpf,Multithreading,在我的XML编辑器中,我希望能够使用索引文件一次打开多个文件。显然,根据文件的数量,这可能需要一些时间,我想使用进度条通知用户程序仍在加载和执行某些操作。 根据我的研究,保持UI进度条更新的方法是使用BackgroundWorker public MainWindow() { InitializeComponent(); tabList = new ObservableCollection<FileTab>();

在我的XML编辑器中,我希望能够使用索引文件一次打开多个文件。显然,根据文件的数量,这可能需要一些时间,我想使用进度条通知用户程序仍在加载和执行某些操作。 根据我的研究,保持UI进度条更新的方法是使用BackgroundWorker

public MainWindow()
        {
            InitializeComponent();

            tabList = new ObservableCollection<FileTab>();

            tabControl.ItemsSource = tabList;

            backgroundWorker = new BackgroundWorker();
            backgroundWorker.DoWork += backgroundWorker_DoWork;
            backgroundWorker.ProgressChanged += backgroundWorker_ProgressChanged;
            backgroundWorker.RunWorkerCompleted += backgroundWorker_RunWorkerCompleted;
            backgroundWorker.WorkerReportsProgress = true; 
        }

(...)

private void OpenProjectButtonClick(object sender, RoutedEventArgs e) 
        {
            openingProgressBar.Value = 0;
            openingProgressBar.Visibility = System.Windows.Visibility.Visible;
            backgroundWorker.RunWorkerAsync();
        }

void backgroundWorker_DoWork(object sender, DoWorkEventArgs e)
        {
            int i = 0;
            foreach (IndexFile file in indexManager.fileList)
            {
                this.Dispatcher.Invoke((Action)(() =>
                    {
                        tabList.Add(new FileTab(file.filePath));
                    }));
                i++;
                Console.WriteLine("-(DoWork)->" + i);
                double percentage = (Convert.ToDouble(i) / Convert.ToDouble(indexManager.fileList.Count)) * 100;
                Console.WriteLine("-(DoWork.percentage)-> "+ percentage);
                backgroundWorker.ReportProgress((int)percentage);
            }
        }
void backgroundWorker_ProgressChanged(object sender, ProgressChangedEventArgs e)
        {
            openingProgressBar.Value = e.ProgressPercentage;
            Console.WriteLine("-(ProgressChanged)->" + openingProgressBar.Value);
        }
void backgroundWorker_RunWorkerCompleted(object sender, RunWorkerCompletedEventArgs e)
        {
            openingProgressBar.Visibility = System.Windows.Visibility.Collapsed;
            backgroundWorker.Dispose();
            Console.WriteLine("-(RunWorkerComplete)-> Done");
        }
public主窗口()
{
初始化组件();
tabList=新的可观察集合();
tabControl.ItemsSource=表格列表;
backgroundWorker=新的backgroundWorker();
backgroundWorker.DoWork+=backgroundWorker\u DoWork;
backgroundWorker.ProgressChanged+=backgroundWorker\u ProgressChanged;
backgroundWorker.RunWorkerCompleted+=backgroundWorker\u RunWorkerCompleted;
backgroundWorker.WorkerReportsProgress=true;
}
(...)
private void OpenProjectButtonClick(对象发送方,路由目标)
{
openingProgressBar.Value=0;
openingProgressBar.Visibility=System.Windows.Visibility.Visible;
backgroundWorker.RunWorkerAsync();
}
无效backgroundWorker_DoWork(对象发送方,DoWorkEventArgs e)
{
int i=0;
foreach(indexManager.fileList中的IndexFile文件)
{
this.Dispatcher.Invoke((操作)(()=>
{
添加(新文件选项卡(file.filePath));
}));
i++;
控制台写入线(“-(DoWork)->”+i);
双百分比=(Convert.ToDouble(i)/Convert.ToDouble(indexManager.fileList.Count))*100;
Console.WriteLine(“-(DoWork.percentage)->”+百分比);
backgroundWorker.ReportProgress((int)百分比);
}
}
void backgroundWorker\u ProgressChanged(对象发送方,ProgressChangedEventArgs e)
{
openingProgressBar.Value=e.ProgressPercentage;
Console.WriteLine(“-(ProgressChanged)->”+openingProgressBar.Value);
}
void backgroundWorker\u RunWorkerCompleted(对象发送方,runworkercompletedeventarge)
{
openingProgressBar.Visibility=System.Windows.Visibility.Collapsed;
backgroundWorker.Dispose();
Console.WriteLine(“-(RunWorkerComplete)->完成”);
}
因为我在DoWork方法中访问tablist,所以我将该调用包装在Dispathcer.Invoke中。在这种形式下,代码实现了我想要的功能。它使折叠的progressBar可见,并每隔一段时间更新一次。遗憾的是,它没有在每次加载文件后更新百分比。从控制台中可以看到,ProgressChanged执行滞后于DoWork。根据我的理解,它在循环的每次迭代中都被调用。即使它触发了,用户界面也不会总是对此做出响应


所以我的问题是:我是否仍然以某种方式阻塞UI线程,如何修复它

尝试将报告进度操作移动到调度程序的操作中:

 foreach (IndexFile file in indexManager.fileList)
            {
                this.Dispatcher.Invoke((Action)(() =>
                    {
                        tabList.Add(new FileTab(file.filePath));
                        Console.WriteLine("-(DoWork)->" + i);
                        double percentage = (Convert.ToDouble(i) / Convert.ToDouble(indexManager.fileList.Count)) * 100;
                        Console.WriteLine("-(DoWork.percentage)-> "+ percentage);
                        backgroundWorker.ReportProgress((int)percentage);
                    }));
                i++;

            }
dispatcher可能在另一个线程中运行…

问题在于

您正在UI和BackgroundWorker之间进行耦合

解决方案

返回您的FileTab对象的ReportProgress


然后将
backgroundWorker\u ProgressChanged
中的FileTab对象添加到
表格列表中

遗憾的是,这似乎并没有改变行为。这起作用了,现在速度如此之快,我几乎不再需要进度条了。。。谢谢不幸的是,我没有工作。我正在报告当前循环
I
,在报告事件中,我将此int设置为一个名为
ProgressValue
的属性。此
ProgressValue
作为值绑定到progressbar。我的用户界面仍在阻塞。@C4u那么改变它怎么样?(我看不出你的问题)。在
DoWork
(所有值都必须是用户界面上的自主值)中完成工作,每次完成一个部件时,通过
ReportProgress
将其报告给用户界面威胁(这里您可以使用用户界面值(如
ProgressValue
)最后,当您的所有工作都完成时,如果您需要在UI中再次执行某些操作,请调用
RunWorkerCompleted
。我希望这将对您有所帮助:-)
void backgroundWorker_DoWork(object sender, DoWorkEventArgs e)
{
    int i = 0;
    foreach (IndexFile file in indexManager.fileList)
    {
        i++;
        Console.WriteLine("-(DoWork)->" + i);
        double percentage = (Convert.ToDouble(i) / Convert.ToDouble(indexManager.fileList.Count)) * 100;
        Console.WriteLine("-(DoWork.percentage)-> "+ percentage);
        backgroundWorker.ReportProgress((int)percentage,new FileTab(file.filePath));
    }
}