C# how to loop while mouse button is held down - c#

Can you point me in the right direction? I'm trying to get a loop to trigger while the form button is depressed.
//pseudocode
While (button1 is pressed)
value1 += 1
And then of course stop looping when the button is released

To avoid using threads you can add a Timer component on your form/control and simply enable it on mouse down and disable it on mouse up. Then put the code you would normally put inside the loop in the Timer_Tick event. If you want to use System.Timers.Timer you can use the Timer.Elapsed event instead.
Example (using System.Timers.Timer):
using Timer = System.Timers.Timer;
using System.Timers;
using System.Windows.Forms;//WinForms example
private static Timer loopTimer;
private Button formButton;
public YourForm()
{
//loop timer
loopTimer = new Timer();
loopTimer.Interval = 500;/interval in milliseconds
loopTimer.Enabled = false;
loopTimer.Elapsed += loopTimerEvent;
loopTimer.AutoReset = true;
//form button
formButton.MouseDown += mouseDownEvent;
formButton.MouseUp += mouseUpEvent;
}
private static void loopTimerEvent(Object source, ElapsedEventArgs e)
{
//this does whatever you want to happen while clicking on the button
}
private static void mouseDownEvent(object sender, MouseEventArgs e)
{
loopTimer.Enabled = true;
}
private static void mouseUpEvent(object sender, MouseEventArgs e)
{
loopTimer.Enabled = false;
}

You could use a thread to do the counting, and stop the thread when the mouse is released. The following has worked nicely for me:
var b = new Button { Text = "Press me" };
int counter = 0;
Thread countThread = null;
bool stop = false;
b.MouseDown += (s, e) =>
{
stop = false;
counter = 0;
countThread = new Thread(() =>
{
while (!stop)
{
counter++;
Thread.Sleep(100);
}
});
countThread.Start();
};
b.MouseUp += (s, e) =>
{
stop = true;
countThread.Join();
MessageBox.Show(counter.ToString());
};
Of course, if you want the event handlers to be methods rather than lambdas, you will have to turn all the variables into fields.

private void button1_MouseDown(object sender, MouseEventArgs e)
{
timer1.Enabled = true;
timer1.Start();
}
private void button1_MouseUp(object sender, MouseEventArgs e)
{
timer1.Stop();
}
private void timer1_Tick(object sender, EventArgs e)
{
numericUpDown1.Value++;
}

I was inspired by what I read here and decided to write my own button class called a RepeatingButton. On first click it waits for 500ms, then repeats ever 300ms until 2s, then repeats every 100ms (i.e. it uses acceleration).
Here is the code;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Windows.Forms;
/// <summary>
/// A repeating button class.
/// When the mouse is held down on the button it will first wait for FirstDelay milliseconds,
/// then press the button every LoSpeedWait milliseconds until LoHiChangeTime milliseconds,
/// then press the button every HiSpeedWait milliseconds
/// </summary>
public class RepeatingButton : Button
{
/// <summary>
/// Initializes a new instance of the <see cref="RepeatingButton"/> class.
/// </summary>
public RepeatingButton()
{
internalTimer = new Timer();
internalTimer.Interval = FirstDelay;
internalTimer.Tick += new EventHandler(internalTimer_Tick);
this.MouseDown += new MouseEventHandler(RepeatingButton_MouseDown);
this.MouseUp += new MouseEventHandler(RepeatingButton_MouseUp);
}
/// <summary>
/// The delay before first repeat in milliseconds
/// </summary>
public int FirstDelay = 500;
/// <summary>
/// The delay in milliseconds between repeats before LoHiChangeTime
/// </summary>
public int LoSpeedWait = 300;
/// <summary>
/// The delay in milliseconds between repeats after LoHiChangeTime
/// </summary>
public int HiSpeedWait = 100;
/// <summary>
/// The changeover time between slow repeats and fast repeats in milliseconds
/// </summary>
public int LoHiChangeTime = 2000;
private void RepeatingButton_MouseDown(object sender, MouseEventArgs e)
{
internalTimer.Tag = DateTime.Now;
internalTimer.Start();
}
private void RepeatingButton_MouseUp(object sender, MouseEventArgs e)
{
internalTimer.Stop();
internalTimer.Interval = FirstDelay;
}
private void internalTimer_Tick(object sender, EventArgs e)
{
this.OnClick(e);
TimeSpan elapsed = DateTime.Now - ((DateTime)internalTimer.Tag);
if (elapsed.TotalMilliseconds < LoHiChangeTime)
{
internalTimer.Interval = LoSpeedWait;
}
else
{
internalTimer.Interval = HiSpeedWait;
}
}
private Timer internalTimer;
}
Anywhere you have a button, you can just replace it with a repeating button and it will just have all the new functionality built in.
Enjoy!
Sterren

