I am trying to make a game launcher where there is a limit of 10 minutes the it should close
Problem statement : I want to load the form , user should be able to do activities and then application should close after 10 minute
when i give any kind of sleep or wait in main method, the form itself is not loading i only get the messagebox ,then the application closes.
public MainForm()
{
InitializeComponent();
MessageBox.Show("Welcome");
Task.Run(async () =>
{
await Task.Delay(60000);
}).GetAwaiter().GetResult();
MessageBox.Show("one minute left");
Task.Run(async () =>
{
await Task.Delay(60000);
}).GetAwaiter().GetResult();
MessageBox.Show("time over");
Application.Exit();
}
Use System.Windows.Forms.Timer as follows:
Timer = new System.Windows.Forms.Timer();
Timer.Interval = TIME_IN_MILLISECONDS;
Timer.Tick += Timer_Tick;
private void Timer_Tick(object sender, EventArgs e)
{
//desired behavior
}
System Timer runs async, after selected interval it triggers your method
Use Microsoft's Reactive Framework (NuGet "System.Reactive.Windows.Forms" and add using System.Reactive.Linq;) and then you can do this:
public MainForm()
{
InitializeComponent();
MessageBox.Show("Welcome");
Observable
.Timer(TimeSpan.FromMinutes(1.0))
.ObserveOn(this)
.Subscribe(x => MessageBox.Show("one minute left"));
Observable
.Timer(TimeSpan.FromMinutes(2.0))
.ObserveOn(this)
.Subscribe(x =>
{
MessageBox.Show("time over");
Application.Exit();
});
}
If you want to start a task in a fire and forget fashion there is no need to call GetAwaiter().GetResult(); because this will block the thread. So, basically, there is no point at all of all of these task that you have created. You could simply make use of the Timers. As you are using Windows Forms you can use the System.Windows.Forms.Timer or you can use System.Threading.Timer. I would use the System.Windows.Forms.Timer to implement such a behavior.
Why not just schedule task from event handler:
private async void OnApplicationStarted(object sender, RoutedEventArgs e)
{
await Task.Delay((int)TimeSpan.FromMinutes(9).TotalMilliseconds);
MessageBox.Show("1 minute left");
await Task.Delay((int)TimeSpan.FromMinutes(1).TotalMilliseconds);
MessageBox.Show("time over");
Application.Exit();
}
I'd use the Form.Load event with an async event handler. This will already show your form, so users will be able to see that the program has been started.
async void OnFormLoading(object sender, ...)
{
// Notify the operator that the form is loading:
this.ShowSplashScreen();
// start the delay, but do not wait yet
Task delayTask = Task.Delay(TimeSpan.FromSeconds(5));
// do other useful initializations, this might be shorter or longer than 5 seconds
DoOtherInitializations();
// now wait for the delay task to finish:
await delayTask;
this.HideSplashScreen();
}
Or an even nicer approach: show an startup-form which shows that you are starting, with only the possibility to cancel:
in class MainForm:
private async void OnFormLoading(object sender, ...)
{
// show an intro screen while doing initializations:
using (var startupForm = new FormStartup()
{
var dlgResult = startupForm.ShowDialog(this);
if (dlgResult == DialogResult.Cancel)
{
// operator cancelled starting up:
this.Close();
}
else
{
// continue starting up
}
}
}
}
}
class FormStartup
{
public void FormStartup() {...}
private async void OnFormLoading(object sender, ...)
{
Task delayTask = Task.Delay(TimeSpan.FromSeconds(5));
DoOtherInitializations();
await delayTask;
}
}
Related
I am making a small game with an auto play feature, but the program runs too fast so the user can't see the outcome at each stage. I am using VS 2017, so I can't use async (at least from what I have read). How can I have the program wait and allow the UI to update?
I am working in a do while loop. The main chunk of the game executes, updates the UI, and then waits for the player to click a button (assuming auto play is not running), with auto play running the do while loop repeats, but after the UI updates it would wait X seconds.
Use a Timer component instead of a loop, and put the loop body in the timer's Elapsed event.
And VS2017 definitely supports async, but it wouldn't help in this case... things would still move too fast for the user.
You can use async/await to slow down the execution of event handler without having to split the logic. This is pretty simple:
async void Button_Click(object sender, RoutedEventArgs e) // wpf event handler
{
...
await Task.Delay(1000); // pause 1 second
...
while (someCondition)
{
...
await Task.Delay(1000);
...
}
}
You can read about async/await at msdn.
If you are using WPF, then you have to look into animations. They are much simpler to use to ensure smooth changes than manually changing something (position, sizes).
Usage: DelayFactory.DelayAction(500, new Action(() => { this.RunAction(); }));`
//Note Forms.Timer and Timer() have similar implementations.
//Assumes you will have a DelayFactory Static Class
public static void DelayAction(int millisecond, Action action)
{
var timer = new DispatcherTimer();
timer.Tick += delegate
{
action.Invoke();
timer.Stop();
};
timer.Interval = TimeSpan.FromMilliseconds(millisecond);
timer.Start();
}
Wait function using timers, no UI locks.
public void wait(int milliseconds)
{
System.Windows.Forms.Timer timer1 = new System.Windows.Forms.Timer();
if (milliseconds == 0 || milliseconds < 0) return;
//Console.WriteLine("start wait timer");
timer1.Interval = milliseconds;
timer1.Enabled = true;
timer1.Start();
timer1.Tick += (s, e) =>
{
timer1.Enabled = false;
timer1.Stop();
//Console.WriteLine("stop wait timer");
};
while (timer1.Enabled)
{
Application.DoEvents();
}
}
Usage:
wait(1000); //wait one second
It looks like you have a couple of options
1.You can try Sleep -(but it may hang the UI)
int Seconds = 1;
Threading.Thread.Sleep(Seconds * 1000);
2.You can try this code:
int Seconds = 1;
Private void WaitNSeconds(int seconds)
{
if (seconds < 1) return;
DateTime _desired = DateTime.Now.AddSeconds(seconds);
while (DateTime.Now < _desired) {
System.Windows.Forms.Application.DoEvents();
}
}
3.Try to use Async and see what happens
async Task MakeDelay() {
await Task.Delay(5000);
}
private async void btnTaskDelay_Click(object sender, EventArgs e) {
await MakeDelay();
}
public void OpenUpForm(object sender, EventArgs e)
{
if (forms.Count == numberoftimes)
{
forms.ForEach(f =>
{
f.Close();
f.Dispose();
});
forms.Clear();
//Need Delay Here
return;
}
forms.Add(new Form1());
forms.Last().Show();
}
Hello I have this code, I need to add delay after forms.Clear(); But im new to coding i couldnt figure it out.
I have tryed with Task.Delay and Thread.Sleep but it locks my user interface. Is it possible to add a delay that dosent lock the application? Thank you.
You can mark the method async and use this:
await Task.Delay(2000);
will not block the ui thread
public async void OpenUpForm(object sender, EventArgs e)
{
if (forms.Count == numberoftimes)
{
forms.ForEach(f =>
{
f.Close();
f.Dispose();
});
forms.Clear();
await Task.Delay(2000);
return;
}
forms.Add(new Form1());
forms.Last().Show();
}
This will behave like so.
Creates a new task which runs to completion after 2 seconds
Ui Thread is bubbled back up to continue executing/processing other events
Once the 2 seconds is up UI thread returns and resumes executing the async method from after the await
I have this Timer:
Timer delayTimer = new Timer();
delayTimer.Interval = 500;
delayTimer.Elapsed += (object sender, ElapsedEventArgs e) => {
Console.WriteLine("test");
textInputDialog.Show();
delayTimer.Stop();
};
delayTimer.Start();
Here I have the following problems:
Timer never stops. Code is executed every 500ms.
textInputDialog.Show(); doesn't work (perhaps cause of problem above)
What is wrong with my code?
Alternative solutions:
This is an alternative to timer as Jens Horstmann mentioned. And this is called on the UI thread:
private async Task SendWithDelay()
{
await Task.Delay(500);
textInputDialog.Show();
}
Another alternative would be NSTimer:
NSTimer.CreateScheduledTimer(new TimeSpan(0,0,0,0,500), delegate {
textInputDialog.Show();
});
And to invoke a call on the UI thread you can use InvokeOnMainThread:
Timer delayTimer = new Timer();
delayTimer.Interval = 500;
delayTimer.Elapsed += (object sender, ElapsedEventArgs e) => {
delayTimer.Stop();
Console.WriteLine("test");
InvokeOnMainThread (() => {
textInputDialog.Show();
});
};
delayTimer.Start();
Stop the timer before you show the dialog:
delayTimer.Elapsed += (object sender, ElapsedEventArgs e) => {
delayTimer.Stop();
Console.WriteLine("test");
textInputDialog.Show();
};
Also you probably used the wrong timer. Don't use System.Threading.Timer or System.Timers because this involves multithreading which does not work well with winforms or WPF. (This is probably the reason your MessageBox does not show - its called on the wrong thread)
In WPF you should use System.Windows.Threading.DispatcherTimer
Edit
In Winforms you should use System.Windows.Forms.Timer (see comments)
Here is a solution without async/await
It also happen to fit in a single statement, which is rather elegant.
This is a C#, cross-platform solution to perform an action after a delay.
Also works for a recurring task.
using System.Threading;
var delayTimer = new Timer((state) => // Create timer, forget about it
InvokeOnMainThread(() => // Fire on main thread
textInputDialog.Show() // Your code goes here
),
null, // Ignore the state
5 * 1000, // 5 seconds until the 1st fire
Timeout.Infinite); // Do not repeat
Something like this worked for me:
private async Task DelayedShow()
{
await Task.Delay(500);
await _loadPop.textInputDialog.Show();
}
Remember to call the method like this:
BeginInvokeOnMainThread(() => DelayedShow());
I have a WinForms application that consists of a main UI thread and 4 tasks. My main form has a private member level variable like this:
private bool keepThreadsRunning = false;
In the Load() event of my main form, I have the following:
keepThreadsRunning = true;
var task1Worker = Task.Factory.StartNew(() => DoStuff1());
var task2Worker = Task.Factory.StartNew(() => DoStuff2());
var task3Worker = Task.Factory.StartNew(() => DoStuff3());
var task4Worker = Task.Factory.StartNew(() => DoStuff4());
Inside of each of my DoStuff() methods, I basically have this:
while (keepThreadsRunning)
{
// do work here
Thread.Sleep(30000); // a couple of my tasks only need to run every 30 seconds or so
}
Lastly, in my Form_Closing() event handler, I have the following:
keepThreadsRunning = false;
this.Close();
Watching my application in task manager, it appears that the process is ending when I close my form but I'm a little confused about the four tasks. Is my call to this.Close() really causing those tasks to terminate (even if they're in the Thread.Sleep() call when it happens)? And is there a better way of accomplishing this than the way I'm coding it right now?
EDIT - I've looked briefly at task cancellation (when my app exits) but my understanding is that my tasks would need to periodically check the cancellation token to determine if they've been cancelled. Given that some of my tasks need to run every 30 seconds, I couldn't figure out how I'd implement that 30s wait (currently a Thread.Sleep()) and still have the task be checking the cancellation token.
Rather than using a boolean and Thread.Sleep(), use a WaitHandle, specifically a ManualResetEvent, created like this:
var threadTerminationHandle = new ManualResetEvent(false);
In your thread:
do {
// do work here
} while (!threadTerminationHandle.WaitOne(TimeSpan.FromSeconds(30))
This will wait until the WaitHandle is set, or 30 seconds elapses, whichever is sooner.
In your form:
threadTerminationHandle.Set();
Close();
First of all, closing the main UI thread will terminate your other tasks. If you need them to keep running, maybe consider running them in a seperate Console Application, or a Windows Service.
Even if you found a way to delay the closing of the form while you finish running the methods you need to run, this would only work if the end user closed the form in the way you wanted, and Windows being Windows there are a million and one ways to close an application so there is no guarantee that this will work.
For running a method asynchronously every x amount of seconds, you could just use a timer for the whole thing, like so:
using System;
using System.Timers;
using System.Windows.Forms;
namespace WindowsFormsApplication3
{
public partial class Form1 : Form
{
public Form1()
{
InitializeComponent();
}
private void Form1_Load(object sender, EventArgs e)
{
var timer1 = new System.Timers.Timer { Interval = 30000, Enabled = true };
var timer2 = new System.Timers.Timer { Interval = 20000, Enabled = true };
var timer3 = new System.Timers.Timer { Interval = 10000, Enabled = true };
var timer4 = new System.Timers.Timer { Interval = 5000, Enabled = true };
timer1.Elapsed += timer1_Elapsed;
timer2.Elapsed += timer2_Elapsed;
timer3.Elapsed += timer3_Elapsed;
timer4.Elapsed += timer4_Elapsed;
}
void timer4_Elapsed(object sender, ElapsedEventArgs e)
{
//do work here
}
void timer3_Elapsed(object sender, ElapsedEventArgs e)
{
//do work here
}
void timer2_Elapsed(object sender, ElapsedEventArgs e)
{
//do work here
}
void timer1_Elapsed(object sender, ElapsedEventArgs e)
{
//do work here
}
}
}
When you close application, tasks will be closed accordingly because task is processed under background thread from thread pool. So, you don't need to periodically check the cancellation token to determine if they've been cancelled
I wish my method to wait about 500 ms and then check if some flag has changed. How to complete this without blocking the rest of my application?
You can use await Task.Delay(500); without blocking the thread like Sleep does, and with a lot less code than a Timer.
Thread.Sleep(500) will force the current thread to wait 500ms. It works, but it's not what you want if your entire application is running on one thread.
In that case, you'll want to use a Timer, like so:
using System.Timers;
void Main()
{
Timer t = new Timer();
t.Interval = 500; // In milliseconds
t.AutoReset = false; // Stops it from repeating
t.Elapsed += new ElapsedEventHandler(TimerElapsed);
t.Start();
}
void TimerElapsed(object sender, ElapsedEventArgs e)
{
Console.WriteLine("Hello, world!");
}
You can set AutoReset to true (or not set it at all) if you want the timer to repeat itself.
I don't really understand the question.
If you want to block before checking, use Thread.Sleep(500);
If you want to check asynchronously every x seconds, you can use a Timer to execute a handler every x milliseconds.
This will not block your current thread.
It the method in question is executing on a different thread than the rest of your application, then do the following:
Thread.Sleep(500);
System.Threading.Thread.Sleep(500);
Update
This won't block the rest of your application, just the thread that is running your method.
Using a timer should do the trick
if you need to use a thread then here is an example
void Main()
{
System.Threading.Thread check= new System.Threading.Thread(CheckMethod);
check.Start();
}
private void CheckMethod()
{
//Code
Thread.Sleep(500);
}
Asynchron Task:
var task = new Task (() => function_test()); task.Start();
public void function_test() { `Wait for 5000 miliseconds` Task.Delay(5000);` }
I've recently been struggling with the same issue where I needed an action to be run on schedule without blocking the UI.
Here's my solution:
private void Button_Click(object sender, RoutedEventArgs e)
{
RunOnSchedule(interval, cancellationToken);
}
private void RunOnSchedule(int interval, CancellationToken cancellationToken)
{
// Start the task you want to run on schedule
TaskToRunOnSchedule(args);
Task.Run(async () =>
{
// This loop checks if the task was requested to be cancelled every 1000 ms
for (int x = 0; x < interval; x+=1000)
{
if (cancellationToken.IsCancellationRequested)
{
break;
}
await Task.Delay(1000);
}
}).GetAwaiter().OnCompleted(() =>
{
// Once the task for delaying is completed, check once more if cancellation is requested, as you will reach this point regardless of if it was cancelled or not.
if (!cancellationToken.IsCancellationRequested)
{
// Run this method again
RunOnSchedule(interval, cancellationToken);
}
});
}
In a WinForms application, when I want to wait on the main thread without blocking the app, I usually use
private void Wait (double milliseconds)
{
DateTime next = System.DateTime.Now.AddMilliseconds(milliseconds);
while (next > System.DateTime.Now)
Application.DoEvents();
}