【问题标题】:I have a C# Windows Forms Application and I want the user to be able to schedule a recurring task我有一个 C# Windows 窗体应用程序,我希望用户能够安排重复任务
【发布时间】:2015-01-04 16:44:19
【问题描述】:

我的应用程序根据 UI 上输入的表单中的数据发送一封电子邮件。 UI 还允许他们将其“安排”为工作。我真的不想使用外部课程,例如石英。我也不想使用任务调度程序,因为这需要完全重写我的应用程序......我知道这可以通过计时器来完成,并且我尝试使用 Windows.Forms.Timer,但是,我不是得到想要的结果。我希望它以用户指定的时间间隔和时间运行,直到设置的结束日期。

private void Submit_Btn_Click(object sender, EventArgs e)
    {
        bool run = false;
        Location_Alert_Timer.Tick += new EventHandler(Location_Alert_Timer_Tick);

        if (Schedule_Chk.Checked == true)
        {
            run = true;

            if (Recur_Txt.Text != "" || Recur_Txt.Text != "0")
            {
                while (run == true)
                {

                    if ((Convert.ToDateTime(DateTime.Now.ToString("MM/dd/yyyy HH:mm")) <= Convert.ToDateTime(End_Date.Value.ToString("MM/dd/yyyy") + " " + Recur_Time_Txt.Text)))
                    {
                        //Execute_Command();
                        DateTime dt1;
                        DateTime dt2;
                        TimeSpan ts = new TimeSpan(Convert.ToInt16(Recur_Txt.Text),0,0,0);
                        //TimeSpan ts = new TimeSpan(0,0,30);
                        dt1 = Convert.ToDateTime(DateTime.Now.ToString("MM/dd/yyyy HH:mm"));
                        dt2 = Convert.ToDateTime(DateTime.Now.ToString("MM/dd/yyyy") + " " + Recur_Time_Txt.Text);
                        if  (dt1 >= dt2)
                        {
                            Execute_Command();
                            Location_Alert_Timer.Interval = (Convert.ToDateTime(DateTime.Now.ToString("MM/dd/yyyy") + " " + Recur_Time_Txt.Text + ":00.000").Add(ts) - DateTime.Now).Milliseconds;
                            Location_Alert_Timer.Start();
                            while (Convert.ToDateTime(DateTime.Now.ToString("MM/dd/yyyy HH:mm")) <= Convert.ToDateTime(End_Date.Value.ToString("MM/dd/yyyy") + " " + Recur_Time_Txt.Text))
                            {
                                Application.DoEvents();
                            }
                        }
                        else
                        {
                            Location_Alert_Timer.Interval = (Convert.ToDateTime(DateTime.Now.ToString("MM/dd/yyyy") + " " + Recur_Time_Txt.Text + ":00.000") - DateTime.Now).Milliseconds;
                            Location_Alert_Timer.Start();
                            while (exitFlag == false)
                            {
                                Application.DoEvents();
                            }
                        }
                    }
                    else
                    {
                        Location_Alert_Timer.Stop();
                        run = false;
                    }
                }
            }
            else
            {
                Execute_Command();
            }
        }
        else
        {
            Execute_Command();
        }
    }

    void Location_Alert_Timer_Tick(object sender, EventArgs e)
    {
        if (Convert.ToDateTime(DateTime.Now.ToString("MM/dd/yyyy HH:mm")) <= Convert.ToDateTime(End_Date.Value.ToString("MM/dd/yyyy") + " " + Recur_Time_Txt.Text))
        {
            Execute_Command();
        }
        else
        {
            exitFlag = true;
        }
    }

我的计时器事件似乎没有正确触发,我知道这是因为我没有正确设置它们......

这是表单 UI:

