C# in Async Task change Label Text - c#

The following Code does not change the Text and stops executing the Task
private void button1_Click(object sender, EventArgs e)
{
label1.Text = "Test";
Task.Run(() => MyAsyncMethod());
}
public async Task MyAsyncMethod()
{
label1.Text = "";
//everything from here on will not be executed
}
would be really handy if you could use async together with the UI

for accessing a GUI control through a second thread you need to invoke.
following example shows how to set a label's text properly
private void setLabel1TextSafe(string txt)
{
if(label1.InvokeRequired)
label1.Invoke(new Action(() => label1.Text = txt));
else
label1.Text = txt;
}
I hope this solves your problem

would be really handy if you could use async together with the UI
The design of async was carefully done so you can use it naturally with the UI.
in my code i run a function that does a lot of IO and stuff that takes a long time
If you have asynchronous I/O methods (which you should), then you can just do this:
private async void button1_Click(object sender, EventArgs e)
{
label1.Text = "Test";
await MyMethodAsync();
}
public async Task MyMethodAsync()
{
label1.Text = "";
await ...; // "lot of IO and stuff"
label1.Text = "Done";
}
That's the most natural approach.
However, if you need to run code on a background thread (e.g., it's actually CPU-bound, or if you just don't want to make your I/O operations asynchronous like they should be), then you can use IProgress<T>:
private void button1_Click(object sender, EventArgs e)
{
label1.Text = "Test";
var progress = new Progress<string>(update => { label1.Text = update; });
await Task.Run(() => MyMethod(progress));
}
public void MyMethod(IProgress<string> progress)
{
if (progress != null)
progress.Report("");
...; // "lot of IO and stuff"
if (progress != null)
progress.Report("Done");
}
Under no circumstances should modern code use Control.Invoke or (even worse) Control.InvokeRequired.

Task.Run is used to envelope an Action (that is not async) into a Task. Any Task that you want to execute should be awaited. Thus, that Task.Run of yours is rigorously doing nothing.
Mark that button1_Click event handler of yours as async. Then remove that Task.Run and instead do await MyAsyncMethod().