A recent article from Fabulous Adventures in Coding provides this narrative, which might help answer your question:
A surprising number of people have magical beliefs about how exactly applications respond to user inputs in Windows. I assure you that it is not magic. The way that interactive user interfaces are built in Windows is quite straightforward. When something happens, say, a mouse click on a button, the operating system makes a note of it. At some point, a process asks the operating system "did anything interesting happen recently?" and the operating system says "why yes, someone clicked this thing." The process then does whatever action is appropriate for that. What happens is up to the process; it can choose to ignore the click, handle it in its own special way, or tell the operating system "go ahead and do whatever the default is for that kind of event." All this is typically driven by some of the simplest code you'll ever see:
while(GetMessage(&msg, NULL, 0, 0) > 0)
{
TranslateMessage(&msg);
DispatchMessage(&msg);
}
That's it. Somewhere in the heart of every process that has a UI thread is a loop that looks remarkably like this one. One call gets the next message. That message might be at too low a level for you; for example, it might say that a key with a particular keyboard code number was pressed. You might want that translated into "the numlock key was pressed". TranslateMessage does that. There might be some more specific procedure that deals with this message. DispatchMessage passes the message along to the appropriate procedure.
I want to emphasize that this is not magic. It's a while loop. It runs like any other while loop in C that you've ever seen. The loop repeatedly calls three methods, each of which reads or writes a buffer and takes some action before returning. If one of those methods takes a long time to return (typically DispatchMessage is the long-running one of course since it is the one actually doing the work associated with the message) then guess what? The UI doesn't fetch, translate or dispatch notifications from the operating system until such a time as it does return.

Override the OnMouseDown() method in your form and then if the button you want is pressed, that would equal your loop. Example:
protected override void OnMouseDown(MouseEventArgs e)
{
if (e.Button == MouseButtons.Left)
{
// this is your loop
}
}
It's not a loop in the traditional sense, but should work for what you need.

You will need to handle the MouseDown() event for your form, using the MouseEventArgs argument to figure out which button was pressed.

RepeatButton is perfect for that:
<RepeatButton Delay="1000" Interval="500" HorizontalAlignment="Left" Content="+" Click="IncreaseButton_Click"/>
private void IncreaseButton_Click(object sender, RoutedEventArgs e)
{
value1++;
}

It's been several years since I posted this but someone upvoted it so it popped up in my notifications. Now that I have a lot more experience lol, I thought I'd see if this simple problem is as simple as it sounds, and it was:
public partial class Form1 : Form
{
private bool _isRunning;
public Form1()
{
InitializeComponent();
txtValue.Text = #"0";
btnTest.MouseDown += (sender, args) =>
{
_isRunning = true;
Run();
};
btnTest.MouseUp += (sender, args) => _isRunning = false;
}
private void Run()
{
Task.Run(() =>
{
while (_isRunning)
{
var currentValue = long.Parse(txtValue.Text);
currentValue++;
txtValue.Invoke((MethodInvoker) delegate
{
txtValue.Text = currentValue.ToString();
});
}
});
}
}

