How to Pass an Object into a Timer Event

How do I pass an object into a timer event?

The easiest way to do this is to change the event handler into an anonymous function. It allows you to pass the string at the point of declaration.

string theString = ...;
timer.Elapsed += (sender, e) => MyElapsedMethod(sender, e, theString);

static void MyElapsedMethod(object sender, ElapsedEventArgs e, string theString) {
...
}

Passing variables to timer event in a class

I tend to solve this problem using anonymous delegates.

public void PID(decimal _actualSpeed, Decimal _speedRequest, out Decimal _pwmAuto, out decimal _preValReg)
{
_pwmAuto = valReg;
_preValReg = valReg - 1;

// Because we cannot use [out] variables inside the anonymous degegates,
// we make a value copy
Decimal pwmAutoLocal = _pwmAuto;
Decimal preValRegLocal = _preValReg;

_timer = new System.Timers.Timer();
_timer.Interval = (3000);
_timer.Elapsed += (sender, e) => { HandleTimerElapsed(_actualSpeed, _speedRequst, pwmAutoLocal, preValRegLocal); };
_timer.Enabled = true;
// {....}

}

static void HandleTimerElapsed(Decimal actualSpeed, Decimal speedRequst, Decimal pwmAuto, Decimal preValReg)
{
// (...)
}

(You have to be mindful when the delegate accesses local variables from the enclosing block. Double-check the code to ensure the values stored in those variables will not change between the assignment of the event handler and the invocation of this handler).

How to pass a parameter to a timer event

You can inherit the Timer class to provide a parameter in the constructor like below. Or you can inherit the Timer class and provide a property:

 [TestClass]
public class TimerTester
{
[TestMethod]
public void TestYourTimer()
{
var timer1 = new TimerWithParameter("param1");
timer1.Interval = 1000;
timer1.ElapsedEvent += Timer_ElapsedEvent;
timer1.Start();

var timer2 = new TimerWithParameter("param2");
timer2.Interval = 1300;
timer2.ElapsedEvent += Timer_ElapsedEvent;
timer2.Start();

Thread.Sleep(5000);
}

private void Timer_ElapsedEvent(object source, ElapsedEventArgs e, string strParam)
{
Debug.WriteLine(strParam);
}
}

public delegate void ElapsedWithParameterDelegate(object source, ElapsedEventArgs e, string strParam);

public class TimerWithParameter:Timer
{
private readonly string _strParam;
public event ElapsedWithParameterDelegate ElapsedEvent;

public TimerWithParameter(string strParam)
{
_strParam = strParam;
this.Elapsed += TimerWithParameter_Elapsed;
}

private void TimerWithParameter_Elapsed(object sender, ElapsedEventArgs e)
{
ElapsedEvent?.Invoke(this, e, _strParam);
}
}

Pass an object to my timer

For a EventHandler that uses the standard implementation the sender parameter is always the object that raises the event, in your case it's the timerP object.

So you can get your MyClass object using

var timer = (Timer) sender;
var myClass = (MyClass) timer.Tag;

How to Pass an Argument into ElapsedEventHandler

It depends on where the local value i is for the delegate to regard it as "specific" to it's scope. As i is defined outside the loop, the delegate doesn't define it's own "copy" of this variable - as all the delegates expect the same i.

What you need to do is assign it to a variable that's "on the same level" as the delegate itself.

Not sure if I'm using the right language here to explain it. But this should work:

class Example
{
Dictionary<int, Timer> timer;

public Example()
{
timer = new Dictionary<int, Timer>();
for(int i = 0; i < 12; ++i)
{
int iInScopeOfDelegate = i;
timer.Add(i, new Timer(5000));
timer[i].Elapsed += delegate { TimerTickCustom(iLocalToDelegate ); };
}
}
public void Process() // called each cycle
{
for(int i = 0; i < 12; ++i)
{
timer[i].Start();
}
}
private void TimerTickCustom(int i)
{
// The value of i coming in does not match the dictionary key.
}
}

There's some interesting discussion if you know which words to type into the search engine (which of course, you can't really know until someone tells you).

Passing a variable to timer_tick

Just use a lambda when defining the tick event handler to close over the parameters you need:

private void AnimateKey(int Start, int Stop)
{
myTimer.Interval = 5;
myTimer.Tick += (s, args) => myTimer_Tick(Start, Stop);
myTimer.Enabled = true;
myTimer.Start();
}

private void myTimer_Tick(int Start, int Stop)
{
//Do stuff
}

Also note that the Tick event of the Timer that you're using will be fired in the UI thread, so there is no need for a lock; the code is already synchronized.

Can someone explain this events/method passing syntax in c#?

Flow:

create aTimer
find Main()
run SetTimer
init aTimer to 2000 milliseconds
set elapsed event to OnTimerEvent
set aTimer to auto reset
set aTimer enabled = true (Start the timer counting)
console write: Press enter to quit.
console write: App started at.
Wait for console input Return key. -This "pauses" the app.
stop aTimer
dispose aTimer
console write: Terminating

What does "+=" do?

Adds a method call to the event stack.

Is it attaching a method to be called whenever the event happens?

Yes, technically adding it to a list of methods to call when the event happens.

Is this spawned in the same thread as the event

