Changing Timer Interval in Backgroundworker DoWork Disables the Timer [C#] - c#

I have the problem with changing the timer Interval in backgroundworker's DoWork event. While changing the Interval by clicking the Button, Timer stops and doesn't start again.
Does anyone know how to solve this problem?
Simple code:
public Form1()
{
InitializeComponent();
timerTest.Tick += new EventHandler(timerTest_Tick);
timerTest.Interval = 1000;
timerTest.Start();
}
private void buttonTest_Click(object sender, EventArgs e)
{
push = true;
}
private void timerTest_Tick(object sender, EventArgs e)
{
ticks++;
labelTest.Text = ticks.ToString();
if(running == false)
{
running = true;
backgroundWorkerTest.RunWorkerAsync();
}
}
public void activate()
{
timerTest.Stop();
timerTest.Interval = 4000;
timerTest.Start();
}
private void DoWork(object sender, DoWorkEventArgs e)
{
while(running)
{
if(push == true)
{
activate();
}
}
}
private void Completed(object sender, RunWorkerCompletedEventArgs e)
{
running = false;
}
}
}

You never set push to false.
Therefore, the following code:
while(running)
{
if(push == true)
{
activate();
}
}
will continuously call activate() in a tight loop. activate() stops the timer and then restarts it, and the time between calls to it will be far less than the timer interval. Therefore, the timer will never be left long enough to fire.
In any case, why don't you call activate() directly from buttonTest_Click()?

I can see this was asked a long time ago, but for the reference:
When it comes to timers or threadings in general (remember timer is system.threading) in combination with background workers (tasks) Never ever try to change a thread proerties randomly without knowing what the worker is doing.
It is always a good practice when assigning the DoWork handler to prepare the background worker Progress and Complete handlers as well.
At each cycle, report the progress or the completion, this would give you the chance to do your checks and modify another thread properties if needed.
private void backgroundWorker1_DoWork(object sender, DoWorkEventArgs e)
{
while (!worker.CancellationPending)
{
// do task 1 as per the timer1 interval
// do task 2 as per such and such .....
// if I call ChangeInterval here I'll be fiddling with another thread when
// this is still in progress
// that a full loop, Progress will be reported now
}
}
private void backgroundWorker1_ProgressChanged(object sender,ProgressChangedEventArgs e)
{
// Now as the Do work is not in progress
// do something
// check if the user wanted to change the interval ?
// if yes then
ChangeInterval(6000);
// here the progress reporting is done so it will go back to DoWork with the
// NewInterval Value in place and the timer enabled
}
private void ChangeInterval(int NewInterval)
{
timer1.Enabled =false;
timer1.Interval = NewInterval;
timer1.Enabled = true;
}

Try invoke your activate method with the UI Thread's Dispatcher. (Assuming Win Forms?)
this.Invoke(new Action(activate));
Reason is that your timer is a UI control and you're updating the Interval on a separate thread. This will throw a Cross-Thread exception.
Why don't you see the exception? When the DoWork method in your BackgroundWorker throws an exception, it will be propogated to the Completed method. So you should always look at e.Error to see if an exception occurred.
private void Completed(object sender, RunWorkerCompletedEventArgs e)
{
if(e.Error != null)
{
// Oh no something went wrong...
}
running = false;
}

It took me a while, but I found out what was wrong. I'll post you a working code, just in case someone will have the same problem.
public partial class Form1 : Form
{
public int ticks = 0;
public bool running = false;
public bool push = false;
public Form1()
{
InitializeComponent();
timerTest.Tick += new EventHandler(timerTest_Tick);
timerTest.Interval = 1000;
timerTest.Start();
}
private void buttonTest_Click(object sender, EventArgs e)
{
push = true;
}
private void timerTest_Tick(object sender, EventArgs e)
{
ticks++;
labelTest.Text = ticks.ToString();
if(running == false)
{
running = true;
backgroundWorkerTest.RunWorkerAsync();
}
}
public void activate()
{
ZmienIntervalNaAwaryjny = true;
}
public bool ZmienIntervalNaAwaryjny = false;
private void DoWork(object sender, DoWorkEventArgs e)
{
if(push == true)
{
activate();
}
}
private void Completed(object sender, RunWorkerCompletedEventArgs e)
{
if(ZmienIntervalNaAwaryjny == true)
{
timerTest.Stop();
timerTest.Interval = 12000;
timerTest.Start();
}
ZmienIntervalNaAwaryjny = false;
running = false;
}
}