Based on Steztric's answer, an extension method with a few bug fixes and different options for the rate of increase.
/// <summary>
/// An extension method to add a repeat click feature to a button. Clicking and holding on a button will cause it
/// to repeatedly fire. This is useful for up-down spinner buttons. Typically the longer the mouse is held, the
/// more quickly the click events are fired. There are different options when it comes to increasing the rate of
/// clicks:
/// 1) Exponential - this is the mode used in the NumericUpDown buttons. The first delay starts off around 650 ms
/// and each successive delay is multiplied by 75% of the current delay.
/// 2) Linear - the delay more slowly reaches the fastest repeat speed. Each successive delay subtracts a fixed
/// amount from the current delay. Decreases in delays occur half a second apart.
/// 3) Two Speed - this delay starts off at a slow speed, and then increases to a faster speed after a specified delay.
/// 4) Three Speed - the repeat speed can increase from slow, to medium, to fastest after a specified delay.
///
/// If repeating is added to a button that already has it, then it will be replaced with the new values.
/// </summary>
public static class RepeatingButtonEx {
private static Hashtable ht = new Hashtable();
private class Data {
private static readonly System.Reflection.MethodInfo methodOnClick = null;
static Data() {
methodOnClick = typeof(Button).GetMethod("OnClick", System.Reflection.BindingFlags.Instance | System.Reflection.BindingFlags.NonPublic);
}
public Button Button = null;
private Timer Timer = new Timer();
public double? GradientRate;
public int? LinearGradient = null;
public int FirstDelayMillis;
public int FastestRepeatMillis;
public int[] SwitchesMillis;
public int[] SpeedsMillis;
private DateTime lastEvent = DateTime.MinValue;
private int millisCount = 0;
private int currentSpeed = 0;
private int waitSum = 0;
public Data(Button button, double? gradientRate, int? linearGradient, int firstDelayMillis, int fastestRepeatMillis, int[] switchesMillis, int[] speedsMillis) {
Button = button;
GradientRate = gradientRate;
LinearGradient = linearGradient;
FirstDelayMillis = firstDelayMillis;
FastestRepeatMillis = fastestRepeatMillis;
SwitchesMillis = switchesMillis;
SpeedsMillis = speedsMillis;
Timer.Interval = firstDelayMillis;
Timer.Tick += Timer_Tick;
Button.MouseDown += Button_MouseDown;
Button.MouseUp += Button_MouseUp;
Button.MouseLeave += Button_MouseLeave;
}
void Button_MouseDown(object sender, MouseEventArgs e) {
if (!Button.Enabled)
return;
lastEvent = DateTime.UtcNow;
Timer.Start();
}
void Button_MouseUp(object sender, MouseEventArgs e) {
Reset();
}
void Button_MouseLeave(object sender, EventArgs e) {
Reset();
}
private void Reset() {
Timer.Stop();
Timer.Interval = FirstDelayMillis;
millisCount = 0;
currentSpeed = 0;
waitSum = 0;
}
void Timer_Tick(object sender, EventArgs e) {
if (!Button.Enabled) {
Reset();
return;
}
methodOnClick.Invoke(Button, new Object[] { EventArgs.Empty });
//Button.PerformClick(); // if Button uses SetStyle(Selectable, false); then CanSelect is false, which prevents PerformClick from working.
if (GradientRate.HasValue || LinearGradient.HasValue) {
int millis = Timer.Interval;
if (GradientRate.HasValue)
millis = (int) Math.Round(GradientRate.Value * millis);
else if (LinearGradient.HasValue) {
DateTime now = DateTime.UtcNow;
var ts = now - lastEvent;
int ms = (int) ts.TotalMilliseconds;
millisCount += ms;
// only increase the rate every 500 milliseconds
// otherwise it appears too get to the maximum rate too quickly
if (millisCount >= 500) {
millis -= LinearGradient.Value;
millisCount -= 500;
lastEvent = now;
}
}
if (millis < FastestRepeatMillis)
millis = FastestRepeatMillis;
Timer.Interval = millis;
}
else {
if (currentSpeed < SpeedsMillis.Length) {
TimeSpan elapsed = DateTime.UtcNow - lastEvent;
if (elapsed.TotalMilliseconds >= waitSum) {
waitSum += SwitchesMillis[currentSpeed];
Timer.Interval = SpeedsMillis[currentSpeed];
currentSpeed++;
}
}
}
}
public void Dispose() {
Timer.Stop();
Timer.Dispose();
Button.MouseDown -= Button_MouseDown;
Button.MouseUp -= Button_MouseUp;
Button.MouseLeave -= Button_MouseLeave;
}
}
///<summary>The repeating speed becomes exponentially faster. This is the default behavior of the NumericUpDown control.</summary>
///<param name="button">The button to add the behavior.<param>
///<param name="firstDelayMillis">The delay before first repeat in milliseconds.</param>
///<param name="fastestRepeatMillis">The smallest delay allowed. Note: Masharling between the timer and the UI thread has an unavoidable limit of about 10 milliseconds.</param>
///<param name="gradientRate">The new interval is the current interval multiplied by the gradient rate.</param>
public static void AddRepeatingExponential(this Button button, int firstDelayMillis = 500, int fastestRepeatMillis = 15, double gradientRate = 0.75) {
AddRepeating(button, firstDelayMillis, fastestRepeatMillis, gradientRate, null, null, null);
}
///<summary>The repeating speed becomes linearily faster.</param>
///<param name="button">The button to add the behavior.<param>
///<param name="firstDelayMillis">The delay before first repeat in milliseconds.</param>
///<param name="fastestRepeatMillis">The smallest delay allowed. Note: Masharling between the timer and the UI thread has an unavoidable limit of about 10 milliseconds.</param>
///<param name="linearGradient">If specified, the repeats gradually happen more quickly. The new interval is the current interval minus the linear gradient.</param>
public static void AddRepeatingLinear(this Button button, int firstDelayMillis = 500, int fastestRepeatMillis = 50, int linearGradient = 25) {
AddRepeating(button, firstDelayMillis, fastestRepeatMillis, null, linearGradient, null, null);
}
///<summary>The repeating speed switches from the slow speed to the fastest speed after the specified amount of milliseconds.</summary>
///<param name="button">The button to add the behavior.<param>
///<param name="firstDelayMillis">The delay before first repeat in milliseconds.</param>
///<param name="fastestRepeatMillis">The smallest delay allowed. Note: Masharling between the timer and the UI thread has an unavoidable limit of about 10 milliseconds.</param>
///<param name="slowRepeatMillis">The delay in milliseconds between repeats when in the slow repeat state.</param>
///<param name="slowToFastestSwitchMillis">The delay in milliseconds before switching from the slow repeat speed to the fastest repeat speed.</param>
public static void AddRepeatingTwoSpeed(this Button button, int firstDelayMillis = 500, int fastestRepeatMillis = 100, int slowRepeatMillis = 300, int slowToFastestSwitchMillis = 2000) {
AddRepeating(button, firstDelayMillis, fastestRepeatMillis, null, null, new[] { slowRepeatMillis, fastestRepeatMillis }, new [] { slowToFastestSwitchMillis, 0 });
}
///<summary>The repeating speed switches from the slow to medium to fastest at speed switch interval specified.</summary>
///<param name="button">The button to add the behavior.<param>
///<param name="firstDelayMillis">The delay before first repeat in milliseconds.</param>
///<param name="fastestRepeatMillis">The smallest delay allowed. Note: Masharling between the timer and the UI thread has an unavoidable limit of about 10 milliseconds.</param>
///<param name="slowRepeatMillis">The delay in milliseconds between repeats when in the slow repeat state.</param>
///<param name="mediumRepeatMillis">The delay in milliseconds between repeats when in the medium repeat state.</param>
///<param name="speedSwitchMillis">The delay in milliseconds before switching from one speed state to the next speed state.</param>
public static void AddRepeatingThreeSpeed(this Button button, int firstDelayMillis = 500, int fastestRepeatMillis = 75, int slowRepeatMillis = 300, int mediumRepeatMillis = 150, int speedSwitchMillis = 2000) {
AddRepeating(button, firstDelayMillis, fastestRepeatMillis, null, null, new[] { slowRepeatMillis, mediumRepeatMillis, fastestRepeatMillis }, new [] { speedSwitchMillis, speedSwitchMillis, 0 });
}
private static void AddRepeating(this Button button, int firstDelayMillis, int fastestRepeatMillis, double? gradientRate, int? linearGradient, int[] speedsMillis, int[] switchesMillis) {
Data d = (Data) ht[button];
if (d != null)
RemoveRepeating(button);
d = new Data(button, gradientRate, linearGradient, firstDelayMillis, fastestRepeatMillis, switchesMillis, speedsMillis);
ht[button] = d;
button.Disposed += delegate {
RemoveRepeating(button);
};
}
///<summary>Removes the repeating behavior from the button.</summary>
public static void RemoveRepeating(this Button button) {
Data d = (Data) ht[button];
if (d == null)
return;
ht.Remove(button);
d.Dispose();
}
}

