我正在开发一个WinForm项目,我在for
循环中有一个标签。我希望每次执行label.text
语句后都显示标签。但它并不是每次都显示出来,而是在for循环结束后显示出来。
我尝试使用Thread.Sleep()
来实现这一目标。但我不能。请帮我。
注意: - lblProgress是标签
这是我的编码。
for (int i = 1; i <= sourceTable.Rows.Count - 1; i++)
{
string checkout;
checkout= sourceTable.Rows[i].Field<string>(0);
dest = new SqlConnection(System.Configuration.ConfigurationManager.ConnectionStrings["local"].ConnectionString);
dest.Open();
destcmd = new SqlCommand(checkout, dest);
destcmd.ExecuteNonQuery();
dest.Close();
prcmail();
prcmessagecheck();
lblProgress.Text = "Hello World"+i;
Thread.Sleep(10000);
}
答案 0 :(得分:5)
每当您创建一个WinForm应用程序时,它都会转换为一个新进程并创建一个新线程。对用户界面的任何更新都在与您的进程相同的线程上完成。这意味着当您的应用程序正在进行繁忙的工作时,您的UI将被阻止,因为它们位于同一个线程上。这意味着,为了实现你想要实现的目标,你必须做一些额外的工作。
我们需要做的第一步是为您的工作例程创建一个函数(我们可以使用匿名函数,但由于您是C#的新手,我认为如果我们将其解析,它会更容易理解) ,像这样:
private void DoWork()
{
for (int i = 1; i <= sourceTable.Rows.Count - 1; i++)
{
string checkout;
checkout= sourceTable.Rows[i].Field<string>(0);
dest = new SqlConnection(System.Configuration.ConfigurationManager.ConnectionStrings["local"].ConnectionString);
dest.Open();
destcmd = new SqlCommand(checkout, dest);
destcmd.ExecuteNonQuery();
dest.Close();
prcmail();
prcmessagecheck();
lblProgress.Text = "Hello World"+i;
Thread.Sleep(1000); // I changed this from 10000 to 1000 (10 seconds down to 1 second)
}
}
接下来,我们需要创建一个执行DoWork()
函数的新线程。目前还不清楚&#34;触发&#34;是为了你的工作,但我会假设点击一下按钮:
private void button1_click(object sender, EventArgs e)
{
var work = new Thread(DoWork);
work.Start();
}
所以现在,只要有人点击按钮,我们就会启动一个新线程,在该线程中执行我们的DoWork
函数。新线程产生,然后立即返回执行,我们的GUI现在将在我们的线程在后台执行时实时更新。
但是等等!我们还有一个问题要处理。问题是Window的表单控件不是线程安全的,如果我们尝试从另一个线程更新控件,而不是GUI的线程,我们将得到一个跨线程操作错误。解决此问题的关键是使用InvokeRequired
和Invoke
。
首先,我们需要创建另一个只执行标签更新的函数:
private void SetProgressLabel(int progress)
{
lblProgress.Text = "Hello World" + progress;
}
在表单类中,我们还需要创建一个新的委托:
public partial class Form1 : Form
{
private delegate void ProgressCallback(int progress);
// ..
// The rest of your code
// ..
}
最后,将您的DoWork()
方法更改为以下内容:
private void DoWork()
{
for (int i = 1; i <= sourceTable.Rows.Count - 1; i++)
{
string checkout;
checkout= sourceTable.Rows[i].Field<string>(0);
dest = new SqlConnection(System.Configuration.ConfigurationManager.ConnectionStrings["local"].ConnectionString);
dest.Open();
destcmd = new SqlCommand(checkout, dest);
destcmd.ExecuteNonQuery();
dest.Close();
prcmail();
prcmessagecheck();
if (lblProgress.InvokeRequired)
{
lblProgress.Invoke(new ProgressCallback(SetProgressLabel), new object[] { i });
}
else
{
SetProgressLabel(i);
}
Thread.Sleep(1000); // I changed this from 10000 to 1000 (10 seconds down to 1 second)
}
}
这使用标签(源自Control
)InvokeRequired
属性来确定是否需要Invoke
。它会返回true
或false
。如果是false
,我们可以像我们通常那样调用我们的SetProgressLabel()
函数。如果是true
,我们必须使用Invoke
来调用我们的函数。
恭喜!您刚刚制作了第一个线程安全应用程序。
现在,正如旁边的说明,您没有正确释放和处置您的对象。我建议您将DoWork()
代码更改为以下内容:
private void DoWork()
{
for (int i = 1; i <= sourceTable.Rows.Count - 1; i++)
{
string checkout;
checkout = sourceTable.Rows[i].Field<string>(0);
using (dest = new SqlConnection(System.Configuration.ConfigurationManager.ConnectionStrings["local"].ConnectionString))
{
dest.Open();
using (destcmd = new SqlCommand(checkout, dest))
{
destcmd.ExecuteNonQuery();
dest.Close();
prcmail();
prcmessagecheck();
if (lblProgress.InvokeRequired)
{
lblProgress.Invoke(new ProgressCallback(SetProgressLabel), new object[] { i });
}
else
{
SetProgressLabel(i);
}
Thread.Sleep(1000); // I changed this from 10000 to 1000 (10 seconds down to 1 second)
}
}
}
}
由于我将IDisposable
包裹在using
块中,因此一旦资源超出范围,资源将自动处理。
答案 1 :(得分:1)
var ui = TaskScheduler.FromCurrentSynchronizationContext();
Task.Factory.StartNew(() =>
{
for (int i = 1; i <= sourceTable.Rows.Count - 1; i++)
{
string checkout;
checkout = sourceTable.Rows[i].Field<string>(0);
dest = new SqlConnection(System.Configuration.ConfigurationManager.ConnectionStrings["local"].ConnectionString);
dest.Open();
destcmd = new SqlCommand(checkout, dest);
destcmd.ExecuteNonQuery();
dest.Close();
prcmail();
prcmessagecheck();
var task = Task.Factory.StartNew(() =>
{
//Thread.Sleep(1000);
lblProgress.Text = "Hello World" + i;
}, CancellationToken.None, TaskCreationOptions.None, ui);
task.Wait();
}
});
答案 2 :(得分:1)
尽管线程化将是更理想的解决方案,但另一种解决方案是:
Application.DoEvents()
这将为UI线程提供更新时间。
实施例
for (int i = 1; i <= sourceTable.Rows.Count - 1; i++)
{
string checkout;
checkout= sourceTable.Rows[i].Field<string>(0);
dest = new SqlConnection(System.Configuration.ConfigurationManager.ConnectionStrings["local"].ConnectionString);
dest.Open();
destcmd = new SqlCommand(checkout, dest);
destcmd.ExecuteNonQuery();
dest.Close();
prcmail();
prcmessagecheck();
lblProgress.Text = "Hello World"+i;
Application.DoEvents();
}
答案 3 :(得分:0)
如果您在UI线程上执行上述代码,则只有在执行完整的for循环后才会刷新UI。根据您的需要,进度条/后台工作者的设置看起来很合适。