嗨,大家好我有一个问题,我希望很容易修复。
我是VB的新手,我正在使用Studio 2008,在程序中我试图让我有一个按钮,当点击开始一个相当长的循环,需要几分钟才能完成。虽然这种情况正在发生,我试图将循环进度的更新发送到我已设置的列表框。不幸的是,这些更新不会进入列表框,直到循环结束,我已尝试使用几种不同的类型(进度条/富文本框等)。在循环进行过程中,我似乎无法进行任何形式的更改。
我是否需要指定一些选项或事件,以便在循环中对表单进行更改?
答案 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()
来到这个地方,相信你。