我想在Windows窗体中实现一个简单的弹出窗口,它会向用户显示一个简单的计时器,同时执行一些运行缓慢的进程。前提很简单;向用户显示确实发生了某些事情并且应用程序未被冻结。请注意,这个缓慢运行的过程不是循环,也不是我可以利用的东西。
我想要的是一个简单的弹出窗口,显示“Elapsed time:x seconds”行中的一些消息,其中x每秒递增一次。
基本概念如下:
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();
}));
}
这是我的弹出窗体代码: public partial class frmProgressTimer:Form { private int counter = 0; private Timer timer1;
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();
}
}
答案 0 :(得分:1)
这实际上很容易做到。创建对话框,定义在显示的非UI线程中进行的长时间运行操作,向该操作添加延续,在任务完成时关闭对话框,然后显示对话框。
MyDialog dialog = new MyDialog();
dialog.Shown += async (sender, args) =>
{
await Task.Run(() => DoLongRunningWork());
dialog.Close();
};
dialog.ShowDialog();
随着时间推移滴答作响的代码应该完全包含在对话框中,并且基于问题,您似乎已经通过简单的Timer
来控制它。
答案 1 :(得分:0)
制作一个新窗体,弹出窗口并显示计时器。这样,它不会被主表单上的所有工作中断,并且计时器将持续工作。
记住在显示new时使用newForm.ShowDialog()而不是newForm.Show()。你可以谷歌差异
答案 2 :(得分:0)
我只是在一个单独的线程上开始你的工作。使用计时器输出启动模态表单。要显示计时器,请使用实际的计时器实例设置每秒更新一次。当计时器事件触发更新对话框时。
最后,一旦你的线程完成关闭对话框,你的主表单就会再次激活。
答案 3 :(得分:0)
首先,您需要让用户不可关闭(好像模态对话框不够烦人)但可关闭码。您可以通过订阅表单的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;
}
创建Timer
,提供Tick
事件处理程序,启用它并将其Interval设置为500毫秒:
label1
。您的Tick
事件处理程序内部和周围执行以下操作:
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
的2个地方(在上一部分代码中标记)中的任何一个地方完成。如果您在PLACE2
中执行此操作,则必须封送回GUI线程的调用,以便能够安全地与WinForms框架进行交互。我为什么提起这件事?这可能是因为长时间运行的过程根本不是从GUI线程中启动的,而你绝对需要这样做。
在长时间运行的过程结束后立即关闭表单。这只能在PLACE3
中完成,您绝对需要整理电话。
要包装前面的2个项目符号和答案,您可以这样做:
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
}
答案 4 :(得分:-2)
最后,我设法以最简单的方式解决了这个问题。它就像一个魅力。以下是如何做到这一点:
//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();
}));
您可以在原始帖子/问题中看到弹出窗口的代码,唯一的区别是tick函数将标签文本更改为:
labelText.Text = string.Format("Elapsed Time: {0} seconds.", counter.ToString());
感谢大家试图帮助我。