you could use the mouseMove Event and check if the mousebutton is held down like:
private void pictureBox1_MouseMove(object sender, MouseEventArgs e)
{
if(e.Button==MouseButtons.Left)
{
//your code here
}
}

Similar to Timwi's solution above except using async/await for asynchronous IO and lock for synchronization for some state...
using System;
using System.Windows.Forms;
using System.Threading.Tasks;
namespace Foo {
partial class Form1: Form {
private static readonly object mousePressLock = new object();
private bool mousePressed;
private Task task;
private async Task MouseAction(Action action) {
while (true) {
lock (mousePressLock) {
if (mousePressed)
action();
else
break;
}
await Task.Delay(100).ConfigureAwait(false);
}
}
private void PnlTranslate_Paint(object sender, PaintEventArgs e) {
}
private void Up_MouseUp(object sender, MouseEventArgs e) {
lock (mousePressLock) { mousePressed = false; }
task.Wait();
}
private void Up_MouseDown(object sender, MouseEventArgs e) {
lock (mousePressLock) { mousePressed = true; }
int cnt = 0;
task = MouseAction(() => {
Console.WriteLine($"mouse up action {++cnt}");
});
}
public Form1() {
InitializeComponent();
mousePressed = false;
task = null;
}
}
}
Also, note the ConfigureAwait(false) call. I ran into deadlock without that b.c. tasks were fighting to be on the same thread. It was so annoying.

Related

Display Stopwatch to screen?

I have been trying to make a stopwatch now for the past few days. I think I just need to figure out how to actually get it to display to the screen and update every second, milli seconds etc. Any code I have put in wont display the timer to the screen and actually show what's happening. I will post the code below and the errors I'm getting. Any help input would be amazing. Thanks.
public partial class MainPage : ContentPage
{
private const string Format = "{0:00}:{1:00}:{2:00}.{3:00}";
DispatcherTimer dTimer = new DispatcherTimer();
Stopwatch stopWatch = new Stopwatch();
string currentTime = string.Empty;
public MainPage()
{
Stopwatch stopWatch = new Stopwatch();
stopWatch.Start();
Thread.Sleep(10000);
stopWatch.Stop();
// Get the elapsed time as a TimeSpan value.
TimeSpan tSpan = stopWatch.Elapsed;
// Format and display the TimeSpan value.
string ClockTextBlock = String.Format(Format,
tSpan.Hours, tSpan.Minutes, tSpan.Seconds,
tSpan.Milliseconds / 10);
Console.WriteLine("RunTime " + ClockTextBlock);
InitializeComponent();
InitializeComponent();
dTimer.Tick += new EventHandler(dt_Tick);
dTimer.Interval = new TimeSpan(0, 0, 0, 0, 1);
void dt_Tick(object sender, EventArgs e)
{
if (stopWatch.IsRunning)
{
TimeSpan t = stopWatch.Elapsed;
currentTime = String.Format("{0:00}:{1:00}:{2:00}",
tSpan.Hours, tSpan.Minutes, tSpan.Seconds);
ClockTextBlock.ToString (currentTime);
}
}
}
public String Display(Object sender, String ClockTextBlock)
{
return ClockTextBlock + String.Format(Format);
}
public void StartButton_Click(Object sender, Stopwatch stopwatch)
{
dTimer.Start();
}
public void StopButton_Click(Object sender, Stopwatch stopwatch)
{
dTimer.Stop();
}
public void ResetButton_Click(Object sender, Stopwatch stopwatch)
{
stopwatch.Reset();
}
}
}
Errors:
cannot convert from 'string' to 'System.IFormatProvider'code
DispatcherTimer.Tick' is not supported by the language; try directly
calling accessor methods
'DispatcherTimer.add_Tick(EventHandler)' or
'DispatcherTimer.remove_Tick(EventRegistrationToken)'
You can try this approach. It can be done easily with an async/await method. Check this:
async void UpdateLoop()
{
while (true) //your condition when it should stop
{
label.Text = DateTime.Now.ToString("T");
await Task.Delay(1000);
}
}
Of course you don't need to display actual time, but you can keep your time in a variable.
And you can call the UpdateLoop() method from any method/constructor. This peace of code is from the official Xamarin.Forms book (page 721). I recommended to check it out.
I did something similar which might help you. Basically I wanted to show the length of time some hardware had been booted for. I had a label in the View which was bound to a property. I then created a dispatcher which called a method every 50 ms which updated the property. In the example below it starts when the constructor for the View-Model is called.
XAML
<Label Grid.Row="5" Grid.Column="2" Content="{Binding TimePortModuleIsUp}" />
View-Model
private string timePortModuleIsUp;
/// <summary>
/// Gets / Sets how long the port module has been booted for.
/// </summary>
public string TimePortModuleIsUp
{
get
{
return timePortModuleIsUp;
}
Set
{
timePortModuleIsUp = value;
OnPropertyChanged("TimePortModuleIsUp");
}
}
Create a DispatcherTimer object.
private DispatcherTimer t;
Constructor in View-Model
This creates a dispatcher object that calls t_tick every 50ms and starts it off.
// Create a dispatcher that calls t_tick every 50 ms.
t = new DispatcherTimer(new TimeSpan(0, 0, 0, 0, 50), DispatcherPriority.Background, t_Tick, Dispatcher.CurrentDispatcher);
// Start the timer.
t.IsEnabled = true;
and finally the method that is called every 50 ms that updates the property the label is bound to.
/// <summary>
/// Method that updates a property representing how long the port module has been booted for.
/// </summary>
/// <param name="sender"></param>
/// <param name="e"></param>
private void t_Tick(object sender, EventArgs e)
{
this.TimePortModuleIsUp = new DateTime((DateTime.Now - this.ModuleBootTime).Ticks).ToString("HH:mm:ss");
}