Related

DotNetZip Cancel extraction in Task

I have a method that executes a zip-extraction in a Task. Now I want the ability to Cancel the operation. But when I call the Cancel() method everything seems to stop immediately and the while runs forever:
public class OsiSourceZip
{
private const string ZipPassword = "******";
private bool _extractionDone;
private bool _cancelExtraction;
public async Task Extract(string sourceFile, string extractionDir)
{
Task extraction = Task.Run(() =>
{
using (ZipFile zipf = ZipFile.Read(sourceFile))
{
zipf.ExtractProgress += delegate(object sender, ExtractProgressEventArgs args)
{
args.Cancel = _cancelExtraction;
RaiseExtractionProgressUpdate(args);
};
zipf.Password = ZipPassword;
zipf.Encryption = EncryptionAlgorithm.WinZipAes256;
zipf.ExtractExistingFile = ExtractExistingFileAction.OverwriteSilently;
zipf.ExtractAll(extractionDir);
}
});
await extraction;
_extractionDone = true;
RaiseSourceInstallationCompleted();
}
public void Cancel()
{
_cancelExtraction = true;
while (!_extractionDone)
{
Thread.Sleep(500);
}
}
}
I've set a break point on args.Cancel = _cancelExtraction; but the event is not fired anymore as soon as the Cancel() method is called.
I've found a solution to this. I basically got rid of my own Cancel Method and do it as the dotnetzip Framework wants it. In the progress event.
Let's say you want to cancel the operation when the Form gets closed. I capture the FormClosing Event, cancel the close procedure and remember the close request. The next time the progress event fires, I set the the cancel property in the event args and close the Form myself in the completed Event:
public partial class MainForm : Form
{
private bool _closeRequested;
private void OnSourceInstallationCompleted(object sender, EventArgs e)
{
if (_closeRequested) { this.Close(); }
}
private void MainForm_FormClosing(object sender, FormClosingEventArgs e)
{
if (!_closeRequested)
{
_closeRequested = true;
e.Cancel = true;
}
}
private void OnExtractionProgressUpdate(object sender, ExtractProgressEventArgs e)
{
e.Cancel = _closeRequested;
}
}
I think it's rather ugly but it works....

wpf c# backgroundworker wait until finished

