如何在for循环中每次更新标签
我正在开发一个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(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); }
无论何时创建WinForm应用程序,它都会转换为新进程并创建新线程。 用户界面的任何更新都在与您的进程相同的线程上完成。 这意味着当您的应用程序正在“忙碌工作”时,您的UI将被阻止,因为它们位于同一个线程上。 这意味着,为了实现你想要实现的目标,你必须做一些额外的工作。
我们需要做的第一步是为你的工作例程创建一个函数(我们可以使用一个匿名函数,但是因为你是C#的新手,我认为如果我们分解它会更容易理解),如下所示:
private void DoWork() { for (int i = 1; i <= sourceTable.Rows.Count - 1; i++) { string checkout; checkout= sourceTable.Rows[i].Field(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()
函数的新线程。 它不清楚你的工作的“触发器”是什么,但我将假设它点击一下按钮:
private void button1_click(object sender, EventArgs e) { var work = new Thread(DoWork); work.Start(); }
所以现在,只要有人点击按钮,我们就会启动一个新线程,在该线程中执行我们的DoWork
function。 新线程产生,然后立即返回执行,我们的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(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(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
块,所以一旦资源超出范围,资源将自动处理掉。
var ui = TaskScheduler.FromCurrentSynchronizationContext(); Task.Factory.StartNew(() => { for (int i = 1; i <= sourceTable.Rows.Count - 1; i++) { string checkout; checkout = sourceTable.Rows[i].Field(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(); } });
虽然线程化将是更理想的解决方案,但另一种解决方案是:
Application.DoEvents()
这将为UI线程提供更新时间。
例
for (int i = 1; i <= sourceTable.Rows.Count - 1; i++) { string checkout; checkout= sourceTable.Rows[i].Field(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(); }
如果您在UI线程上执行上述代码,则只有在执行完整的for循环后才会刷新UI。 根据您的需要,进度条/后台工作者类型设置看起来合适。
上述就是C#学习教程:如何在for循环中每次更新标签分享的全部内容,如果对大家有所用处且需要了解更多关于C#学习教程,希望大家多多关注—计算机技术网(www.ctvol.com)!
本文来自网络收集,不代表计算机技术网立场,如涉及侵权请联系管理员删除。
ctvol管理联系方式QQ:251552304
本文章地址:https://www.ctvol.com/cdevelopment/936375.html