Preventing multiple View clicks in Android (MonoDroid) using C# lock()

I'm trying to prevent multiple clicks from taking affect, and would like to know if the following logic will cover all possibilities?
Currently, each successive "click" (touch) fires off an additional download.
private object CLICK_LOCK = new System.Object();
public void ViewClickHandler(object o, System.EventArgs a)
{
View clickedView = (View)o;
lock (CLICK_LOCK)
{
if (clickedView.Clickable == false)
{
return;
}
clickedView.Clickable = false;
}
// sets clickedView.Clickable = true when finished
(new DownloadAsyncTask()).Execute();
}
To prevent multiple View clicks in a short time, a better solution is set a limit time for the Button. If the time interval between the two clicks does not exceed the MIN_CLICK_DELAY_TIME, the event will not trigger.
//Prevent multiple clicks in 1 second.
public static int MIN_CLICK_DELAY_TIME = 1000;
private long lastClickTime = 0;
bt.Click += ViewClickHandler;
public void ViewClickHandler(object o, System.EventArgs a)
{
long currentTime = Calendar.Instance.TimeInMillis;
if (currentTime - lastClickTime > MIN_CLICK_DELAY_TIME)
{
lastClickTime = currentTime;
OnNoDoubleClick();
}
}
protected void OnNoDoubleClick()
{
(new DownloadAsyncTask()).Execute();
}

C# - How to pause application until timer is finished?