Try this. You don't need to fire a new thread to invoke the async method. compiler will do it for you.
private void button1_Click(object sender, EventArgs e)
{
label1.Text = "Test";
MyAsyncMethod();
}
public async Task MyAsyncMethod()
{
return await Task.Run(() =>{
label1.Text = "";
//everything from here on will not be executed
}
}

I think both the questions and some of the answers are not clear. Depending on where in the task thread you need to update the label you have to use invoke. Otherwise you can leverage await and leverage the standard semantics.
private async void button1_Click(object sender, EventArgs e)
{
label1.Text = "Starting to run a long task... carry on...";
await snooze(3);
label1.Text = "Done with long task";
}
public Task<int> snooze(int seconds)
{
label1.Text = "zzzz...";
return Task.Run(
() => {
label1.Invoke(new Action(() => label1.Text = "For some reason I need to alert you here.. bad dream perhaps...direct access to label1 will fail"));
Thread.Sleep(seconds * 1000);
return seconds;
});
}

Related

C# WPF Update Label before and after processing - immediately

I have already tried several online examples (Thread, Dispatcher, await/async) but none is working for me in my C#/WPF project.
I have the following button click method:
private void BtnInstall_Click(object sender, RoutedEventArgs e)
{
this.lblResponse.Content = "";
executeInstall(); //do some work
this.lblResponse.Content = "DONE";
}
The label gets updated afterwards to DONE, but when I click again on the button the label isnt getting emptied before the processing of executeInstall.
As I mentioned I already tried several different examples from other questions (Dispatcher.BeginInvoke, Thread, Task, await/async) but none of them has worked - the label change before is never done before the processing of executeInstall.
I am working in .NET framework 4.7.2.
Is there maybe a setting that debug mode only executes the program with one thread and thats maybe why none of the solutions works for me?
Use async for that.
private async void BtnInstall_Click(object sender, RoutedEventArgs e)
{
this.lblResponse.Content = "";
await Task.Run(()=> executeInstall());
this.lblResponse.Content = "DONE";
}
UPDATE: If you need to access the UI inside your executeIntall method you will need to invoke the Dispatcher. In this case you would need to delay the Task to give the label time to update before the install starts. Note that this will cause the UI to freeze during the entire install.
private async void BtnInstall_Click(object sender, RoutedEventArgs e)
{
lblResponse.Content = "starting...";
await Task.Delay(100).ContinueWith(_=>
{
App.Current.Dispatcher.Invoke(() =>
{
executeInstall();
lblResponse.Content = "DONE";
});
});
}
A better approach would be to only call the dispatcher when it's actually needed. This would keep the UI responsive during the entire process.
private async void BtnInstall_Click(object sender, RoutedEventArgs e)
{
lblResponse.Content = "starting...";
await Task.Run(()=> executeInstall());
lblResponse.Content = "DONE";
}
private void executeInstall()
{
Thread.Sleep(1000); //do time consuming operation
App.Current.Dispatcher.Invoke(() => lblResponse.Content = "Downloading Files...");
Thread.Sleep(1000); //do time consuming operation
App.Current.Dispatcher.Invoke(() => lblResponse.Content = "Unzipping Files...");
Thread.Sleep(1000); //do time consuming operation
App.Current.Dispatcher.Invoke(() => lblResponse.Content = "Updating Files...");
Thread.Sleep(1000); //do time consuming operation
}

ObjectDisposedException when form is being closed

I have a timer on WinForm which I start when the form loads:
private void MainForm_Load(object sender, EventArgs e)
{
Action action = () => lblTime.Text = DateTime.Now.ToLongTimeString();
Task task = new Task(() => {
while (true)
{
Invoke(action);
Task.Delay(1000);
}
});
task.Start();
}
The problem is when I start the app in Debug mode in VS and the close it. I get an ObjectDisposedException which states that my form is already disposed.
I tried to fix it the following way:
private bool _runningTimer = true;
public MainForm()
{
InitializeComponent();
// ...
FormClosing += MainForm_FormClosing;
}
private void MainForm_FormClosing(object sender, FormClosingEventArgs e)
{
_runningTimer = false;
}
private void MainForm_Load(object sender, EventArgs e)
{
Action action = () => lblTime.Text = DateTime.Now.ToLongTimeString();
Task task = new Task(() => {
while (_runningTimer)
{
Invoke(action);
Task.Delay(1000);
}
});
task.Start();
}
But the problem still ocurrs. What Am I doing wrong here?
UPDATE: I know that there is a standart timer for WinForms that works great in multithreaded invironment. I just wanted to know how it is possible to make it work to better understand how to deal with race conditions. This kind of timer is just an example, it could be another process that needs to update GUI.
UPDATE 2: Following the Hans Passant and Inigmativity answers I came to that code:
private void MainForm_Load(object sender, EventArgs e)
{
Action action = () => { lblTime.Text = DateTime.Now.ToLongTimeString(); };
Task task = new Task(async () => {
while (!IsDisposed)
{
Invoke(action);
await Task.Delay(1000);
}
});
task.Start();
}
But anyway if I make time interval, for example 100ms, the ObjectDisposedException still throws.
This is not real life example, I just experimenting with it...
In your first example the Task has no idea your app is exiting and is at risk of invoking the action after the label is destroyed, hence the ObjectDisposedException.
Though you attempt to alert the task in the second example, it isn't really that thread-safe and you could still invoke the action after the control is disposed.
Timers
A better solution is to just use a WinForms Timer. If you place the timer on the form via the designer, it automatically registers it as a component dependency making lifetime management much easier.
With WinForm timers you don't need to worry about threads or Tasks and more importantly you won't need to worry about Invoke if you need to update the UI (as opposed to child threads or non-UI context tasks)
Tell me more
How to: Run Procedures at Set Intervals with the Windows Forms Timer Component
Ok, I tried to use task cancellation the following way:
public MainForm()
{
InitializeComponent();
cts = new CancellationTokenSource();
Load += MainForm_Load;
FormClosing += MainForm_FormClosing;
}
private void MainForm_FormClosing(object sender, FormClosingEventArgs e)
{
cts.Cancel();
}
private void MainForm_Load(object sender, EventArgs e)
{
CancellationToken ct = cts.Token;
Action action = () => { lblTime.Text = DateTime.Now.ToLongTimeString(); };
var task = Task.Factory.StartNew(async () => {
ct.ThrowIfCancellationRequested();
while (true)
{
Invoke(action);
await Task.Delay(100);
}
}, ct);
}
Don't know whether it's right but it seems works even if the time interval set to 10 ms.

is wrapper necessary in async await structure

I'm relatively new to C# and I've been learning asynchronous programming. So I've been trying to make a simple UI with a label and a button and when I press the button, the label text changes and I've been taught to do it like this:
private void button1_Click(object sender, EventArgs e)
{
CallChangeLable();
label1.Text = "Loading ...";
}
private async void CallChangeLable()
{
var result = await ChangeLabelAsync("Oliver");
label1.Text = result;
}
private Task<string> ChangeLabelAsync(string name)
{
return Task.Run(() => ChangeLable(name));
}
private string ChangeLable(string name)
{
Thread.Sleep(2000);
return $"Hi {name}";
}
now I've tried this code without the wrapper step:
private Task<string> ChangeLabelAsync(string name)
{
return Task.Run(() => ChangeLable(name));
}
Of course I moved the task.run method to somewhere else and I modified the naming and the code still works fine, the UI is still responsive. My question is, why is the wrapper step important and can I skip it?
Here's the modified code:
private void button1_Click(object sender, EventArgs e)
{
CallChangeLable();
label1.Text = "Loading ...";
}
private async void CallChangeLable()
{
var result = await Task.Run(() => ChangeLable("Mahmoud"));
label1.Text = result;
}
private string ChangeLable(string name)
{
Thread.Sleep(2000);
return $"Hi {name}";
}
My question is, why is the wrapper step important and can I skip it?
Actually, you shouldn't have a wrapper. Providing a fake-asynchronous method is an antipattern.
using SynchronizationContext you can do something like this
private async void button1_Click(object sender, EventArgs e)
{
SynchronizationContext.Current.Post((txt)=>{
label1.Text = txt;
},"Loading....");
}
Well, you want to ensure that you don't block the UI thread - So don't skip it.
Stephen Cleary has some excellent articles on the subject:
https://blog.stephencleary.com/2013/11/taskrun-etiquette-examples-dont-use.html
For Async/Await programming in general take a look at the best practices from an old msdn post also by Stephen Cleary:
https://msdn.microsoft.com/en-us/magazine/jj991977.aspx
Hope that helps.

Is this a good way to start a anonymous task and continue with ui thread?

there have been some time since i worked with tasks and lambda expressions. Is this a good way to run a anonymous task with a lambda expression and then run code on the UI thread when task is finished?
private void btn_mods_Click(object sender, RoutedEventArgs e)
{
function_buttons_stackpanel.IsEnabled = false;
Loading();
Task task = new Task(() => {
if (IsServiceIsUp() != false)
{
webServiceMods = JsonConvert.DeserializeObject(_webServiceResponse).mods;
webServiceBaseUrl = JsonConvert.DeserializeObject(_webServiceResponse).basePath;
Console.Write(webServiceBaseUrl);
}
});
task.Start();
task.ContinueWith((foo) =>
{
FinishedLoading();
function_buttons_stackpanel.IsEnabled = true;
}, TaskScheduler.FromCurrentSynchronizationContext());
}
private void Loading()
{
img_loading.Visibility = Visibility.Visible;
}
private void FinishedLoading()
{
img_loading.Visibility = Visibility.Collapsed;
}
I tried to chain the task.Start directly but that gave me an error Cannot Implicitly convert type void to System.Threading.Tasks.Task.
Basically what i wanted to do was to chain the whole process from start to end.
Task task = new Task(() => {
if (IsServiceIsUp() != false)
{
webServiceMods = JsonConvert.DeserializeObject(_webServiceResponse).mods;
webServiceBaseUrl = JsonConvert.DeserializeObject(_webServiceResponse).basePath;
Console.Write(webServiceBaseUrl);
}
}).Start();
In PHP I would do something like this:
$task = new Task(() => {
if (IsServiceIsUp() != false)
{
$webServiceMods = JsonConvert::DeserializeObject($_webServiceResponse).mods;
$webServiceBaseUrl = JsonConvert::DeserializeObject($_webServiceResponse).basePath;
Console::Write($webServiceBaseUrl);
}
})
->Start()
->ContinueWith(($foo) =>
{
FinishedLoading();
$function_buttons_stackpanel.IsEnabled = true;
}, TaskScheduler::FromCurrentSynchronizationContext());
Is this possible? If so, is there any reason to not do it, and if there is a better way to do this, could you give me an example?
And thanks!
You can do this rather easily and a bit cleaner with async-await:
private async void btn_mods_Click(object sender, RoutedEventArgs e)
{
if (!IsServiceIsUp())
return;
function_buttons_stackpanel.IsEnabled = false;
Loading();
await Task.Run(() =>
{
var result = JsonConvert.DeserializeObject(_webServiceResponse);
Console.Write(result.webServiceBaseUrl);
});
FinishedLoading();
function_buttons_stackpanel.IsEnabled = true;
}
Performance wise, I wouldn't be so sure you'd need to use a threadpool thread just for deserializing a JSON. I would definitely test this code to determine if it's worth it.
If you declare your event handler as async function, then you don't have to start the task, it already runs asynchronously.
All async functions should return Task instead of void and Task<TResult> instead of TResult. The only exception is the event handler. The event handler returns void
In proper async-await this would be as follows:
private async void btn_mods_Click(object sender, RoutedEventArgs e)
{
function_buttons_stackpanel.IsEnabled = false;
Loading();
if (IsServiceIsUp())
{ // The service is up, start the deserialization
// because this function is async, you can call other async functions
// without having to start a task
// The UI remains responsive
webServiceMods = await JsonConvert.DeserializeObjectAsync(_webServiceResponse).mods;
// because of the await above, the second task is started after the first is finished:
webServiceBaseUrl = await JsonConvert.DeserializeObjectAsync(_webServiceResponse).basePath;
// because of the await the second task is also finished
Console.Write(webServiceBaseUrl);
FinishedLoading();
function_buttons_stackpanel.IsEnabled = true;
}
}
By making your event handler async your code will make full usage of async-await. Your code would look much neater without the magic of continueWith and other Task related functions from before the async-await era.
One final remark: the following code looks silly and unnecessary difficult:
if (IsServiceIsUp() != false) ...
This should of course be:
if (IsServiceIsUp()) ...

check if task with specific method is already running

I have async method OnValueChange where i call static method as a task, my method is updating gui with help of Iprogress intereface and my method is returning Image:
public async void OnValueChange(object sender, EventArgs e)
{
var progress = new Progress<int>(i => ProgresBar.Value = i);
Image im = await Task.Factory.StartNew(() => MyStaticClass.MyStaticFunction(progress),
TaskCreationOptions.LongRunning);
Picture = im;
}
In some cases i am calling OnValueChange function frequently in short period of time, but i want only one task running at time.
what is most efficient method to check if task with specific method is already running?
You should avoid async void. On a side note, I explain on my blog that the way you're using StartNew is dangerous (you should use Task.Run instead).
Applying both of these guidelines to your example, the code will look like this:
public async Task OnValueChangeAsync()
{
var progress = new Progress<int>(i => ProgresBar.Value = i);
Image im = await Task.Run(() => MyStaticClass.MyStaticFunction(progress));
Picture = im;
}
public async void OnValueChange(object sender, EventArgs e)
{
await OnValueChangeAsync();
}
At this point, it becomes more clear how to detect whether a method is already running (since that method actually returns a Task now). One implementation is as such:
private async Task OnValueChangeImplAsync()
{
var progress = new Progress<int>(i => ProgresBar.Value = i);
Image im = await Task.Run(() => MyStaticClass.MyStaticFunction(progress));
Picture = im;
_onValueChangeTask = null;
}
private Task _onValueChangeTask;
public Task OnValueChangeAsync()
{
if (_onValueChangeTask != null)
return _onValueChangeTask;
_onValueChangeTask = OnValueChangeImplAsync();
return _onValueChangeTask;
}
public async void OnValueChange(object sender, EventArgs e)
{
await OnValueChangeAsync();
}

Categories

Resources