0

I have a function in C# which, at the outset, sets the value of a GUI DateTimePicker object to today's date (time = midnight), then does other stuff. When executed via GUI button, the function (DBIO_Morning) runs fine. But, executed via timed action:

private void SetupTimedActions()
{
   ...

   DateTime ref_morning = new DateTime(DateTime.Today.Year, DateTime.Today.Month, DateTime.Today.Day, 8, 16, 0);
   if (DateTime.Now < ref_morning)
      At.Do(() => DBIO_Morning(), ref_morning);
   ...
}

it fails in the second line:

private void DBIO_Morning()
{
   DateTime date_current = new DateTime(DateTime.Today.Year, DateTime.Today.Month, DateTime.Today.Day, 0, 0, 0);
   DTPicker_start.Value = date_current;
   ...
}

( At.Do object is from the third answer here: C# execute action after X seconds )

Community
  • 1
  • 1
Jack Huang
  • 531
  • 1
  • 5
  • 12
  • That code has serious problems. Hiding a thread is a really bad idea. A garbage collection making it disappear without a trace bakes the cake. Throw it away, just use a Winforms Timer. Don't hide it. – Hans Passant Dec 17 '11 at 15:12

2 Answers2

0

Controls are not thread-safe, meaning you must not call a control's methods from another thread. You can wait until the control's thread is ready to handle your action using Control.Invoke:

private void DBIO_Morning()
{
    DateTime date_current = new DateTime(DateTime.Today.Year, DateTime.Today.Month, DateTime.Today.Day, 0, 0, 0);
    Action setValue = () => DTPicker_start.Value = date_current;
    if (DTPicker_start.InvokeRequired)
        DTPicker_start.Invoke(setValue);
    else
        setValue();
}
  • I tested your suggestion and it works, but I have additional GUI calls later in the function and in other functions called by timers. So, instead of changing every relevant GUI call, I'll try to limit my changes to the timing functions by implementing the second solution below. Thanks, though! – Jack Huang Dec 17 '11 at 14:47
  • Assuming all controls are created in the same thread, you can do everything in a single `Control.Invoke` call, you don't have to create separate `Action`s for each method invocation. I agree though, using the other `Timer` is probably easier, and avoids a thread where you don't really need one. Best of luck. –  Dec 17 '11 at 14:51
0

You are trying to modify GUI elements from another thread, created implicitly by At.Do(). See this thread.

Using System.Windows.Forms.Timer in At.Do() instead of System.Threading.Timer will probably solve the problem. (Just change new Timer(...) to new System.Windows.Forms.Timer(...).)

Community
  • 1
  • 1
user1071136
  • 15,636
  • 4
  • 42
  • 61
  • Literally just changing `System.Threading.Timer` to `System.Windows.Forms.Timer` didn't work, because there's no 4-argument overload for `System.Windows.Forms.Timer`. But, it's likely the more convenient fix in general because only the timing functions are affected. Do you know how to make a `System.Windows.Forms.Timer` call duplicate the functionality of a `new System.Threading.Timer(TimerCallback timer, Action action, int delayMilliseconds, int interval)` ? – Jack Huang Dec 17 '11 at 14:42
  • [This page](http://msdn.microsoft.com/en-us/library/xdh6857z.aspx) has an example. You create a `timer`, set its `Tick` event and `Interval` property, then call its `Start` method. You don't have a one-line constructor for this, but its pretty much the same. – user1071136 Dec 17 '11 at 16:06
  • I looked through that page before, but how do I make the timed event a once-daily (or one-off) occurrence at a set time? I can calculate the time difference between runtime and event-schedule-time and use that as Interval, but how do I prevent event execution from recurring at that set Interval? – Jack Huang Dec 17 '11 at 17:00
  • Call `myTimer.Stop();` from inside the event handler. (see the example in the same page) – user1071136 Dec 17 '11 at 18:27