I have an application that I need to have wait a specific amount of time, but I also need to be able to cancel the current operation if needed. I have the following code:
private void waitTimer(int days)
{
TimeSpan waitTime = TimeSpan.FromDays(days);
System.Timers.Timer timer = new System.Timers.Timer(waitTime.TotalMilliseconds); // Wait for some number of milliseconds
timer.Enabled = true;
timer.Start();
timer.Elapsed += new ElapsedEventHandler(OnTimedEvent); // Subscribe to event handler
while (!TimerSettings.TimerFinished && !quitToken.IsCancellationRequested); // Loop forever untill timer is finished or operation is cancled.
timer.Elapsed -= new ElapsedEventHandler(OnTimedEvent); // Unsubscribe
DoWork(); // Do work when timer finishes.......
}
Below is the event handler for the timer finished event:
private void OnTimedEvent(object obj, ElapsedEventArgs e)
{
TimerSettings.TimerFinished = true;
}
The while loop just loops infinitely until the timer is finished or until a cancelation request is put in. I want to retain this functionality but I would rather not loop forever while waiting for the timer to finish. My timer can be set to run on an interval of multiple days so it doesn't make sense to loop for so long.
Is there another way of doing this?
I know I could do:
Thread.Sleep(runDuration.TotalMilliseconds);
However, this would be blocking and I would not be able to put in a cancelation request.
EDIT: So in order to elaborate on what/why I need to pause here is a more detailed explination of my application. Basically I want to have an application that performs "work" on a regular interval. So based on one of the answers provided below, if I did something like this:
class Program
{
// Do something in this method forever on a regular interval
//(could be every 5min or maybe every 5days, it's up to the user)
static void Main(string[] args)
{
while(true)
{
if(args?.Length > 0)
waitTimer(args[0]);
else
wiatTimer(TimeSpan.FromDays(1).TotalSeconds); // Default to one day interval
}
}
private void waitTimer(int numIntervals)
{
this.ElapsedIntervals = 0;
this.IntervalsRequired = numIntervals;
this.timer = new System.Timers.Timer(1000); // raise the elapsed event every second
timer.Elapsed += new ElapsedEventHandler(OnTimedEvent); // Subscribe to event handler
//timer.Enabled = true; timer.Start() does this for you, don't do this
timer.Start();
//thats all here
}
private void OnTimedEvent(object obj, ElapsedEventArgs e)
{
this.ElapsedIntervals += 1;
if(this.CancelRequested)
{
this.ElapsedIntervals = 0;
this.timer.Stop();
return;
}
if(this.ElapsedIntervals >= this.IntervalsRequired)
{
this.ElapsedIntervals = 0;
this.timer.Stop();
DoWork(); // This is where my work gets done.......
return;
}
}
}
Then my service/console app would start and go into an infinite loop that just sets timers all day long. Previously, I was actually halting execution of any other code at:
while (!TimerSettings.TimerFinished && !quitToken.IsCancellationRequested);
Which at least worked, but as mentioned, can possibly be resource intensive way to pause a thread. Basically what I really need is a way to block my thread untill the timer is up.
EDIT2: This is my final implementation that seems to work for me using a wait handle...
class TimerClass
{
/// <summary>
/// Initialize new timer. To set timer duration,
/// either set the "IntervalMinutes" app config
/// parameter, or pass in the duration timespan.
/// </summary>
/// <param name="time"></param>
internal bool StartTimer(CancellationToken quitToken, TimeSpan? duration = null)
{
TimeSpan runDuration = new TimeSpan();
runDuration = duration == null ? GetTimerSpan() : default(TimeSpan);
if (runDuration != default(TimeSpan))
{
WaitTimer(runDuration); // Waits for the runduration to pass
}
return true;
}
/// <summary>
/// Get duration to run the timer for.
/// </summary>
internal TimeSpan GetTimerSpan()
{
TimerSettings.Mode = App.Settings.Mode;
DateTime scheduledTime = new DateTime();
switch (TimerSettings.Mode)
{
case "Daily":
scheduledTime = DateTime.ParseExact(App.Settings.ScheduledTime, "HH:mm:ss", CultureInfo.InvariantCulture);
if (scheduledTime > DateTime.Now)
TimerSettings.TimerInterval = scheduledTime - DateTime.Now;
else
TimerSettings.TimerInterval = (scheduledTime + TimeSpan.FromDays(1)) - DateTime.Now;
break;
case "Interval":
double IntervalMin = double.TryParse(App.Settings.PollingIntervalMinutes, out IntervalMin) ? IntervalMin : 15.00;
int IntervalSec = Convert.ToInt32(Math.Round(60 * IntervalMin));
TimeSpan RunInterval = new TimeSpan(0, 0, IntervalSec);
TimerSettings.TimerInterval = RunInterval;
break;
case "Manual":
TimerSettings.TimerInterval = TimeSpan.FromMilliseconds(0);
break;
default:
TimerSettings.TimerInterval = (DateTime.Today + TimeSpan.FromDays(1)) - DateTime.Now;
break;
}
return TimerSettings.TimerInterval;
}
/// <summary>
/// Event handler for each timer tick.
/// </summary>
/// <param name="obj"></param>
/// <param name="e"></param>
private void OnTimedEvent(object obj, ElapsedEventArgs e)
{
ElapsedIntervals += 1;
if (CancelRequested.IsCancellationRequested) // If the application was cancled
{
ElapsedIntervals = 0;
timer.Stop();
WaitHandle.Set();
return;
}
if (ElapsedIntervals >= IntervalsRequired) // If time is up
{
ElapsedIntervals = 0;
timer.Stop();
WaitHandle.Set();
return;
}
}
/// <summary>
/// Timer method to wait for a
/// specified duration to pass.
/// </summary>
/// <param name="span"></param>
private void WaitTimer(TimeSpan span)
{
WaitHandle = new AutoResetEvent(false);
int tickDuration = 1000; // Number of milliseconds for each tick
IntervalsRequired = Convert.ToInt64(span.TotalMilliseconds / (tickDuration > 0 ? tickDuration : 0.01));
timer = new System.Timers.Timer(tickDuration); // Raise the elapsed event every tick
timer.Elapsed += new ElapsedEventHandler(OnTimedEvent); // Subscribe to event handler for when each tick is complete
timer.Start(); // Start ticking
WaitHandle.WaitOne(); // Halt the main thread untill span is reached
}
// Timer parameters:
private static long ElapsedIntervals { get; set; }
private static long IntervalsRequired { get; set; }
private static System.Timers.Timer timer { get; set; }
private static CancellationToken CancelRequested { get; set; }
private static string Mode { get; set; }
private static TimeSpan TimerInterval { get; set; }
private static EventWaitHandle WaitHandle { get; set; }
}
internal static class TimerSettings
{
internal static string Mode { get; set; }
internal static TimeSpan TimerInterval { get; set; }
}
You should look at the Timer.Elapsed Event documentation. This event will be raised repeatedly every time the interval elapses while the AutoReset property is set to true (which is default). I would keep your own count of how many intervals have elapsed and compare it to the required elapsed intervals in this event handler to check whether it is time to stop the timer. In that event, you can also handle cancellation. If your timer finishes its required number of intervals, you may call your doWork function from that event handler.
private void waitTimer(int numIntervals)
{
this.ElapsedIntervals = 0;
this.IntervalsRequired = numIntervals;
this.timer = new System.Timers.Timer(1000); // raise the elapsed event every second
timer.Elapsed += new ElapsedEventHandler(OnTimedEvent); // Subscribe to event handler
//timer.Enabled = true; timer.Start() does this for you, don't do this
timer.Start();
//thats all here
}
private void OnTimedEvent(object obj, ElapsedEventArgs e)
{
this.ElapsedIntervals += 1;
if(this.CancelRequested)
{
this.ElapsedIntervals = 0;
this.timer.Stop();
return;
}
if(this.ElapsedIntervals >= this.IntervalsRequired)
{
this.ElapsedIntervals = 0;
this.timer.Stop();
DoWork();
return;
}
}
https://msdn.microsoft.com/en-us/library/system.timers.timer.elapsed(v=vs.110).aspx
As I see it, with regards to "pausing", there are two reasons to want to pause and I am unsure which reason is yours:
You want to prevent the application from "finishing" execution and terminating normally.
You want to hold off on executing other code until the number of required intervals has elapsed
If your reason is #2, then this answer is complete.
First of all: "you absolutely do not(!) want to 'busy-wait' for anything!" (BAD Dog! NO Biscuit!)
Ahem...
A practical solution to this problem is to perform a timed wait on a semaphore (or any other suitable mutual-exclusion object ...), instead of using an actual Timer. If you need to bust-out of the wait before it is finished, just strobe the thing upon which it is waiting.
The critical problem with your present "solution" is that it will slam the process into 100% CPU utilization, altogether wastefully. Never do that!

