Warning: file_get_contents(/data/phpspider/zhask/data//catemap/2/csharp/315.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
C# 如何实现带有计数器的弹出窗口_C#_Winforms - Fatal编程技术网

C# 如何实现带有计数器的弹出窗口

C# 如何实现带有计数器的弹出窗口,c#,winforms,C#,Winforms,我想在Windows窗体中实现一个简单的弹出窗口,当一些运行缓慢的进程正在执行时,它将向用户显示一个简单的计时器。前提很简单,;向用户显示确实发生了一些事情,并且应用程序没有冻结。请注意,这个缓慢运行的过程不是一个循环,也不是我可以利用的东西 我想要的是一个简单的弹出窗口,显示一些沿“已用时间:x秒”行的消息,其中x每秒递增一次 基本概念如下: public void test() { //Some code which does stuff //Popup window wi

我想在Windows窗体中实现一个简单的弹出窗口,当一些运行缓慢的进程正在执行时,它将向用户显示一个简单的计时器。前提很简单,;向用户显示确实发生了一些事情,并且应用程序没有冻结。请注意,这个缓慢运行的过程不是一个循环,也不是我可以利用的东西

我想要的是一个简单的弹出窗口,显示一些沿“已用时间:x秒”行的消息,其中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();
    }));            
}
这是我的弹出表单代码: 公共部分类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
    。如果在
    PLACE2
    中执行此操作,则将必须封送回GUI线程,以便能够安全地与WinForms框架交互。我为什么要提出此问题?这可能是因为长时间运行的进程根本不是从GUI线程中启动的你绝对需要这样做

  • 在长时间运行的流程完成后立即关闭表单。这只能在
    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