public void timer_thing()
{
Thread timer = new Thread(new ThreadStart(() =>
{
Thread.Sleep(500);
if (is_mouse_down)
timer1.Enabled = true;
}
));
timer.Start();
}
private void timer1_Tick(object sender, EventArgs e)
{
//some stuff happens here
}
As you can see I want the thread to activate the timer. But the timer doesn't activate. I guess this is not the right way. Or i'm missing something.
It could be that is_mouse_down is false whenever the thread hits that instruction. The thread is not going to magically wait for it to turn true.
However, you have another, bigger problem to worry about. The thing is you cannot touch any UI element from a worker thread or any other other than the UI thread. This includes the System.Windows.Forms.Timer. All sorts of undefined chaos may ensue. Your application may fail unpredictably and spectacularly.
It is not really clear to me why a thread is needed in the first place. Can you not handle the Control.MouseDown event and enable the timer in the event handler for that event? That is how I would solve the problem.
Your thread isn't waiting for the is_mouse_down event. It just checks after half a second and if it's not the time won't get enabled and the tread closes. Maybe you should try using an event?
You can use AutoResetEvent to automatically trigger it from Button Click handler.
So in thread just set:
autoResetEvent.WaitOne();
timer1.Enabled = true;
and in button click handler:
autoResetEvent.Set();
BTW, why you can not initialize a Timer in the Click handler?
This problem as described in literature has two solutions:
1) Active waiting
2) Notifications
If you want the active waiting solution(which is really an outdated one, your thread should have while loop).
If you want the notification when you should call some method which starts the timer in the mouse down event handler
If you have questions about c# multithreading you should read this article. It is really helpful and will show you AutoresetEvent, ManualResetEvents, Thread Timers, Timers, etc. Really good general article.
You have to pass the timer1_Tick callback when you declare your timer object.
Related
In some games, there is a splash screen that downloads content from a server. It might give some tips while you're waiting. I'm doing something similar, only that the loading happens really quick, but I want it to wait a few more seconds.
When the user first loads my application, it has a screen with a progress bar. At the moment, it checks if the server is online. If it is, it says "Connected!" However, it immediately fades out my controls. I want it to wait about 5 more seconds so the reader can read it. Then fade the controls out.
private void frmMain_Loaded(object sender, RoutedEventArgs e)
{
// Start background worker
m_bgWorker = new System.ComponentModel.BackgroundWorker();
m_bgWorker.ProgressChanged += M_bgWorker_ProgressChanged;
m_bgWorker.WorkerReportsProgress = true;
m_bgWorker.RunWorkerAsync();
m_bgWorker.ReportProgress(100);
}
private void M_bgWorker_ProgressChanged(object sender, System.ComponentModel.ProgressChangedEventArgs e)
{
progConnect.Value = 0;
if (SystemBO.IsOnline())
{
lblConnection.Content = "Connected!";
}
progConnect.Value = e.ProgressPercentage;
// TODO: Wait 5 seconds here...
// Fade out controls
lblTitle.BeginAnimation(Label.OpacityProperty, doubleAnimation);
progConnect.BeginAnimation(Label.OpacityProperty, doubleAnimation);
lblConnection.BeginAnimation(Label.OpacityProperty, doubleAnimation);
}
Note:
I tried System.Sleep(), but that made no difference. I understand why. The idea is the same though: I want the background worker to sleep for 5 seconds before completing.
Solution:
I added a few more events: DoWork and RunWorkerCompleted.
Then I added this code:
private void M_bgWorker_DoWork(object sender, System.ComponentModel.DoWorkEventArgs e)
{
for (int i = 0; i < 100; i++)
{
m_index++;
System.Threading.Thread.Sleep(40);
m_bgWorker.ReportProgress(m_index);
}
System.Threading.Thread.Sleep(3000); // wait 3 seconds to read
}
It worked. The progress bar animates quite smoothly.
You shouldn't do the fade-out in the ProgressChanged handler. That handler really should only be used to update progress information like text and a percentage bar.
Your fade-out logic should be moved to the separate RunWorkedCompleted event handler. Here you can let the thread sleep/await a scheduled task for a while before beginning to fade out the controls. (Using async/await over Thread.Sleep() here has the advantage of not blocking the UI thread while waiting, so the UI still remains responsive while waiting for the time to pass.)
It also ensures that the code is called at the very end and not every time the worker reports any kind of progress. And it's also cleaner because it allows you to handle different termination states by checking the RunWorkedCompletedEventArgs:
If e.Error is set, an exception occurred in the worker thread.
If e.Cancelled is true, the worker thread got cancelled by calling CancelAsync() on the worker. (Only possible if WorkerSupportsCancellation is set to true and only useful if code in the DoWork handler actually checks for the cancellation flag)
Otherwise everything went okay.
Side note:
I am assuming that your code is not a full example, because you have no actual handler assigned to the DoWork event. So right now it would not do anything. And calling ReportProgress() right from the main thread is wrong as well. That method is designed to be called from within the DoWork event handling method in order to let the asynchronous thread report status updates back to the UI, since those events are handled on the main thread.
rather than the last line in frmMain_Loaded:
m_bgWorker.ReportProgress(100);
I would put a for loop with Sleep(), for simulating some extra processing:
for (int i=0; i<=100; i+=10)
{
if (SystemBO.IsOnline())
i = 100;
Sleep(1000);
m_bgWorker.ReportProgress(i);
}
I'm trying to do a waiting screen for my application coded in c#.
when the user click on start, to prevent possible errors just show a message in the middle, i used backgroundWorker but doesn't work like i want.
i have this:
backgroundWorker1.RunWorkerAsync(formwaiddt.ShowDialog());
the problem is the program waits until i close this dialog.
also i tried other ways like this
Can you link to a good example of using BackgroundWorker without placing it on a form as a component?
and i obtain the same result.
then anyone knows how to show a message in the middle of the screen, also i create a panel but doesn't shows it correctly, to lock the user interactions
thanks.
The simplest case is to use BackgroundWorker to create a method that matches the delegate signature, attach that method to BackgroundWorker`s DoWork event, and then call the RunWorkerAsync() method of BackgroundWorker:
// Set your cursor to busy
Cursor.Current = Cursors.WaitCursor;
BackgroundWorker backgroundWorkerExample = new BackgroundWorker();
backgroundWorkerExample.DoWork += new DoWorkEventHandler(backgroundWorkerExample_DoWork);
backgroundWorkerExample.RunWorkerAsync();
// elsewhere:
void backgroundWorkerExample_DoWork(object sender, DoWorkEventArgs e)
{
// body of the work elided
}
When the background worker thread has finished, it raises the RunWorkerComplete event on the foreground thread, so you can switch your cursor back to the normal state using:
Cursor.Current = Cursors.Default;
Alternatively, you could use the WorkerReportsProgress property to inform BackgroundWorker that the worker procedure will report progress to the foreground thread at regular intervals.
Have you tried wiring up an event on a timer, so that it runs on a separate thread to check if the process has finished?
I am trying to establish a pause and play communication between threads.I am spawning two threads for two forms at the startup, one is the mainthread which is for mainwindow and second thread of form2,
var thread = new Thread(ThreadStart);
thread.TrySetApartmentState(ApartmentState.STA);
thread.Start();
private static void ThreadStart()
{
Application.Run(new SecondForm()); // other form started on its own UI thread
}
Mainwindow has a button on clicking which multiple threads would be spawn for different operations - it executes without pause until the intended jobs are complete.
In the second form I have two buttons stop, and start clicking on which I should be able to stop and start the mainwindow(All the child threads).
For this I am declaring
public static ManualResetEvent mre = new ManualResetEvent(false);
in program.cs where two UI threads are started
and trying to invoke it from the second form on start and stop
private void btnStop_Click(object sender, EventArgs e)
{
Invoke((MethodInvoker) delegate
{
Program.mre.WaitOne();
});
}
private void btnStart_Click(object sender, EventArgs e)
{
Invoke((MethodInvoker) delegate
{
Program.mre.Set();
});
}
Once I click the button on mainwindow which continues with series of operations I try to click on STOP button in secondform, this disables the second form, and the operation from the mainwindow continues, please let me know where I am going wrong.
Other than the fact that this is a completely silly threading design, there's two issues:
You Invoke on the second form, so you're blocking its messaging loop. Instead, you would want to do mainWindow.Invoke( ... ).
Even that will not help you when main window spawns worker threads. It will only stop the form's message loop, not the threads it has already spawned.
I found a solution to the problem. When I click on Pause button I am setting a bool say continue to false, and using this variable in a loop where actual calls to function is done.
In the same class where I have my loop I have the manual reset event initialized, when continue is false I am calling mre.WaitOne(), which would pause the current thread from execution without causing any harm to other threads.
When Play is clicked I am Setting the Manual Reset event object which would restart the thread.
I have a System.Windows.Forms.Timer associated with my main form. I need this timer to fire every 1000ms so that some work can be performed. The Timer.Enabled property is set to true in the Form.Shown event and the interval is set to 1000.
The Timer.Tick event does fire about every second for the most part. However, the Tick events stop firing when my application is executing a foreach statement. I have tried Thread.Sleep but, that did not help. Does anyone have any suggestions? The code that handles my Timer.Tick event is wrapped with exception handling and I have verified that the event stops firing in the debugger during execution of the foreach block. Additionally, my application is not multi-threaded at this time. Any suggestions?
If you are keeping the UI thread busy in a loop, then no, the timers Tick handler won't be able to execute. If you have a long running loop, push it onto a background thread. Sleep won't help because that also blocks the UI thread. There are other timers that don't fire on the UI thread (System.Timer), but if your tick needs access to the UI, then that won't actually help you if the UI thread is still busy.
Implementation of the System.Timers.Timer might look something like this:
var timer = new System.Timers.Timer();
timer.Interval = 1000; // every second
timer.Elapsed += TimerTick;
...
private void TimerTick(object state, System.Timers.ElapsedEventArgs e)
{
// do some work here
Thread.Sleep(500);
var reportProgress = new Action(() =>
{
// inside this anonymous delegate, we can do all the UI updates
label1.Text += string.Format("Work done {0}\n", DateTime.Now);
});
Invoke(reportProgress);
}
Whenever I try to do something like this the timer doesnt stop:
private void timer1_Tick(object sender, EventArgs e)
{
if ((addedToFriendsCounter == 4) || (followJobFinished))
{
//stop the timer
}
}
Any suggestions?
Yes, no problem. A comment can't stop a timer. Use
timer1.Stop();
or
((Timer)sender).Stop();
There's no problem stopping the timer from within the Tick event handler. What the heck is addedToFriendsCount and followJobFinished? Your error is either with one of these or the code for //stop the timer.
Yes, there is no problem stopping the timer from the Tick event. The event runs in the main thread, so there is no cross-thread problems when you access the Timer control.
You can stop the timer either by calling the Stop method or by setting the Enabled property to false.