0

Within a user-defined class, I have a timer and it just won't start when I Timer.Enabled.

User-Defined Class:

  TSerialIndicator = public class
  private
    method TxTimerEvent(Sender:System.Object; e:System.EventArgs);
  public    
    Txlight:Label;
    Txtimer:System.Windows.Forms.Timer;
    constructor(mform:Form);
    method Transmit;
    method Receive;
  end;

Here is the constructor:

constructor TSerialIndicator(mform:Form);
begin
    TxLight := new Label;

    TxLight.AutoSize := false;

    TxLight.BorderStyle := BorderStyle.FixedSingle;

    TxLight.Location := new point(52,163);

    TxLight.Width := 20;
    TxLight.Height := 20;

    mform.Controls.Add(TxLight);

    TxTimer := new System.Windows.Forms.Timer;
    TxTimer.Interval:=1;

    TxTimer.Enabled:=false;
    TxTimer.Tick += new System.EventHandler(@TxTimerEvent);

    TxLight.BackColor := Color.Black;
end;

Here is Transmit method as defined:

method TSerialIndicator.Transmit;
begin
  TxLight.BackColor := Color.Red;

  if TxTimer.Enabled = false then
     TxTimer.Enabled:=true;
end;

Here is TxTimerEvent as defined:

method TSerialIndicator.TxTimerEvent(Sender:System.Object; e:System.EventArgs);
begin
    TxLight.BackColor := Color.Black;
    TxTimer.Enabled:=false;
end;

Here is how it is created and used:

Slight := new TSerialIndicator(self);
Slight.Transmit;

When I call Transmit from some other part of the program, it does its thing, but TxTimerEvent won't fire at all ever. I even tried Start/Stop its methods. It still didn't execute its Tick Event. However, I did notice that when I do enable the timer from within the constructor it does fire TxTimerEvent ONCE.

What am I doing wrong?

Thanks in advance,

Ken White
  • 123,280
  • 14
  • 225
  • 444
ThN
  • 3,235
  • 3
  • 57
  • 115
  • 1
    This does not look like C#... – PhonicUK Nov 09 '12 at 16:39
  • @akatakritos, I initially had 50 and still it never fired. So, thinking maybe it is too big of a interval, I set it to 1 to see if it has to do with timing. As you can see, it still doesn't work for me. – ThN Nov 09 '12 at 16:43
  • Thats one millisecond, it could change colors to red and back to black so quickly you didnt notice. – akatakritos Nov 09 '12 at 16:44
  • @akatakritos, regardless of the timing, I debugged the program to make sure its event actually is called. I ran the code with breakpoints. My program never hit the breakpoint for the TxTimerEvent. – ThN Nov 09 '12 at 16:46
  • Is Transmit being called on a different thread than the one that called the constructor? – akatakritos Nov 09 '12 at 16:53
  • Could you show the code that constructs the instance of TSerialIndicator used to call the Transmit method? – Steve Nov 09 '12 at 16:54

1 Answers1

4

With method names like "Transmit" and "Receive", it is likely that a thread is involved. Like the threadpool thread on which a SerialPort's DataReceived event runs. Or code that runs due to a System.Timers.Timer's Elapsed event. Etcetera.

Setting a System.Windows.Forms.Timer's Enabled property to true in a worker thread like that doesn't work, it is not a thread-safe class. It does what it normally does, creates a hidden window that uses Windows' SetTimer() method to get the Tick event to fire. But that window is created on a thread that doesn't pump a message loop. So Windows doesn't generate the WM_TIMER messages.

Use Control.Begin/Invoke() as necessary to ensure any code that's related to timers or controls runs on the UI thread.

Hans Passant
  • 922,412
  • 146
  • 1,693
  • 2,536
  • Hi - I could have named the Transmit and Receive methods as BigBird and Elmo respectively. :) However, I don't have a background thread associated with my `TSerialIndicator` class itself, although the label is added to the UI or main winform. It's true that I have a background thread for my serialport communication as you know. I will have to try your suggestion. – ThN Nov 09 '12 at 17:17
  • You are correct. If you are using System.Windows.Form.Timer, then it should be added or used on a winform. If you just want a timer to do something repeatedly at a specific time interval, then you use System.Timers.Timer. Otherwise, you will have to Control.invoke/Begin timer's code to run within UI thread. So, I went ahead and changed my timer to System.Timers.Timer and it is working as expected. Thank you, Hans and others. – ThN Nov 09 '12 at 19:20