Part of my program receives input over a network connection, and sends a message back. I want to limit the number of times a certain input can trigger a message, so the program can't be overloaded.
I have a background worker that waits for the input, and then when it receives the certain input, it calls into a static class that will determine if it has been enough time since the last input. I'm using a
System.Windows.Forms.Timer
To do this. It looks like this (everything is public so I can debug):
public static class InputResponse
{
public static System.Windows.Forms.Timer Time = new System.Windows.Forms.Timer();
public static void CreateTimer()//set all the properties of the timer
{
Time.Interval = 3000;
Time.Tick += new EventHandler(Time_Tick);
}
public static void InputAReceived()
{
if (Time.Enabled) //if the timer is running, do nothing
return;
else
{
//send response here
Time.Start();
}
}
public static void Time_Tick(object sender, EventArgs e)
{
Console.WriteLine("Time_Tick");
Time.Stop();
}
}
The problem is, the Time_Tick method never gets called from the timer. I can use Invoke() to trigger the method like so,
EventHandler testHandler = new EventHandler(InputResponse.Time_Tick);
testHandler.Invoke(sender, e);//triggered by a button
which writes to the console like it should, but just waiting for the timer doesn't work. It will send the response once, and then won't send it again, since the timer never gets stopped.
The ridiculous thing is I have it working almost exactly the same in another class. The only difference is that the timer is constantly running.
What am I missing?
One problem with your code is that it is using the System.Windows.Forms.Timer class from a background thread and not associated with a window. This violates the instructions given in the documentation:
This timer is optimized for use in Windows Forms applications and must be used in a window.
For timers not related to GUI objects, use System.Timers.Timer.
This may or may not be the cause of the problems you're having, but it's one thing you'll need to address for your code to work correctly.
Related
lets say i have 2 Windows ( loginWindow and MainWindow ). When a certain time runs out(lets say 30 seconds) i want that my Program automatically logs of ( open loginWindow ) again. I tried to code a global stopwatch that i can reset from where ever i want to in my Programm so i can open loginWindow after the time runs out.
This is what i got: (also im very new into WPF)
GlobalClass :
public static Stopwatch stopwatch = new Stopwatch();
public static long timeSpan;
public static void startStopwatch()
{
stopwatch.Start();
Thread.Sleep(10000);
stopwatch.Stop();
Console.WriteLine(stopwatch.ElapsedMilliseconds);
timeSpan = stopwatch.ElapsedMilliseconds / 1000;
}
public static void restartStopwatch()
{
stopwatch.Stop();
startStopwatch();
}
Page1 (MainWindow):
public Page1()
{
InitializeComponent();
GlobalClass.startStopwatch();
if(GlobalClass.timeSpan == 10)
{
MessageBox.Show("Stopwatch reached end"); //also jump back to loginWindow
}
}
so this doesnt work but i dont know why. my Question is: before i go more into Stopwatch should i solve this Problem with Stopwatch or use a different way to achieve what i want. also why is this not working:(.
thanks in Advance <3
You can implement a timer as a Singelton and register the navigation handler (or your navigation concept) to the TickEvent.
The event is triggered when the time you set has expired and you can return to the logon screen.
You can now start/stop/reset from different locations.
public class GlobalTimer : Timer
{
private GlobalTimer instance;
public GlobalTimer Instance => instance ?? (instance = new GlobalTimer());
private GlobalTimer()
{
}
}
Edit:
A StopWatch, as the name suggests, is a class that measures a time span.
What you want to have is more of an alarm clock that informs you when a certain time has elapsed.
Your program does not work because you are waiting in the UI creation. This means that the UI is not yet drawn and you tell the thread who is drawing
wait 10 seconds and then draw the previous one again. Page1 is therefore not displayed.
Therefore never Thread.wait(x) in a UI thread (your UI freezes when the thread is not working (sleeping)).
Here is a Link to the Timer Class for more information how an Timer work: https://msdn.microsoft.com/en-us/library/system.timers.timer(v=vs.110).aspx
Edit 2:
As Clemens said, the DispatcherTimer is the better option for a UI application because events are processed with the UI thread.
If you want to use the "normal" timer, you may have to call UI elements(variables/Methoen/etc.) using a dispatcher.
Otherwise data will be managed in different threads which does not work.
See the answer/more infos here: WPF - Threads and change variable
My Question is more about getting to the same destination, but there must be another way. Right now im creating a DateTime and compare that to another DateTime and check, if the time difference I set up might be right. So far so good but I just can't accept that I create a new propertie everytime the loop will get into that code.
Is there any possible way to get to the same destination, but in some kind of more effective way?
I got you guys some example code here:
private void RunService()
{
// Runs as long as the service didn't got a stop call.
while (!SetStop)
{
//Get MinutesToWait
this.MinutesToWait = 5;
DateTime CheckRunTime = this.LastRun;
CheckRunTime.AddMinutes(this.MinutesToWait);
if (DateTime.Now >= CheckRunTime)
{
// Imagine some good and smart and totally runnable code?
}
}
}
If I understood correctly, what you want to do is execute a piece of code some time after the service starts. If that is the case, then your best bet would be to use a timer.
First off, you have to convert the amount of time you want to wait to milliseconds. For example, 5 minutes equals 300000ms. Then, you have to move the code you want to execute to a separate method. I will name this method RunCode() for the example. Finally, you create your timer like so:
private void RunService()
{
var timer = new Timer(300000);
timer.Elapsed += (s, e) => this.RunCode();
timer.Start();
Thread.Sleep(Timeout.Infinite);
}
What we are doing here is the following.
Instantiating the timer with an interval of 300000ms
Subscribing to the timer's Elapsed event, which fires when the specified time has passed
Starting the timer
Sleeping our main thread forever. This line is optional. Whether you need it or not depends on the structure of your program. If nothing else happens in your code after you start the timer, the main thread and by extension the whole program will exit. However by sleeping it with an infinite timeout, we can prevent that.
If you're sure that this, delayed execution of code, is what you want, then the solution I have provided should work quite well. However I'm worried this may be an XY problem, meaning this is the solution you have come up with for a different issue which could be solved better. So I have to ask, why exactly do you need this in your service?
Use System.Timers
static void Main(string[] args) {
Timer T = new Timer();
T.Elapsed += Run;
T.Interval = 100;
T.Start();
}
static void Run(object source, ElapsedEventArgs e) {
}
In relation to a previous question of mine ([question] : Which thread will timer method run in? ), I've added a Timer to my Windows Forms app to run every 100ms to show how long the program session has been running. I've defined it as follows (these are only the snippets relevant to the timer):
private System.Timers.Timer timerPureTime = new System.Timers.Timer(100);
timerPureTime.Elapsed += new System.Timers.ElapsedEventHandler(updateTimeElapsed);
this.timerPureTime.SynchronizingObject = currentForm; //where currentForm is my main Form
public void updateTimeElapsed(object sender, ElapsedEventArgs e)
{
if (currentForm.lblTimeElapsed.InvokeRequired) //lblTimeElapsed is your standard Windows Form label
{
currentForm.lblTimeElapsed.Invoke((MethodInvoker)delegate //also, trying to make make GUI invoking thread-safe here
{
TimeSpan t = TimeSpan.FromSeconds(purelyTime);
string showTime = string.Format("{0:D2} min {1:D2} sec",
t.Minutes,
t.Seconds);
currentForm.lblTimeElapsed.Text = showTime;
});
}
else
{
TimeSpan t = TimeSpan.FromSeconds(purelyTime);
string showTime = string.Format("{0:D2} min {1:D2} sec",
t.Minutes,
t.Seconds);
currentForm.lblTimeElapsed.Text = showTime;
}
purelyTime += 0.1;
}
As I understand it the Timer should be running in a thread of its own (taken from the Threadpool) however it still experiences some delay every now and then, throwing the timer off-course. Other threads within the application run pretty regularly (every 250ms) and computation-intensive, but shouldn't these be independent of Timer threads?
What could be the possible causes for timer lag in such cases?
Windows cannot guarantee a precisely regular callback for a timer, so you will definitely see that kind of variance.
You need to take a different approach:
Initialise a Stopwatch field in your class.
Call Stopwatch.Restart() when you want to reset the timing.
Inside updateTimeElapsed() use Stopwatch.Elapsed instead of purelyTime.
Note that your code is completely ignoring the amount of time spent in the timer handler function itself. Inside the handler, you are using Invoke to send a message to the UI and waiting for it to return. That can take an arbitrary amount of time, particularly if the UI is busy.
I'm trying to use a timer in C# to run a method at an interval of five seconds. Though this code doesn't seem to work. I do not get any errrors when running it but the program (I run this in a console) shuts down right after IP.timer1.Start(). The timer1_Elapsed method is never getting executed. I know that because I've tried making the program print a string to the console at the first line of the timer1_Elapsed method.
class Program
{
Timer timer1 = new Timer();
static void Main(string[] args)
{
Program IP = new Program();
IP.timer1.Elapsed += new ElapsedEventHandler(timer1_Elapsed);
IP.timer1.Interval = 5000;
IP.timer1.Enabled = true;
IP.timer1.Start();
}
static void timer1_Elapsed(object sender, ElapsedEventArgs e)
{
//Function to get executed each time the counter elapses.
}
}
The reason is that the Start method of the timer starts the timer on another thread, and immediately returns from the method. This causes your Main method to end, and the console to shut down.
Depending on what Timer you are using (there are a few similarly named classes in the BCL) you may want to implement the fix differently. I suggest reading the documentation on System.Timers.Timer, System.Windows.Forms.Timer or System.Threading.Timer depending on which it is you are using.
Your program will exit the moment the main function terminates.
You need to prevent main from exiting until you are ready, possibly with a Console.ReadLine();
The timer starts on another thread, Use the following to suspend the thread until the user hits a key after the timer start.
Console.ReadLine();
The reason that the program exits right after IP.timer1.Start() is that it is done executing the Main()-function and there is nothing stopping it from returning.
If you want a simple way to keep your program running you can add Console.ReadKey(); after timer1.Start(); so that your application will wait until that function returns (which is when you press any key). After doing this your callback should be called every five seconds as specified.
I just added some extra functionality to a Coding4Fun project. I have my project set up with an extra option to allow it to automatically change the background after X amount of time. X is set from a ComboBox. However, I know I've done this in a terrible way, as I have created a new timer class with System.Timers.Timer as a parent so when the static method in the ElapsedEventHandler is called, I'm able to get back to the form and call ChangeDesktopBackground().
What is a better way to call ChangeDesktopBackground() at a user defined interval?
Here is my current solution, which involves me casting the sender as my inherited timer, which then gets a reference to the form, which then calls the ChangeDesktopBackground method.
private static void timerEvent(object sender, System.Timers.ElapsedEventArgs e)
{
((newTimer)sender).getCycleSettingsForm().ChangeDesktopBackground();
}
Edit:Added coding sample to show current solution
I've written something like this before myself. System.Timers.Timer is overkill for this. You should probably use System.Windows.Forms.Timer, for a couple of reasons:
You're doing something that doesn't have to be too precise. The Windows timer is just a WM_TIMER message sent to your windows app's message pump, so you're not getting super great precision, but changing your wallpaper once a second is unrealistic. (I wrote mine to change every 6 hours or so)
When using a Windows Forms app that does some kind of timer-based task, you're going to run into all kinds of thread affinity issues if you go with System.Timers.Timer. Any Windows control has an affinity for the thread on which it was created, meaning that you can only modify the control on that thread. A Windows.Forms.Timer will do all that stuff for you. (For future nitpickers, changing wallpaper doesn't really count, cause it's a registry value change, but the rule holds generally)
Timers are probably the most straight-forward way of doing it, although I'm not sure you're using a timer correctly. Here's how I've used timers in my projects:
// here we declare the timer that this class will use.
private Timer timer;
//I've shown the timer creation inside the constructor of a main form,
//but it may be done elsewhere depending on your needs
public Main()
{
// other init stuff omitted
timer = new Timer();
timer.Interval = 10000; // 10 seconds between images
timer.Tick += timer_Tick; // attach the event handler (defined below)
}
void timer_Tick(object sender, EventArgs e)
{
// this is where you'd show your next image
}
Then, you'd connect your ComboBox onChange handler such that you'd be changing timer.Interval.
I would use Microsoft's Reactive Framework for this. Just NuGet "Rx-WinForms".
Here's the code:
var subscription =
Observable
.Interval(TimeSpan.FromMinutes(1.0))
.ObserveOn(this)
.Subscribe(n => this.getCycleSettingsForm().ChangeDesktopBackground());
To stop it just do subscription.Dispose().
Simple.