Passing a variable to timer_tick

Got a problem at the moment, I'm using a timer to do animations and I want to be able to decide where to start using Start and Stop integers as i'll show below.
private void Button1_Click(object sender, EventArgs e)
{
AnimateKey(0,100);
}
private void AnimateKey(int Start, int Stop)
{
myTimer.Interval = 5;
myTimer.Tick += new EventHandler(myTimer_Tick);
myTimer.Enabled = true;
myTimer.Start();
}
private void myTimer_Tick(object sender, EventArgs e)
{
lock (myTimer)
{
int StartingFrame = Start;
int StopFrame = Stop;
etc...etc..
}
}
Now my problem is that I want to pass the values 0 and 100 to the Timer Tick event but I have no idea on how to go about doing it.
How can I get the Integers 0 and 100 from the button click to the 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.
use a class with all info:
public class TimerInfo
{
public int Start;
public int Stop;
}
store an instance in timer's Tag
myTimer.Tag = new TimerInfo { Start = 0, Stop = 100 };
inside the eventhandler you access this info
myTimer = (Timer)sender;
TimerInfo ti = (TimerInfo)myTimer.Tag;
Somewhat hard to understand what you mean, but let's give it a try.
If you ment you want to pass the integers start and stop to the function TimerTick, you probably dont understand the EventArgs parameter. EventArgs is ment to store the arguements which are relevant to your scenario - and the solution is simple.
class myTimerEventArgs:EventArgs // Declaring your own event arguements which you want to send
{
public int start{get;set;}
public int stop {get;set;}
/*Constructor, etc...*/
}
...
//Making the call inside another class:
myTimer_Tick(this,new myTimerEventArgs(0,100);
However, I could be misunderstanding you; If are talking about counting the ticks until it reaches 100 ticks (/intervals), the solution is a simple function added to the event, which would probably look like this:
int Count = 0;
...
private void Counter(object sender, EventArgs e)
{
Count++;
}
...
private void AnimateKey(int Start, int Stop)
{
myTimer.Interval = 5;
myTimer.Tick += new EventHandler(myTimer_Tick);
myTimer.Tick += new EventHandler(Counter);
myTimer.Enabled = true;
myTimer.Start();
while(Count!=100);
myTimer.Stop();
}
Hope I helped, have a nice day :)

How do I subscribe to raised events and printing together?

