C# 如何实现带有计数器的弹出窗口
我想在Windows窗体中实现一个简单的弹出窗口,当一些运行缓慢的进程正在执行时,它将向用户显示一个简单的计时器。前提很简单,;向用户显示确实发生了一些事情,并且应用程序没有冻结。请注意,这个缓慢运行的过程不是一个循环,也不是我可以利用的东西 我想要的是一个简单的弹出窗口,显示一些沿“已用时间:x秒”行的消息,其中x每秒递增一次 基本概念如下:C# 如何实现带有计数器的弹出窗口,c#,winforms,C#,Winforms,我想在Windows窗体中实现一个简单的弹出窗口,当一些运行缓慢的进程正在执行时,它将向用户显示一个简单的计时器。前提很简单,;向用户显示确实发生了一些事情,并且应用程序没有冻结。请注意,这个缓慢运行的过程不是一个循环,也不是我可以利用的东西 我想要的是一个简单的弹出窗口,显示一些沿“已用时间:x秒”行的消息,其中x每秒递增一次 基本概念如下: public void test() { //Some code which does stuff //Popup window wi
public void test()
{
//Some code which does stuff
//Popup window with counter
//Perform long running process
//Close popup window with counter
//Some other code which does other stuff
}
我试着用各种方法,包括后台工作、线程,当然还有定时器。但我并没有设法使它按我所希望的那样工作。我更愿意不发布我的任何代码,这样就不会“引导”响应到一种特定的方式
那么,做这项工作的最佳方式是什么呢
谢谢
更新:
在回复一些评论时,由于我无法在回复部分粘贴任何代码,我正在编辑我的原始问题以适应这一点。我尝试的实现之一是在单独的线程中生成弹出窗口。虽然没有运行时错误,但弹出窗口没有正确刷新。它确实会弹出,但里面不会显示任何文本,计数器也不会刷新。代码如下:
private void test()
{
frmProgressTimer ofrmProgressTimer = new frmProgressTimer(); //Instance of popup Form
System.Threading.Tasks.Task loadTask = new System.Threading.Tasks.Task(() => ProgressTimer(ofrmProgressTimer));
loadTask.Start();
//Perform long running process
System.Threading.Tasks.Task cwt = loadTask.ContinueWith(task => EndProgressTimer(ofrmProgressTimer));
}
private void ProgressTimer(frmProgressTimer ofrmProgressTimer)
{
ofrmProgressTimer.Show();
ofrmProgressTimer.Invoke((Action)(() =>
{
ofrmProgressTimer.startTimer();
}));
}
private void EndProgressTimer(frmProgressTimer ofrmProgressTimer)
{
ofrmProgressTimer.Invoke((Action)(() =>
{
ofrmProgressTimer.stopTimer();
ofrmProgressTimer.Close();
}));
}
这是我的弹出表单代码:
公共部分类frmProgressTimer:表单
{
专用整数计数器=0;
私人定时器1
public frmProgressTimer()
{
InitializeComponent();
timer1 = new Timer();
timer1.Interval = 1000;
timer1.Tick += new EventHandler(timer1_Tick);
}
public void startTimer()
{
timer1.Start();
}
public void stopTimer()
{
timer1.Stop();
}
private void timer1_Tick(object sender, EventArgs e)
{
counter += 1;
labelText.Text = counter.ToString();
}
}
创建一个新窗体,它将弹出,并显示一个计时器。这样它就不会被主窗体上的所有工作中断,计时器将持续工作
请记住,当显示新的from时,要使用newForm.ShowDialog()而不是newForm.Show()。您可以通过谷歌搜索差异我只需在单独的线程上开始工作。使用计时器输出启动模式窗体。要显示计时器,请使用设置为每秒更新的实际计时器实例。当计时器事件触发时,更新您的对话框 最后,线程完成后,关闭对话框,使主窗体再次处于活动状态。
- 首先,您需要使其不可由用户关闭(好像模态对话框不够烦人),但可由您的代码关闭。您可以通过订阅表单的
事件来完成此操作。假设您的弹出表单的名称为FormClosing
:Form2
private bool mayClose = false; public void PerformClose() { this.mayClose = true; this.Close(); } private void Form2_FormClosing(object sender, FormClosingEventArgs e) { if (!this.mayClose) e.Cancel = true; }
- 创建一个
,提供一个计时器
事件处理程序,启用它并将其间隔设置为500毫秒:勾选
- 创建一个标签来承载所需的文本。我们称之为
label1
- 在
勾选事件处理程序内部和周围执行以下操作:
private DateTime appearedAt = DateTime.UtcNow; private void timer1_Tick(object sender, EventArgs e) { int seconds = (int)(DateTime.UtcNow - this.appearedAt).TotalSeconds; this.label1.Text = string.Format(@"Ellapsed seconds: {0}", seconds); }
- 确保长时间运行的进程发生在后台线程上,而不是GUI线程上。
假设您的长时间运行的流程可以被认为是一个名为
的方法的执行。 如果是这种情况,那么您需要从辅助线程调用该方法MyProcess
// PLACE 1: GUI thread right here Thread thread = new Thread(() => { // PLACE 2: this place will be reached by the secondary thread almost instantly MyProcess(); // PLACE 3: this place will be reached by the secondary thread // after the long running process has finished }); thread.Start(); // PLACE 4: this place will be reached by the GUI thread almost instantly
- 在长时间运行的流程开始之前显示表单。这可以在两个位置中的任意一个位置完成(在代码的前一部分中标记)调用
或PLACE1
。如果在PLACE2
中执行此操作,则将必须封送回GUI线程,以便能够安全地与WinForms框架交互。我为什么要提出此问题?这可能是因为长时间运行的进程根本不是从GUI线程中启动的你绝对需要这样做PLACE2
- 在长时间运行的流程完成后立即关闭表单。这只能在
中完成,您绝对需要封送一个调用PLACE3
- 要总结前面的两个项目和答案,您可以执行以下操作:
private void DoIt() { Form2 form2 = new Form2(); Action showIt = () => form2.Show(); Action closeIt = () => form2.PerformClose(); // PLACE 1: GUI thread right here Thread thread = new Thread(() => { form2.BeginInvoke(showIt); // PLACE 2: this place will be reached by the secondary thread almost instantly MyProcess(); form2.BeginInvoke(closeIt); // PLACE 3: this place will be reached by the secondary thread // after the long running process has finished }); thread.Start(); // PLACE 4: this place will be reached by the GUI thread almost instantly }
//Create an instance of the popup window
frmProgressTimer ofrmProgressTimer = new frmProgressTimer();
Thread thread = new Thread(() =>
{
ofrmProgressTimer.startTimer();
ofrmProgressTimer.ShowDialog();
});
thread.Start();
//Perform long running process
ofrmProgressTimer.Invoke((Action)(() =>
{
ofrmProgressTimer.stopTimer();
ofrmProgressTimer.Close();
}));
您可以在原始帖子/问题中看到弹出窗口的代码,唯一的区别是勾号功能将标签文本更改为:
labelText.Text = string.Format("Elapsed Time: {0} seconds.", counter.ToString());
感谢大家尝试帮助我。这其实很容易做到。创建对话框,定义长时间运行的操作,在显示时在非UI线程中进行,在该操作中添加一个延续,在任务完成时关闭对话框,然后显示对话框
MyDialog dialog = new MyDialog();
dialog.Shown += async (sender, args) =>
{
await Task.Run(() => DoLongRunningWork());
dialog.Close();
};
dialog.ShowDialog();
随着时间的推移滴答作响的代码应该完全包含在对话框中,基于这个问题,您似乎已经用一个简单的
计时器控制住了。您知道这个缓慢运行的过程总是要花费一定的时间吗?相反,您可能需要考虑使用后台工作程序来我会显示一个进度条,这样他们就知道已经完成了X%,而不是(假设)还剩1分钟,59秒,等等。应该会有帮助。不,不幸的是,这个过程所花费的时间是未知的和可变的。这里的想法不是显示还有多少工作要做,因为我不知道,也不能以任何方式计算。这个想法是向用户显示正在进行某些事情,并且这个过程没有冻结。@sallushan请发布一个a回答并解释为什么这会是一个更好的主意。我不是说这不是一个清晰的观点,而是关于正在发生的事情,我们想要完成的事情和使用更小的粒度比教授框架、类和传统实践更具教育性和有效性。@Eduard Dumitru,我看到您的代码的一个问题是位置4。因为GUI线程几乎立即到达his,这意味着在此点之后依赖于MyProcess()的任何代码都将失败。这就是为什么我更喜欢在线程中使用弹出窗口的原因,而不是MyProcess()位置4是不可避免的。它只是存在的。是否放置依赖于MyProcess()的代码是否在第四位取决于你。这就是为什么我指出它。Y