关于c#:如何在繁忙循环中显示进度?

How do I display progress during a busy loop?

我有一个循环,可以从外部源读取大量数据。该过程大约需要20秒钟,我想向用户显示进度。我不需要任何花哨的进度条,因此我选择在标有" Step 1/1000"的标签上绘制进度,然后更改为" Step 2/1000"等。

我的代码如下所示:

1
2
3
4
5
6
7
8
9
10
//"count" is the number of steps in the loop,
// I receive it in previous code

String countLabel ="/"+count.ToString();

for (i = 0; i < count; i++)
{
    ... do analysis ...
    labelProgress.Content ="Step"+i.ToString()+countLabel
}

但是,在该分析过程中,屏幕"卡住了",进度没有显示为前进。我从过去的C语言中了解了这种行为,在C语言中,我可能会有一个单独的线程显示进度条,该进度条接收来自循环的通知,或者某种形式的重绘/刷新,或者强制窗口/应用程序处理其消息队列。 >

在C#中正确的方法是什么?我不依赖于标签,所以如果有一个简单的进度栏弹出屏幕,我可以使用它代替此标签,那也很棒...

谢谢


将工作移至BackgroundWorker并使用ReportProgress方法。

1
2
3
4
5
6
7
8
9
10
11
for (i = 0; i < count; i++)
{
    ... do analysis ...
    worker.ReportProgress((100 * i) / count);
}

private void MyWorker_ProgressChanged(object sender,
    ProgressChangedEventArgs e)
{
    taskProgressBar.Value = Math.Min(e.ProgressPercentage, 100);
}


1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
    //Create a Delegate to update your status button
    delegate void StringParameterDelegate(string value);
    String countLabel ="/" + count.ToString();
    //When your button is clicked to process the loops, start a thread for process the loops
    public void StartProcessingButtonClick(object sender, EventArgs e)
    {
        Thread queryRunningThread = new Thread(new ThreadStart(ProcessLoop));
        queryRunningThread.Name ="ProcessLoop";
        queryRunningThread.IsBackground = true;
        queryRunningThread.Start();
    }

    private void ProcessLoop()
    {
        for (i = 0; i < count; i++)
        {
            ... do analysis ...
            UpdateProgressLabel("Step"+i.ToString()+countLabel);
        }
    }

    void UpdateProgressLabel(string value)
    {
        if (InvokeRequired)
        {
            // We're not in the UI thread, so we need to call BeginInvoke
            BeginInvoke(new StringParameterDelegate(UpdateProgressLabel), new object[] { value });
            return;
        }
        // Must be on the UI thread if we've got this far
        labelProgress.Content = value;
    }


由于当前线程的优先级高于最终将设置标签;)的UI线程的优先级,因此未更新UI。因此,直到您的线程写完为止,它最终将更新您的标签。

对我们来说幸运的是,每个WPF控件上都有一个Dispatcher属性,可让您使用另一个优先级来启动新线程。

1
2
labelProgress.Dispatcher.Invoke(DispatcherPriority.Background,
                    () => labelProgress.Content = string.Format("Step {0}{1}", i, countLabel));

这会在后台启动线程并完成工作!您也可以尝试其他DispatcherPriority选项

PS我还自由地添加了一个匿名方法并以某种方式修复了您的字符串解析..希望您不要介意..