Yes

and can multiple methods be called this way(like doing Elapsed += ArbitrarySecondMethod)

Yes, you could say: Elapsed += FirstThing; Elapsed += SecondThing; etc....

or is this overriding some response/method that is connected to "Elapsed" by default?

No, it's additive.

What would even be the default response?

Nothing, like a button without it's Click even assigned.

In the "OnTimedEvent", how are "source" and the elapsed events actually being passed to it?

Like any delegate, the object that triggered it is sent as the 'sender' along with a new instance of whatever eventargs class the event uses. When you assign a method using += it will check to make sure the method signature matches that patter (object, EventArg of some kind)

I don't see how 'e.SignalTime' even gets past the compiler stage.

Because it's a part of the ElapsedDEventArgs class. Notice the Elapsed at the front of that. It's not a default EventArgs class, but something inheriting from it, with more stuff. That extra stuff is populated by the timer when it fires the event.

How would "OnTimedEvent" even know a Timer object exists?

It doesn't really. It just knows that it is expecting to get both an Object thing and an ElapsedEventArgs thing.

Also, with parameters passed to the "OnTimedEvent", is that (Object x, Args y) a standard format used in this kind of situation

Yes, you'll find most, if not all, .Net event handlers to expect this kind of signature. Always Object sender first and then some kind of EventArg object. That event arg object will change depending on the event it's subscribing to. The MSDN Library should let you see what you'll need.

, what would I look up to learn more about how to use these types of 'add-on' methods(in a general context)

These are simple event methods so anything that talks about .Net events will go over this. For something more specific see:

MS Docs Events

MS Docs Event Patters

MS Docs Events Guide

How to raise an event from an object that uses a timer back to the UI thread

Added after comment:

You would need to pickup some latent detail that you can exploit to be able to accomplish that goal.

One thing that comes to mind is creating your own Forms/WPF timer at construction time and then use this and some synchronization to hide the details of coordination across threads. We can infer from your sample that construction of your poller should always happen in context of your consumer's thread.

This is a rather hack-ish way to accomplish what you want, but it can accomplish the deed because the construction of your poll-listener happens from the consumer's thread (which has a windows message pump to fuel the dispatches of Forms/WPF timers), and the rest of the operation of the class could occur from any thread as the forms Timer's tick will heartbeat from the original thread. As other comments and answers have noted, it would be best to reassess and fix the operating relationship between your polling operations and the consumer.

Here is an updated version of the class, PollingListener2 that uses a ManualResetEvent and a concealed System.Windows.Forms.Timer to ferry the polling notice across threads. Cleanup code is omitted for the sake of brevity. Requiring the use of IDisposable for explicit cleanup would be recommended in a production version of this class.

ManualResetEvent @ MSDN

public class PollingListener2
{
System.Timers.Timer timer = new System.Timers.Timer(1000);
public event EventHandler<EventArgs> Polled;

System.Windows.Forms.Timer formsTimer;
public System.Threading.ManualResetEvent pollNotice;

public PollingListener2()
{
pollNotice = new System.Threading.ManualResetEvent(false);
formsTimer = new System.Windows.Forms.Timer();
formsTimer.Interval = 100;
formsTimer.Tick += new EventHandler(formsTimer_Tick);
formsTimer.Start();
timer.Elapsed += new System.Timers.ElapsedEventHandler(PollNow);
timer.Start();
}

void formsTimer_Tick(object sender, EventArgs e)
{
if (pollNotice.WaitOne(0))
{
pollNotice.Reset();
var temp = Polled;
if (temp != null)
{
Polled(this, new EventArgs());
}
}
}

void PollNow(object sender, EventArgs e)
{
pollNotice.Set();
}
}

This has some precedent in the distant Win32 past where some people would use hidden windows and the like to maintain one foot in the other thread without requiring the consumer to make any significant changes to their code (sometimes no changes are necessary).


Original:

You could add a member variable on your helper class of type Control or Form and use that as the scope for a BeginInvoke() / Invoke() call on your event dispatch.

Here's a copy of your sample class, modified to behave in this manner.

public class PollingListener
{
System.Timers.Timer timer = new System.Timers.Timer(1000);
public event EventHandler<EventArgs> Polled;

public PollingListener(System.Windows.Forms.Control consumer)
{
timer.Elapsed += new System.Timers.ElapsedEventHandler(PollNow);
timer.Start();
consumerContext = consumer;
}

System.Windows.Forms.Control consumerContext;

void PollNow(object sender, EventArgs e)
{
var temp = Polled;
if ((temp != null) && (null != consumerContext))
{
consumerContext.BeginInvoke(new Action(() =>
{
Polled(this, new EventArgs());
}));
}
}
}

Here's a sample that shows this in action. Run this in debug mode and look at your output to verify that it is working as expected.

public partial class Form1 : Form
{
public Form1()
{
InitializeComponent();

listener = new PollingListener(this);
}

PollingListener listener;

private void Form1_Load(object sender, EventArgs e)
{
listener.Polled += new EventHandler<EventArgs>(listener_Poll);
}

void listener_Poll(object sender, EventArgs e)
{
System.Diagnostics.Debug.WriteLine("ding.");
}
}


Related Topics



Leave a reply



Submit