如果将来有人有类似的实现,@itsmatt 的解释让我重写了我的整个活动,从头开始……下面是最终的实现,效果非常好:

        private void Submit_Btn_Click(object sender, EventArgs e)
    {
        bool run = false;


        if (Schedule_Chk.Checked == true)
        {
            run = true;

            if (Recur_Txt.Text != "" || Recur_Txt.Text != "0")
            {
                while (run == true)
                {
                    if ((Convert.ToDateTime(DateTime.Now.ToString("MM/dd/yyyy HH:mm")) <= Convert.ToDateTime(End_Date.Value.ToString("MM/dd/yyyy") + " " + Recur_Time_Txt.Text)))
                    {
                        DateTime dt1;
                        DateTime dt2;
                        TimeSpan ts = new TimeSpan(Convert.ToInt16(Recur_Txt.Text),0,0,0);
                        dt1 = Convert.ToDateTime(DateTime.Now.ToString("MM/dd/yyyy HH:mm"));
                        dt2 = Convert.ToDateTime(DateTime.Now.ToString("MM/dd/yyyy") + " " + Recur_Time_Txt.Text);
                        if (dt1 >= dt2)
                        {
                            Execute_Command();
                            Location_Alert_Timer.Interval = Convert.ToInt32((Convert.ToDateTime(DateTime.Now.ToString("MM/dd/yyyy") + " " + Recur_Time_Txt.Text + ":00.000").Add(ts) - DateTime.Now).TotalMilliseconds);
                            Location_Alert_Timer.Start();
                            timerActive = true;
                            TimerHold();
                        }
                        else
                        {
                            Location_Alert_Timer.Interval = Convert.ToInt32((Convert.ToDateTime(DateTime.Now.ToString("MM/dd/yyyy") + " " + Recur_Time_Txt.Text + ":00.000") - DateTime.Now).TotalMilliseconds);
                            singleuse = true;
                            Location_Alert_Timer.Start();
                            timerActive = true;
                            TimerHold();
                            MessageBox.Show(DateTime.Now.ToString());
                            MessageBox.Show(DateTime.Now.Add(ts).ToString());
                            MessageBox.Show((Convert.ToDateTime(DateTime.Now.Add(ts).ToString()) - DateTime.Now).TotalMilliseconds.ToString());
                            Location_Alert_Timer.Interval = Convert.ToInt32((Convert.ToDateTime(DateTime.Now.Add(ts).ToString()) - DateTime.Now).TotalMilliseconds);
                            Location_Alert_Timer.Start();
                            timerActive = true;
                            TimerHold();
                        }
                    }
                    else
                    {
                        Location_Alert_Timer.Stop();
                        run = false;
                    }
                }
            }
        }
    }

    public void TimerHold()
    {
        while (timerActive)
        {
            Application.DoEvents();
        }
    }

    void Location_Alert_Timer_Tick(object sender, EventArgs e)
    {
        if (singleuse)
        {
            if (uses < 1)
            {
                if (Convert.ToDateTime(DateTime.Now.ToString("MM/dd/yyyy HH:mm")) <= Convert.ToDateTime(End_Date.Value.ToString("MM/dd/yyyy") + " " + Recur_Time_Txt.Text))
                {
                    uses++;
                    //DoItOnce();
                    Execute_Command();
                }
                else
                {
                    timerActive = false;
                }
            }
            else
            {
                singleuse = false;
                timerActive = false;
                Location_Alert_Timer.Stop();
            }
        }
        else
        {
            if (Convert.ToDateTime(DateTime.Now.ToString("MM/dd/yyyy HH:mm")) <= Convert.ToDateTime(End_Date.Value.ToString("MM/dd/yyyy") + " " + Recur_Time_Txt.Text))
            {
                //DoIt();
                Execute_Command();
            }
            else
            {
                timerActive = false;
            }
        }
    }

【问题讨论】:

  • 您是否假装您的应用程序永远运行?来自 msdn "此计时器已针对在 Windows 窗体应用程序中的使用进行了优化,并且必须在窗口中使用。"
  • 这类作品使用quartz-scheduler.net
  • 为什么要在按钮回调中注册tick事件?在别处注册。这在 UI 线程上运行。那是你要的吗?也许不是,对吧?还有其他可用的计时器不在 UI 线程上。
  • 应用程序现在表现如何..?它至少有响应吗..?
  • @kclewis 像您一样在提交按钮单击处理程序中注册滴答事件处理程序将导致第一次单击按钮时调用 Location_Alert_Timer_Tick() ,下次单击两次等。它是一遍又一遍地注册相同的回调函数,这会导致在引发滴答事件时多次调用该回调函数。相反,在初始化时 += 那个事件处理程序(可能在构造函数或其他一些 setup() 函数中)。这样,当引发计时器滴答事件时,您将获得一个且只有一个对 Location_Alert_Timer_Tick() 的函数调用。

标签: c# .net winforms timer .net-3.5


【解决方案1】:

用这个例子怎么样?

A-Simple-Scheduler-in-Csharp

【讨论】:

  • 我认为这个例子可行,但我希望尽可能少地重新定义。你的回答很好,我可能会在未来的实施中使用它,但现在对我来说可能有点复杂......我是新手,正在寻找简单的解决方案,但这让我能够做这项工作。
  • 好吧,恐怕没有一个真正简单可靠的解决方案......您也许可以使用示例项目中的一些代码。在表单应用程序中安排任务时,我总是觉得我无法控制。
【解决方案2】:

@kclewis 在提交按钮单击处理程序中注册滴答事件处理程序将导致 Location_Alert_Timer_Tick() 在第一次单击按钮时被调用,下一次被调用两次,等等。它正在注册相同的回调函数一遍又一遍,这会导致在引发滴答事件时多次调用它。相反, += 初始化时的事件处理程序(可能在构造函数或其他一些 setup() 函数中)。这样,当引发计时器滴答事件时,您将获得一个且只有一个对 Location_Alert_Timer_Tick() 的函数调用。通过@itsmatt 的社区 wiki 发布

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2015-12-23
    • 1970-01-01
    • 1970-01-01
    • 2015-03-26
    • 1970-01-01
    • 2021-06-09
    • 2023-02-10
    相关资源
    最近更新 更多