I have several textboxes in my wpf application. The LostFocus-Event of each textbox starts a backgroundworker to send the data to a connected serial port.
private readonly BackgroundWorker online_mode_send_worker = new BackgroundWorker();
online_mode_send_worker.DoWork += online_mode_send_worker_DoWork;
online_mode_send_worker.RunWorkerCompleted += online_mode_send_worker_RunWorkerCompleted;
private void TextBox_LostFocus(object sender, RoutedEventArgs e)
{
online_mode_send_worker.RunWorkerAsync(data);
}
private void online_mode_send_worker_DoWork(object sender, DoWorkEventArgs e)
{
List<object> data = (List<object>)e.Argument;
Port.WriteLine(STARTCHARACTER + XMLSET + XML_TAG_START + data[0] + XML_TAG_STOP + data[1] + ENDCHARACTER);
string received = Port.ReadLine();
}
private void online_mode_send_worker_RunWorkerCompleted(object sender, RunWorkerCompletedEventArgs e)
{
//do some things after worker completed
}
At this point, everything is working fine.
But sometimes I have to send two data-points directly after each other and there I have a problem.
private void TextBox_LostFocus(object sender, RoutedEventArgs e)
{
online_mode_send_worker.RunWorkerAsync(data1);
//wait until backgroundworker has finished
online_mode_send_worker.RunWorkerAsync(data2);
}
The Backgroundworker is still running and I get an exception thrown.
Is it possible to wait after the first online_mode_send_worker.RunWorkerAsync(data) until it has finished and then start the second online_mode_send_worker.RunWorkerAsync(data)?
while(online_mode_send_worker.isBusy); is not working because the main-thread is blocking and the RunWorkerCompleted() is not thrown and so the Backgroundwoker is always busy.
I have found something like this, but Application.DoEvents() is not available in wpf.
while (online_mode_send_worker.IsBusy)
{
Application.DoEvents();
System.Threading.Thread.Sleep(100);
}
Here is a rough idea of what I mentioned in the comments.
public class Messenger {
private readonly BackgroundWorker online_mode_send_worker = new BackgroundWorker();
private readonly ConcurrentQueue<object> messages;
public Messenger() {
messages = new ConcurrentQueue<object>();
online_mode_send_worker.DoWork += online_mode_send_worker_DoWork;
online_mode_send_worker.RunWorkerCompleted += online_mode_send_worker_RunWorkerCompleted;
}
public void SendAsync(object message) {
if (online_mode_send_worker.IsBusy) {
messages.Enqueue(message);
} else {
online_mode_send_worker.RunWorkerAsync(message);
}
}
public Action<object> MessageHandler = delegate { };
private void online_mode_send_worker_DoWork(object sender, DoWorkEventArgs e) {
if (MessageHandler != null)
MessageHandler(e.Argument);
}
private void online_mode_send_worker_RunWorkerCompleted(object sender, RunWorkerCompletedEventArgs e) {
object nextMessage = null;
if (messages.Count > 0 && messages.TryDequeue(out nextMessage)) {
online_mode_send_worker.RunWorkerAsync(nextMessage);
}
}
}
You have a queue to hold on to messages that were sent while the background worker was busy and have the worker check the queue for any pending messages when it has completed doing its work.
The messenger can be used like this.
private Messenger messenger = new Messenger();
private void Initialize() { //I would expect this to be in the constructor
messenger.MessageHandler = MessageHandler;
}
private void TextBox_LostFocus(object sender, RoutedEventArgs e)
{
messenger.SendAsync(data);
}
private void MessageHandler(object message)
{
List<object> data = (List<object>)message;
Port.WriteLine(STARTCHARACTER + XMLSET + XML_TAG_START + data[0] + XML_TAG_STOP + data[1] + ENDCHARACTER);
string received = Port.ReadLine();
}
It seems that I missed the serial stuff. So what you want to do is synchronize your asynchronuouscalls:
private void Button_Click(object sender, RoutedEventArgs e)
{
Task.Run(() => mySerialDevice1.WriteData(data1));
Task.Run(() => mySerialDevice1.WriteData(data2));
}
public class SerialDevice
{
public Port Port { get; set; }
public object _LockWriteData = new object();
public void WriteData(string data)
{
lock(_LockWriteData)
{
Port.WriteLine(data);
}
}
}
also see:
https://msdn.microsoft.com/en-us/library/c5kehkcz.aspx
https://msdn.microsoft.com/en-us/library/de0542zz(v=vs.110).aspx
ORIGINAL ANSWER
You can use Task instead of Backgroundworker.
private void Button_Click(object sender, RoutedEventArgs e)
{
Task.Run(() => OnlineModeSendData(data1));
Task.Run(() => OnlineModeSendData(data2));
}
private void OnlineModeSendData(List<string> data)
{
Port.WriteLine(STARTCHARACTER + XMLSET + XML_TAG_START + data[0]+ XML_TAG_STOP + data[1] + ENDCHARACTER);
string received = Port.ReadLine();
}
I also would like to suggest that you make real objects instead of passing string arrays as arguments.
For Example send BlinkLedRequest:
public class BlinkLedRequest
{
public int LedId{get;set;}
public int DurationInMilliseconds {get;set}
}
and a corresponding method:
public void SendBlinkLed(BlickLedRequest request)
{
....
}
I think your should use RunWorkerCompleted event and add a delegate:
online_mode_send_worker.RunWorkerCompleted += (s, ev) =>
{
if (ev.Error != null)
{
//log Exception
}
//if(conditionToBrake)
// return;
online_mode_send_worker.RunWorkerAsync(data2);
};
online_mode_send_worker.RunWorkerCompleted(data1);
Make sure you put there a condition to avoid infinite loop.
I'd say that if you MUST wait until after the first "job" is done, that what you want is Task.ContinueWith() and change your interface accordingly. The msdn page is good for it IMO, but watch out that you're waiting on the "correct" task object. Hint: it's the return value of ContinueWith() that you should call Wait() on. This is a good pattern to do for launching a Task and then waiting for it later as long as you can keep the Task that is returned so you can wait on it.
For a more generic "I only want one background thread doing things in the order they're added, and I want to wait until they're ALL done and I know when I'm done adding." I would suggest using a BlockingCollection<Action> with only one thread consuming them. An example of how to do that is found in this other answer.
Update:
bw.RunWorkerAsync(data1);
//wait here
bw.RunWorkerAsync(data2);
Is not good aproach, because UI will be blocked on time of waiting. Better:
bw.RunWorkerAsync(new object[] { data1, data2 }); //or new object[] { data1 } if no data2
Original answer:
I advice not to use construction: while (bw.Busy) { ... } (it consumes cpu time), use synchronization objects, for example, ManualResetEvent
BackgroundWorker is great class, but does not support waiting. Just create addition object for waiting:
var bw = new BackgroundWorker();
bw.DoWork += Bw_DoWork;
bw.RunWorkerCompleted += Bw_RunWorkerCompleted;
bool wasError;
ManualResetEvent e = null;
private void TextBox_LostFocus(object sender, RoutedEventArgs e)
{
if (e != null)
return;
wasError = false;
e = new ManualResetEvent(false); //not signaled
bw.RunWorkerAsync(data1);
e.Wait(); //much better than while(bw.Busy())
if (!wasError)
bw.RunWorkerAsync(data2);
e = null;
}
private void Bw_DoWork(object sender, DoWorkEventArgs e)
{
//background work in another thread
}
private void Bw_RunWorkerCompleted(object sender, RunWorkerCompletedEventArgs e)
{
if (e.Error != null)
{
//catch exception here
wasError = true;
}
e.Set(); //switch to signaled
}
If you need only call twice you can do this:
bw.RunWorkerCompleted += new RunWorkerCompletedEventHandler(bw_RunWorkerCompleted);
void bw_RunWorkerCompleted(object sender, RunWorkerCompletedEventArgs e)
{
online_mode_send_worker.RunWorkerAsync(data2);
}
But if you need to queue commands you need rewrite in another way Using Task.
One Task where inside it you will have a for-loop where you will send your data through serial port sequentially.
https://msdn.microsoft.com/pt-br/library/system.threading.tasks.task(v=vs.110).aspx

