如何使用计数器实现弹出窗口

时间:2014-04-30 13:02:11

标签: c# winforms

我想在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();
    }
}

5 个答案:

答案 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毫秒:

enter image description here

  • 创建标签以托管您想要的文字。我们称之为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
    
  • 在长时间运行的进程开始之前显示表单。这可以在名为PLACE1PLACE2的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());

感谢大家试图帮助我。