I have been working on a program that has 3 classes of which 2 of the classes have timers that repeat at different intervals and once one "cycle" of the timer is done it raises an event with a string as return. The 3rd class subscribes to the events from the other two timer classes and prints them to screen. it works great!
But my issue is that it is printing them separately. Lets say that the first timer class runs and then raises "hello" every 2 minutes and the other class "dog" every second and every time an event is raised it prints the raised event to console. I would want it to instead print "hellodog" every second and store the value of the first timer(hello) in a private field or something so it still prints to screen even if the timer(the slower 2 minute timer) hasn't been fired. and when the 2 minute timer fires it updates the value to whatever the new one is and that new value get printed to screen until it fires again.
If it is confusing I will gladly clarify. its kind of hard to explain.
namespace Final
{
public class Output
{
public static void Main()
{
var timer1 = new FormWithTimer();
var timer2 = new FormWithTimer2();
timer1.NewStringAvailable += new EventHandler<BaseClassThatCanRaiseEvent.StringEventArgs>(timer1_NewStringAvailable);
timer2.NewStringAvailable += new EventHandler<BaseClassThatCanRaiseEvent.StringEventArgs>(timer2_NewStringAvailable);
Console.ReadLine();
}
static void timer1_NewStringAvailable(object sender, BaseClassThatCanRaiseEvent.StringEventArgs e)
{
var theString = e.Value;
//To something with 'theString' that came from timer 1
Console.WriteLine(theString);
}
static void timer2_NewStringAvailable(object sender, BaseClassThatCanRaiseEvent.StringEventArgs e)
{
var theString2 = e.Value;
//To something with 'theString2' that came from timer 2
Console.WriteLine(theString2);
}
}
public abstract class BaseClassThatCanRaiseEvent
{
public class StringEventArgs : EventArgs
{
public StringEventArgs(string value)
{
Value = value;
}
public string Value { get; private set; }
}
//The event itself that people can subscribe to
public event EventHandler<StringEventArgs> NewStringAvailable;
protected void RaiseEvent(string value)
{
var e = NewStringAvailable;
if (e != null)
e(this, new StringEventArgs(value));
}
}
public partial class FormWithTimer : BaseClassThatCanRaiseEvent
{
Timer timer = new Timer();
public FormWithTimer()
{
timer = new System.Timers.Timer(200000);
timer.Elapsed += new ElapsedEventHandler(timer_Tick); // Everytime timer ticks, timer_Tick will be called
timer.Interval = (200000); // Timer will tick evert 10 seconds
timer.Enabled = true; // Enable the timer
timer.Start(); // Start the timer
}
void timer_Tick(object sender, EventArgs e)
{
...
RaiseEvent(gml.ToString());
}
}
public partial class FormWithTimer2 : BaseClassThatCanRaiseEvent
{
Timer timer = new Timer();
public FormWithTimer2()
{
timer = new System.Timers.Timer(1000);
timer.Elapsed += new ElapsedEventHandler(timer_Tick2); // Everytime timer ticks, timer_Tick will be called
timer.Interval = (1000); // Timer will tick evert 10 seconds
timer.Enabled = true; // Enable the timer
timer.Start(); // Start the timer
}
void timer_Tick2(object sender, EventArgs e)
{
...
RaiseEvent(aida.ToString());
}
}
}
You can use the same event handler for both timers. And construct the output by identifying the senders. (Didn't test the code for syntax errors.)
private static string timer1Value = string.Empty;
private static string timer2Value = string.Empty;
private static FormWithTimer timer1;
private static FormWithTimer2 timer2;
public static void Main()
{
timer1 = new FormWithTimer();
timer2 = new FormWithTimer2();
timer1.NewStringAvailable += new EventHandler<BaseClassThatCanRaiseEvent.StringEventArgs>(timer1_NewStringAvailable);
timer2.NewStringAvailable += new EventHandler<BaseClassThatCanRaiseEvent.StringEventArgs>(timer1_NewStringAvailable);
Console.ReadLine();
}
static void timer1_NewStringAvailable(object sender, BaseClassThatCanRaiseEvent.StringEventArgs e)
{
if (sender == timer1)
{
timer1Value = e.Value.ToString();
}
else if (sender == timer2)
{
timer2Value = e.Value.ToString();
}
if (timer1Value != String.Empty && timer2Value != String.Empty)
{
Console.WriteLine(timer1Value + timer2Value);
// Do the string concatenation as you want.
}
Correct me if I misunderstand the question, but it sounds like you want to coordinate your response to the two timer events (print "hellodog").
It seems to me that the easiest way to do this is to just use a single timer, and have the timer's event handler count the number of times the handler has been invoked to decide whether to take the once-per-second action, or also take the once-per-two-minutes action.
Since the slow timer is an exact multiple of your fast timer, you would set just one timer that triggers every second, and also do the 2-minute action every 120 invocations of the 1 second timer (120 seconds = 2 minutes).
I think I understand what you want and that is to synchronize the output of both timers. I am afraid there is no way to do it other than to slog through it. Set up a bunch of Boolean variables that track whether each event fired and whether the synchronized message was sent to the output.
This should do what you want.
public static void Main()
{
var timer1 = new FormWithTimer();
var timer2 = new FormWithTimer2();
var value1 = "";
var value2 = "";
Action writeValues = () => Console.WriteLine(value1 + value2);
timer1.NewStringAvailable += (s, e) =>
{
value1 = e.Value;
writeValues();
};
timer2.NewStringAvailable += (s, e) =>
{
value2 = e.Value;
writeValues();
};
Console.ReadLine();
}
Let me know if this is right. Cheers.
The second (quicker) timer should be the only one to print.
The first (slower) timer should only update a string which the second timer will use.
In the 'Output' class (you can put it before Main):
string string1;
and then:
static void timer1_NewStringAvailable(object sender, BaseClassThatCanRaiseEvent.StringEventArgs e)
{
string1 = e.Value;
}
static void timer2_NewStringAvailable(object sender, BaseClassThatCanRaiseEvent.StringEventArgs e)
{
var theString2 = e.Value;
//To something with 'theString2' that came from timer 2
Console.WriteLine(string1 + theString2);
}

Categories