Creating a timer winform application using only delegates and events [WITHOUT TIMER OBJECT]

While using timers, stopwatches and threads is the standard way, I was wondering if there was a way to create a Winform Application in c# which had a label with initial value as 0 and which automatically kept on incrementing once a button is clicked and when the same button is clicked again it should pause. Personally, I feel that the trick is to use multicast delegates. But I am stuck as to how to proceed.
NOTE: Possible use of method callback and InvokeRequired().
this code dose not use timer or stopwatch.
i have wrote a simple class for you, forgive me if its not so standard because im so lazy for now :)
public partial class Form1 : Form
{
CancellationTokenSource src;
CancellationToken t;
public Form1()
{
InitializeComponent();
}
//start incrementing
private async void button1_Click(object sender, EventArgs e)
{
this.Start.Enabled = false;
this.Cancel.Enabled = true;
this.src = new CancellationTokenSource();
this.t = this.src.Token;
try
{
while (true)
{
var tsk = Task.Factory.StartNew<int>(() =>
{
Task.Delay(500);
var txt = int.Parse(this.Display.Text) + 1;
return (txt);
}, this.t);
var result = await tsk;
this.Display.Text = result.ToString();
}
}
catch (Exception ex)
{
return;
}
}
// Stop incrementing
private void button1_Click_1(object sender, EventArgs e)
{
this.src.Cancel();
this.Cancel.Enabled = true;
this.Start.Enabled = true;
}
}
Really not sure why you think this can be done with your restrictions in place. If you want a delay in-between your "events", then you need to use some kind of Timer, or some kind of thread (classic Thread or some kind of Task) that has a delay within it...no way around that.
Here's another approach that'll probably violate your restrictions:
public partial class Form1 : Form
{
private Int64 value = -1;
private bool Paused = true;
private int IntervalInMilliseconds = 100;
private System.Threading.ManualResetEvent mre = new System.Threading.ManualResetEvent(false);
public Form1()
{
InitializeComponent();
this.Shown += Form1_Shown;
}
private async void Form1_Shown(object sender, EventArgs e)
{
await Task.Run(delegate ()
{
while (true)
{
value++;
label1.Invoke((MethodInvoker)delegate ()
{
label1.Text = value.ToString();
});
System.Threading.Thread.Sleep(IntervalInMilliseconds);
mre.WaitOne();
}
});
}
private void button1_Click(object sender, EventArgs e)
{
if (Paused)
{
mre.Set();
}
else
{
mre.Reset();
}
Paused = !Paused;
}
}
USE an EVENT.
If you can not use timers or threads, then how about creating a do while loop that executes an event.
Some PSEUDO code is below - it should give you the idea..
bool IWantEvents = false;
public event EventHandler<myHandler> myNonTimerEvent ;
FormStart()
{
this.myNonTimerEvent += new MyNonTimerEventHandler();
IWantEvents = true;
Do
{
.. do some weird stuff - set IWantEvents False on condition ..
}
while(IWantEvents)
}
MyNonTimerEventHandler()
{
.. Do what I would do if I was using a timer event.
}

