无法在循环中更新表单?

时间:2011-04-06 13:40:08

标签: .net vb.net winforms

嗨,大家好我有一个问题,我希望很容易修复。

我是VB的新手,我正在使用Studio 2008,在程序中我试图让我有一个按钮,当点击开始一个相当长的循环,需要几分钟才能完成。虽然这种情况正在发生,我试图将循环进度的更新发送到我已设置的列表框。不幸的是,这些更新不会进入列表框,直到循环结束,我已尝试使用几种不同的类型(进度条/富文本框等)。在循环进行过程中,我似乎无法进行任何形式的更改。

我是否需要指定一些选项或事件,以便在循环中对表单进行更改?

5 个答案:

答案 0 :(得分:9)

如果您不需要用户与流程进行交互(停止或暂停),您可以使用:

 lblMyStatus.Text = "Finished " + i.ToString() + "%";
 lblMyStatus.Refresh(); //this forces the label to redraw itself

这将刷新GUI,但仍然无法响应用户输入。如果您需要在循环中响应用户输入,那么后台工作程序是其中一个选项...

答案 1 :(得分:4)

这里的教训是,你永远不应该对GUI线程进行任何计算密集型工作。这是因为在处理器忙于执行计算时无法更新GUI。如果没有多个线程,您的应用程序一次只能执行一项操作,因此必须先处理某些事情。考虑到这一点,答案是将长时间运行的计算转移到单独的线程上。使用内置的BackgroundWorker组件是一个简单的解决方案。

上面链接的文档甚至给出了一个很好的例子:

  

要尝试此代码,请创建Windows窗体应用程序。添加名为resultLabel的Label控件,并添加两个名为startAsyncButton和cancelAsyncButton的Button控件。为两个按钮创建Click事件处理程序。从“工具箱”的“组件”选项卡中,添加名为backgroundWorker1的BackgroundWorker组件。为BackgroundWorker创建DoWork,ProgressChanged和RunWorkerCompleted事件处理程序。在表单的代码中,使用以下代码替换现有代码:

<强> VB.NET:

Imports System.ComponentModel
Imports System.Windows.Forms

Namespace BackgroundWorkerSimple
    Public Partial Class Form1
        Inherits Form
        Public Sub New()
            InitializeComponent()
            backgroundWorker1.WorkerReportsProgress = True
            backgroundWorker1.WorkerSupportsCancellation = True
        End Sub

        Private Sub startAsyncButton_Click(sender As Object, e As EventArgs)
            If Not backgroundWorker1.IsBusy Then
                ' Start the asynchronous operation.
                backgroundWorker1.RunWorkerAsync()
            End If
        End Sub

        Private Sub cancelAsyncButton_Click(sender As Object, e As EventArgs)
            If backgroundWorker1.WorkerSupportsCancellation Then
                ' Cancel the asynchronous operation.
                backgroundWorker1.CancelAsync()
            End If
        End Sub

        ' This event handler is where the time-consuming work is done.
        Private Sub backgroundWorker1_DoWork(sender As Object, e As DoWorkEventArgs)
            Dim worker As BackgroundWorker = TryCast(sender, BackgroundWorker)

            For i As Integer = 1 To 10
                If worker.CancellationPending Then
                    e.Cancel = True
                    Exit For
                Else
                    ' Perform a time consuming operation and report progress.
                    System.Threading.Thread.Sleep(500)
                    worker.ReportProgress(i * 10)
                End If
            Next
        End Sub

        ' This event handler updates the progress.
        Private Sub backgroundWorker1_ProgressChanged(sender As Object, e As ProgressChangedEventArgs)
            resultLabel.Text = (e.ProgressPercentage.ToString() & "%")
        End Sub

        ' This event handler deals with the results of the background operation.
        Private Sub backgroundWorker1_RunWorkerCompleted(sender As Object, e As RunWorkerCompletedEventArgs)
            If e.Cancelled Then
                resultLabel.Text = "Canceled!"
            ElseIf e.[Error] IsNot Nothing Then
                resultLabel.Text = "Error: " & e.[Error].Message
            Else
                resultLabel.Text = "Done!"
            End If
        End Sub
    End Class
