edit: Application.DoEvents(); this did it. found here: Force GUI update from UI Thread
c#, winforms. i want to increase a number by steps of 1 and have those increments shown inside a listview, so that the user sees the number counting up (for example from 10 to 15).
i have another button that increments just by 1 when clicked and uses the same display(). it works fine and updates the display as expected.
i tried these codes (shortened to save space here):
(1)
for (int i = 0; i < 5; i++)
{
var t = Task.Run (async () =>
{
myInt++;
await Task.Delay(300);
display(); //forces screen refresh
});
}
(2)
for (int i = 0; i < 5; i++)
{
var t = Task.Run (() =>
{
myInt++;
Task.Delay(300).Wait;
display();
});
//t.Wait(); //causes "An unhandled exception of type 'System.AggregateException' occurred in mscorlib.dll"
}
(3)
for (int i = 0; i < 5; i++)
{
myInt++;
display();
System.Threading.Thread.Sleep(300);
}
(4)
Stopwatch stopwatch = new Stopwatch();
for (int i = 0; i < 5; i++)
{
stopwatch.Restart();
while (true)
{
if (stopwatch.ElapsedMilliseconds >= 300)
{
break;
}
}
stopwatch.Stop();
myInt++;
display();
}
all use this:
private void display()
{
myListView.Items.Clear();
myListView.Items.Add(new ListViewItem(new[] { myInt }));
}
(1) and (2) increment the number by 5 but the display is not updated at all. it shows when the display is updated by some other function.
(3) and (4) increment the number by 5, but the display is only updated after about 1500ms, the display skipping the single steps and displaying just the final result (eg 15).
any suggestions to make this work? can i force a refresh in the display() function somehow?
why does t.Wait(); cause an exception? (i found the task code somewhere on the net)
edit:
(5)
private void team_comm_btn_all_Click(object sender, EventArgs e)
{
for (int i = 0; i < 5; i++)
{
await Run(i); //error 74 see below
}
}
private async Task Run(int i)
{
myInt++;
display();
await Task.Delay(300);
}
await Run(i); gives the following:
Error 74 The 'await' operator can only be used within an async method. Consider marking this method with the 'async' modifier and changing its return type to 'Task'.
just doing "Run(i)" instead gives a warning that "await" is missing... in this case it compiles and increments by 5 without any delay.
(6)
private void team_comm_btn_all_Click(object sender, EventArgs e)
{
for (int i = 0; i < 5; i++)
{
var task = Task.Run(async () =>
{
await Run(i);
});
}
}
private async Task Run(int i)
{
myInt++;
display();
await Task.Delay(300);
}
increments by 5 but does not update display at all.
Normally you would use System.Windows.Forms.Timer for that. But async/await makes such things trivial as soon as you understand it (and read carefully the compiler warning and error messages). Shortly, (5) is the way to go with a small modification resolving the compiler error.
But let start from the beginning. Assuming you first write a normal synchronous version like this (basically your (3))
private void team_comm_btn_all_Click(object sender, EventArgs e)
{
for (int i = 0; i < 5; i++)
{
if (i != 0) System.Threading.Thread.Sleep(300);
myInt++;
display();
}
}
just to see that the UI is not updating. So you decide to turn it into asynchronous, which with the help of async/await is simple replacing Thread.Sleep with Task.Delay like this
private void team_comm_btn_all_Click(object sender, EventArgs e)
{
for (int i = 0; i < 5; i++)
{
if (i != 0) await Task.Delay(300);
myInt++;
display();
}
}
But now it fails to compile with the following error
Error 74 The 'await' operator can only be used within an async method. Consider marking this method with the 'async' modifier and changing its return type to 'Task'.
The message is clear enough. You must mark your method with the async keyword. What about the other recommendation of changing the return type to Task, normally you should take it into consideration, but since you are inside a event handler, i.e. a method which signature cannot be changed, you can ignore that part - that's the exact case why async void is allowed.
To conclude, your final code will look like this
private async void team_comm_btn_all_Click(object sender, EventArgs e)
{
for (int i = 0; i < 5; i++)
{
if (i != 0) await Task.Delay(300);
myInt++;
display();
}
}
Related
I've been using Progress<T> and wondered if it can be replaced by Action<T>.
In the code below, using each of them for reporting progress, i.e. ReportWithProgress() or ReportWithAction(), didn't make any noticeable difference to me. How progressBar1 increased, how the strings were written on the output window, they seemed the same.
// WinForm application with progressBar1
private void HeavyIO()
{
Thread.Sleep(20); // assume heavy IO
}
private async Task ReportWithProgress()
{
IProgress<int> p = new Progress<int>(i => progressBar1.Value = i);
for (int i = 0; i <= 100; i++)
{
await Task.Run(() => HeavyIO());
Console.WriteLine("Progress : " + i);
p.Report(i);
}
}
private async Task ReportWithAction()
{
var a = new Action<int>(i => progressBar1.Value = i);
for (int i = 0; i <= 100; i++)
{
await Task.Run(() => HeavyIO());
Console.WriteLine("Action : " + i);
a(i);
}
}
But Progress<T> can't be a reinvention of the wheel. There should be a reason why it was implemented. Googling "c# Progress vs Action" didn't give me much help. How is Progress different from Action?
Calling progressBar1.Value = i from a different thread results in the dreaded "cross-thread operation not valid" exception. The Progress class, on the other hand, dispatches the event to the synchronization context captured in the moment of construction:
// simplified code, check reference source for actual code
void IProgress<T>.Report(T value)
{
// post the processing to the captured sync context
m_synchronizationContext.Post(InvokeHandlers, value);
}
private void InvokeHandlers(object state)
{
// invoke the handler passed through the constructor
m_handler?.Invoke((T)state);
// invoke the ProgressChanged event handler
ProgressChanged?.Invoke(this, (T)state);
}
This ensures that all updates to progress bars, labels and other UI elements are done on a (one and only) GUI thread.
So, it only makes sense to instantiate the Progress class outside of the background thread, inside a method which is called on a UI thread:
void Button_Click(object sender, EventArgs e)
{
// since this is a UI event, instantiating the Progress class
// here will capture the UI thread context
var progress = new Progress<int>(i => progressBar1.Value = i);
// pass this instance to the background task
Task.Run(() => ReportWithProgress(progress));
}
async Task ReportWithProgress(IProgress<int> p)
{
for (int i = 0; i <= 100; i++)
{
await Task.Run(() => HeavyIO());
Console.WriteLine("Progress : " + i);
p.Report(i);
}
}
The difference is that with a Progress<T> you have an event where multiple listeners can listen for progress and Progress<T> does capture the SynchonizationContext when the instance is constructed and thus does not need to be invoked to the GUI-thread if created in the GUI-thread.
You can also add multiple listeners to an Action<T> (thanks to #Servy for pointing that out), but each of them are then executed in the thread which invokes the action.
Think of the following extended example, where the Progress<T> will work, but the Action<T> will throw an exception:
private async Task ReportWithProgress()
{
var p = new Progress<int>(i => progressBar1.Value = i);
p.ProgressChanged += (s, e) => progressBar2.Value = e;
Task.Run(() =>
{
for (int i = 0; i <= 100; i++)
{
await Task.Run(() => HeavyIO());
Console.WriteLine("Progress : " + i);
((IProgress<int>)p).Report(i);
}
});
}
private async Task ReportWithAction()
{
var a = new Action<int>(i => progressBar1.Value = i);
a += i => progressBar2.Value = i;
Task.Run(() =>
{
for (int i = 0; i <= 100; i++)
{
await Task.Run(() => HeavyIO());
Console.WriteLine("Action : " + i);
a(i);
}
});
}
This question already has an answer here:
Detailed Explanation of Variable Capture in Closures
(1 answer)
Closed 8 years ago.
I expect the variable in the loop to output 1,2,3 to my listbox below. But it outputs
2
2
2
what is wrong?
C# Code
public partial class Tester : Form
{
public int test = 1;
............................
private void button1_Click(object sender, EventArgs e)
{
test++;
for (int i = 0; i < 3; i++)
{
Task t = Task.Factory.StartNew(() =>
{
System.Threading.Thread.Sleep(5000);
}).ContinueWith(o =>
{
listBox1.Items.Add(test);
}, TaskScheduler.FromCurrentSynchronizationContext());
}
}
If you want to know the sequence in which the Tasks are done you can use the following:
private void button1_Click(object sender, EventArgs e)
{
for (int i = 0; i < 3; i++)
{
int tasknumber = test;
Task t = Task.Factory.StartNew(() =>
{
System.Threading.Thread.Sleep(5000);
return tasknumber;
}).ContinueWith(o =>
{
listBox1.Items.Add(o.Result);
}, TaskScheduler.FromCurrentSynchronizationContext());
test++;
}
}
With this code, the value of test is increased at the end of every loop. In the first Task, the tasknumber will be 1. In the second Task the tasknumber will be 2 etc. When the Task is done, the result will be passed to your ListBox.
You might also want to read this blog about StartNew.
Here is my code:
private void RunCoinFlip()
{
ToggleControlsUsability();
Task task = new Task(CoinFlippingAnimation);
task.Start();
task.Wait();
ToggleControlsUsability();
flipOutcome = picCoin.Image == coinSideImages[0] ? CoinSides.Heads : CoinSides.Tails;
lblResult.Text = userGuess == flipOutcome ? "Congrats you won!" : "Too bad you lost.";
}
private void ToggleControlsUsability()
{
btnHeads.Enabled = !btnHeads.Enabled;
btnTails.Enabled = !btnTails.Enabled;
}
private void CoinFlippingAnimation()
{
Random rng = new Random();
for (int i = 0; i < 15; i++)
{
int side = rng.Next(0, coinSideImages.Length);
picCoin.Image = coinSideImages[side];
Thread.Sleep(100);
}
}
Basically, the buttons should be frozen during the operation and be unfrozen afterwards, and the coin flipping animation flips the coin. Unfortunately, the GUI is locked up during the animation so you can't move the window or resize.
I've been reading about async and await but I'm not sure if that applies here or how to add it. The different results I try always lead to either blocking, instant unfreezing of controls, or cross-thread execution errors.
There's no need for another thread here, since the time is all being spent in Thread.Sleep and not the random number generation. So, a simple async solution would be:
private async Task RunCoinFlipAsync()
{
ToggleControlsUsability();
await CoinFlippingAnimationAsync();
ToggleControlsUsability();
flipOutcome = picCoin.Image == coinSideImages[0] ? CoinSides.Heads : CoinSides.Tails;
lblResult.Text = userGuess == flipOutcome ? "Congrats you won!" : "Too bad you lost.";
}
private async Task CoinFlippingAnimationAsync()
{
Random rng = new Random();
for (int i = 0; i < 15; i++)
{
int side = rng.Next(0, coinSideImages.Length);
picCoin.Image = coinSideImages[side];
await Task.Delay(100);
}
}
I have multiple tasks that will be running in parallel. They are gathered in a list of tasks. Each task has a progress bar associated with it. I've been trying to implement the IProgress API in order to update the progress bars while the task runs. However, the progress bar only updates when the task is completed.
I have a set of test code that simulates my problem, and no matter how I modify it, the task bars only update when the task is completed.
As each task is created, I pass Progress object that will update the right progress bar. This Progress object is passed to the inner task to be updated with each loop. The Progress.Report() method will check for an InvokeRequired, although I don't think this should be necessary if you're using the IProgress API with asynchronous methods.
I've included the code. The form simply has a button (button1) that starts all the processes, and there are 11 progress bars that should update with each task.
public partial class Form1 : Form
{
public Form1()
{
InitializeComponent();
}
private async void button1_Click(object sender, EventArgs e)
{
List<ProgressBar> listOfProgressBars = new List<ProgressBar>
{
progressBar1,
progressBar2,
progressBar3,
progressBar4,
progressBar5,
progressBar6,
progressBar7,
progressBar8,
progressBar9,
progressBar10,
progressBar11
};
List<Task<bool>> taskList = new List<Task<bool>>();
foreach (ProgressBar pBar in listOfProgressBars)
{
pBar.Value = 0;
pBar.Minimum = 0;
pBar.Maximum = 100;
}
int i = 11;
for (int j = 1; j <= i; j++)
{
ProgressBar thisProgressBar = listOfProgressBars[j - 1];
var progress = new Progress<int>((int value) =>
{
UpdateProgressBar(thisProgressBar, value);
});
taskList.Add(InnerProcess(j, progress));
}
await Task.WhenAll(taskList);
}
public void UpdateProgressBar(ProgressBar pBar, int value)
{
if (this.InvokeRequired)
{
this.EndInvoke(this.BeginInvoke(new MethodInvoker(delegate() { UpdateProgressBar(pBar, value); })));
}
else
{
pBar.Value = value;
}
}
public Task<bool> InnerProcess(int waitTime, IProgress<int> progress)
{
return Task<bool>.Run(() =>
{
var job = new LongJob();
job.Delay(waitTime, progress);
return true;
});
}
class LongJob
{
public void Delay(int i, IProgress<int> progress)
{
for (int j = 1; j <= i; j++)
{
Thread.Sleep(500);
if (progress != null)
{
progress.Report(j/i*100);
}
}
}
}
}
progress.Report(j/i*100);
j/i is integer division, since j is less that i until completion, j/i is 0.
cast j to a float and all should be good
You have a bug in your progress calculation due to integer division. Change it to this:
progress.Report((int)(((double)j) / i * 100));
Previously, when making a looping task it would run perfectly the first time and fall apart because of the timing problems, such as the functions of one individual task would start running faster than another, even though it was programmed to come before that task.
So my idea to figure out a way to repeat the events of a task once the delays(times) have ended, in my head it would go something like this:
private async Task programCore()
{
int n = 1000;
for (int i = 0; i < n; )
{
FirstThing();
await Task.Delay(2100);
SecondThing();
await Task.Delay(2200);
ThirdThing();
await Task.Delay(2300);
}
////////////////////////////////////////////////////////////////////////////
else if (programCore.TasksHaveAllTimedOut) // Idealistic code.
{
programCore.Restart();
}
}
Is something like this possible? If not, is there a better way to do it?
How about this:
private async Task programCore()
{
int n = 1000;
for (int i = 0; i < n; )
{
await FirstThing();
await SecondThing();
await ThirdThing();
}
}
private async Task FirstThing()
{
// Do something here
}
private async Task SecondThing()
{
// Do something here
}
private async Task ThirdThing()
{
// Do something here
}
This has the effect that it runs FirstThing, waits for it to finish, then runs SecondThing, waits for it to finish, then finally runs ThirdThing and waits for it to finish before repeating the loop.
I believe #CodingGorilla has the right idea. Your code should look something like this (using CancellationTokens for timeouts):
private async Task programCore()
{
int n = 1000;
for (int i = 0; i < n; ++i)
{
var timeout1 = new CancellationTokenSource(2100);
await FirstThingAsync(timeout1.Token);
var timeout2 = new CancellationTokenSource(2200);
await SecondThingAsync(timeout2.Token);
var timeout2 = new CancellationTokenSource(2300);
await ThirdThingAsync(timeout3.Token);
}
}
The code above will raise an exception if any task exceeds its timeout, which may not be exactly what you want. You can catch the OperationCanceledException if you wish to handle this differently.