C# Singleton form not opening correctly from Timer, opening correctly from button press

I have a singletone form that can be opened from a ribbon button or that will check every minute whether it should be open after passing a few conditional checks.
When opening the form from the ribbon button, it works correctly every time.
When opening on the timer, the form does not get rendered correctly, any place a control should be is just displayed as a white rectangle. Screenshots below.
ThisAddIn.cs
using Timer = System.Timers.Timer;
public partial class ThisAddIn
{
private Timer ticker;
private void ThisAddIn_Startup(object sender, System.EventArgs e) {
ticker = new Timer(5 * 60 * 1000);
ticker.AutoReset = true;
ticker.Elapsed += new System.Timers.ElapsedEventHandler(checkForOverdue);
ticker.Start();
}
private void checkForOverdue(object sender, System.Timers.ElapsedEventArgs e)
{
bool overdue = false;
foreach (Reminder reminder in reminders)
{
DateTime now = DateTime.Now;
if (reminder.time <= now)
{
overdue = true;
break;
}
}
if (overdue)
{
RemindersList form = RemindersList.CreateInstance();
if (form != null)
{
form.Show();
}
}
}
}
Ribbon.cs
public partial class Ribbon
{
private void reminderListButton_Click(object sender, RibbonControlEventArgs e)
{
RemindersList form = RemindersList.CreateInstance();
if (form != null)
{
form.Show();
}
}
}
RemindersList.cs
public partial class RemindersList : Form
{
private static RemindersList _singleton;
private RemindersList()
{
InitializeComponent();
this.FormClosed += new FormClosedEventHandler(f_formClosed);
}
private static void f_formClosed(object sender, FormClosedEventArgs e)
{
_singleton = null;
}
public static RemindersList CreateInstance(List<Reminder> rs)
{
if (_singleton == null)
{
_singleton = new RemindersList(rs);
_singleton.Activate();
// Flash in taskbar if not active window
FlashWindow.Flash(_singleton);
return _singleton;
}
else
{
return null;
}
}
}
EDIT - SOLUTION
Per sa_ddam213's answer, I changed out the System.Timers.Timer for a Windows.Forms.Timer and it's now working just how I wanted.
Code changes:
ThisAddIn.cs
using Timer = System.Windows.Forms.Timer;
public partial class ThisAddIn {
private void ThisAddIn_Startup(object sender, System.EventArgs e) {
ticker = new Timer();
ticker.Interval = 5 * 60 * 1000;
ticker.Tick += new EventHandler(checkForOverdue);
ticker.Start();
}
// Also needed to change the checkForOverdue prototype as follows:
private void checkForOverdue(object sender, EventArgs e)
}
You can't touch UI controls/elements with any other thread than the UI thread, in your case the System.Timer is running on another thread and the window will never open
Try switching to a Windows.Forms.Timer
Or invoke the call back to the UI thread.
private void checkForOverdue(object sender, System.Timers.ElapsedEventArgs e)
{
base.Invoke(new Action(() =>
{
/// all your code here
}));
}
I suspect that the timer event handler is not launched on the UI thread, which could cause all sorts of problems. I would check that first and ensure that the UI stuff is actually done on the UI thread.