End Namespace

C#:

using System;
using System.ComponentModel;
using System.Windows.Forms;

namespace BackgroundWorkerSimple
{
    public partial class Form1 : Form
    {
        public Form1()
        {
            InitializeComponent();
            backgroundWorker1.WorkerReportsProgress = true;
            backgroundWorker1.WorkerSupportsCancellation = true;
        }

        private void startAsyncButton_Click(object sender, EventArgs e)
        {
            if (!backgroundWorker1.IsBusy)
            {
                // Start the asynchronous operation.
                backgroundWorker1.RunWorkerAsync();
            }
        }

        private void cancelAsyncButton_Click(object sender, EventArgs e)
        {
            if (backgroundWorker1.WorkerSupportsCancellation)
            {
                // Cancel the asynchronous operation.
                backgroundWorker1.CancelAsync();
            }
        }

        // This event handler is where the time-consuming work is done.
        private void backgroundWorker1_DoWork(object sender, DoWorkEventArgs e)
        {
            BackgroundWorker worker = sender as BackgroundWorker;

            for (int i = 1; i <= 10; i++)
            {
                if (worker.CancellationPending)
                {
                    e.Cancel = true;
                    break;
                }
                else
                {
                    // Perform a time consuming operation and report progress.
                    System.Threading.Thread.Sleep(500);
                    worker.ReportProgress(i * 10);
                }
            }
        }

        // This event handler updates the progress.
        private void backgroundWorker1_ProgressChanged(object sender, ProgressChangedEventArgs e)
        {
            resultLabel.Text = (e.ProgressPercentage.ToString() + "%");
        }

        // This event handler deals with the results of the background operation.
        private void backgroundWorker1_RunWorkerCompleted(object sender, RunWorkerCompletedEventArgs e)
        {
            if (e.Cancelled)
            {
                resultLabel.Text = "Canceled!";
            }
            else if (e.Error != null)
            {
                resultLabel.Text = "Error: " + e.Error.Message;
            }
            else
            {
                resultLabel.Text = "Done!";
            }
        }
    }
}

答案 2 :(得分:1)

Application.Doevents 是此问题的关键。

这将刷新列表框并使程序响应其他内容。例如,按钮在循环中单击。所以你可以有一个按钮来中止长时间运行的任务。

private abort as boolean =false
sub doit()
    abort=false
    do until abort
        listbox1.additem "weee"
        my.application.doevents
    loop
end sub

sub abortbutton_click(...) handles abortbutton.click()
   abort=true
end sub

来自MSDN: http://msdn.microsoft.com/en-us/library/system.windows.forms.application.doevents.aspx

  

如果您在代码中调用DoEvents,   你的应用程序可以处理另一个   事件。例如,如果你有   将数据添加到ListBox和的表单   将DoEvents添加到您的代码和表单中   当另一个窗口出现时重新绘制   拖过它。如果你删除   您的代码和表单中的DoEvents   在点击事件之前不会重新绘制   按钮的处理程序完成   执行。

答案 3 :(得分:0)

我建议使用后台工作程序来运行循环并使用ProgressChanged事件实现UI更改。

这可能会让您开始http://msdn.microsoft.com/en-us/library/system.componentmodel.backgroundworker.aspx

答案 4 :(得分:0)

您正在UI线程中执行。

当UI线程在您的循环中时,它无法更新您的表单或响应系统消息(例如“重绘yer winder!”)。

这是新形式程序员面临的第一个真正的问题之一。你有两个选择。

首先,如果您的工作是短暂的,请承认您在逻辑工作时无法更新表单。放弃。停止想要移动那个进度条。停下来。做你的工作就完成了。

另一种选择是启动多线程。另外两个(此时)答案暗示了这一点。线程对新程序员来说很难。地狱,对大多数程序员来说很难。我留给你来决定是否值得尝试。但是,如果你这样做,意识到你无法从后台线程更新UI;你会Invoke()来到这个地方,相信你。