Setting a delay for an action on ListBox _SelectedIndexChanged

I have a listbox with filenames. When the selected index is changed I load the file.
I want something like jQuery's HoverIntent that delays the action of loading the file for a short time so the user can use the down arrow and quickly cycle through the items in the list without the application trying to load each one. Thread.Sleep pauses the whole app so a user can't select another list item until the sleep completes, this is obviously not what I want.
This will work if your using WinForms, make a call to the InitTimer method in the Form constructor.
Load the file in the _timer_Tick event handler. To change the delay set the Interval property in InitTimer to another value.
private System.Windows.Forms.Timer _timer;
private void InitTimer()
{
_timer = new Timer { Interval = 500 };
_timer.Tick += _timer_Tick;
}
private void listBox1_SelectedIndexChanged(object sender, EventArgs e)
{
_timer.Stop();
_timer.Start();
}
private void _timer_Tick(object sender, EventArgs e)
{
_timer.Stop();
// TODO: Load file here
}
Use Threading to separate the loading from your GUI.
This should get you started:
public partial class MainWindow : Window
{
CancellationTokenSource cts;
bool loading;
private void SelectedIndexChanged(int index)
{
if (loading)
cts.Cancel();
cts = new CancellationTokenSource();
var loader = new Task.Delay(1000);
loader.ContinueWith(() => LoadFile(index))
.ContinueWith((x) => DisplayResult(x));
loader.Start();
}
private void DisplayResult(Task t)
{
// TODO: Invoke this Method to MainThread
if (!cts.IsCancellationRequested)
{
// Actually display this file
}
}
Could not test, as I'm still on .net 4 whereas Task.Delay() is .net 4.5
You may need to add another field in the form for the file content transfer from the tasks to the GUI.
Winforms:
public partial class Form1 : Form
{
public Form1()
{
InitializeComponent();
}
private CancellationTokenSource _cancel;
private object _loadLock = new object();
private void listBox1_SelectedIndexChanged(object sender, EventArgs e)
{
lock (_loadLock)
{
handleCancellation();
var loader = new Task((chosenFileItemInListbox) =>
{
Thread.Sleep(1000);
LoadFile(chosenFileItemInListbox);
}, listBox1.SelectedItem, _cancel.Token);
}
}
private bool handleCancellation()
{
bool cancelled = false;
lock (_loadLock)
{
if (_cancel != null)
{
if (!_cancel.IsCancellationRequested)
{
_cancel.Cancel();
cancelled = true;
}
_cancel = null;
}
}
return cancelled;
}
private void LoadFile(object chosenFileItemInListbox)
{
if (handleCancellation())
{
return;
}
}
}
The code above could also be applied to WPF, but WPF contains some built in magic for handling delays and cancellation of previous updates.
<ListBox SelectedItem="{Binding Path=SelectedFile, Delay=1000}" />

Categories

Resources