Windows Phone 8.1 Showing progress view until async/await task finishes - c#

I have two tasks which download an mp3 from web address inside local storage and returns timeSpan.
public async Task<TimeSpan> filedownload_Ignition_Outside()
{
Uri sourceIgnition_Outside = new Uri(TemporaryAddress.download_playCarSound+"Ignition_Outside");
//download and store file
return duration_Ignition_Outside;
}
public async Task<TimeSpan> filedownload_Idle_Outside()
{
Uri sourceIdle_Outside = new Uri(TemporaryAddress.download_playCarSound +"Idle_Outside");
StorageFile destinationFileIdle_Outside;
//download and store file
return duration_Idle_Outside;
}
Now I need to show an indeterminate progressbar in UI while downloading starts till it ends But dont know how to find the task completed?
On my NavigatedTo function I have set it as async and am calling
await downloadFile();
Now inside my downloadFile()
public async Task<TimeSpan> downloadFiles()
{
//ProgressShow
var temp= await filedownload_Ignition_Outside();
//if (filedownload_Ignition_Outside().IsCompleted)
//{
// //progressStop
//}
return temp;
}
But its not executing the stop statement as it waits asynchronously how can I get the event where both task gets finished? Can I call both tasks inside my downloadFiles() method and still able to get an event for both task completion.

Try something similar to the code given below in your OnNavigatedTo event.
ShowProgress=true;
downloadFiles().ContinueWith((sender) =>
{
this.Dispatcher.RunAsync(CoreDispatcherPriority.Normal, () =>
ShowProgress=false;
);
});
You need to run the ShowProgress=False; in UI thread

Related

Chain tasks using continuation task

I'm trying to chain tasks, so as soon as the one finishes the next starts, but the UI doesn't update. I did a course in react and the one lesson is where you update the UI based on state changes in the application, and that is what I'm trying to replicate. Change the state of the application (basically I'll be running methods that run return a bool for validation), and then update the UI accordingly, I'm also using binding, but for some reason its not running as intended, I don't know if I follow the documentation incorrectly. What can I change or fix to make this work and is it practically correct to use more than one task in a single async Task<T> method
public async Task<string> Connect_To_Ip()
{
await Task.Run(() =>
{
details.State = "Connection To IP 127.0.01.258.....";
Task.Delay(5000).Wait();
}).ContinueWith(result => new Task(async () =>
{
await Task.Run(() =>
{
if (result.Status == TaskStatus.RanToCompletion)
{
details.State = "Validating Card Number......";
}
});
}), TaskContinuationOptions.OnlyOnRanToCompletion);
return details.State;
}
How I'm calling the original task
Task connect = Connect_To_Ip();
await connect;
When you use await then you don't need Task.ContinueWith. Everything that follows the awaited operation is a continuation. Since you want to validate on a background thread, you must post the changes back to the UI thread in order to update the UI elements, otherwise you will produce cross-thread exceptions.
This is because UI elements can't be updated from a background thread, except the update occurs via INotifyPropertyChanged and data binding.
One way to do this is to use the Dispatcher to invoke UI manipulations on the UI thread or use the Progress<T> class, which will always execute the registered callback on the UI thread.
Your fixed and simplified code could look like this example:
public async Task ValidateAsync()
{
// Register the callback that updates the UI with the 'progressReporter'.
// Progress<T> must be instantiated on the UI thread it is associated with
var progressReporter = new Progress<string>(message => details.State = message);
// Execute the operation on a background thread
await Task.Run(() => ConnectToIp(progressReporter));
// Continuation starts here, after await
}
public async Task ConnectToIp(IProgress<string> progressReporter)
{
progressReporter.Report("Connection To IP 127.0.01.258.....");
await Task.Delay(TimeSpan.FromSeconds(5));
// Continuation starts here, after await
progressReporter.Report("Validating Card Number......");
}
It is recommended to use async APIs when possible instead of using background threads. For example, to connect to a server without blocking the UI you can use
HttpClient client = new HttpClient();
HttpResponseMessage response = await client.GetAsync("http://www.contoso.com/");
Many IO classes provide an async API.
Furthermore, I recommend to take a look at the INotifyDataErrorInfo interface. It is the recommended way to implement property validation and allows to provide UI error feedback in a very easy way.
I did this in Windows Forms (I had a test Windows Forms project open), but it should be about the same in WPF. I dropped a button, a label and a text box on the form. Then I wrote this code:
private async void button1_Click(object sender, EventArgs e)
{
var result = await ValidateTextBox();
if (result != null)
{
label1.Text = result;
return;
}
var intResult = await ReadTextBox();
label1.Text = intResult.ToString();
await IncrementTextBox();
intResult = await ReadTextBox();
label1.Text = intResult.ToString();
}
private async Task<string> ValidateTextBox()
{
await Task.Delay(2000);
if (!int.TryParse(textBox1.Text, out _)) {
return "Not Valid";
}
//otherwise
return null;
}
private async Task<int> ReadTextBox()
{
await Task.Delay(3000);
if (!int.TryParse(textBox1.Text, out var result))
{
throw new Exception("Don't do that");
}
return result;
}
private async Task IncrementTextBox()
{
await Task.Delay(3000);
if (!int.TryParse(textBox1.Text, out var result))
{
throw new Exception("Don't do that");
}
textBox1.Text = (result + 1).ToString();
}
If you type something that's not an int into the text box and press the button, a few seconds go by, and then Not Valid shows up in the label.
If there is a number there, then there is a pause and the number shows up in the label. Then another pause and the text box number will increment by 1. Finally after another pause, the label will show the incremented value.
Note that this all runs on a single thread. But, in spite of all the delays, the UI remains responsive the whole time.
Put breakpoints at the start of each function and on the lines after each of the awaits in the button click handler. Step through (not into) the whole thing and you'll see how the awaits create continuations

WPF Download files in the background [duplicate]

This question already has answers here:
Execute task in background in WPF application
(3 answers)
Closed 2 years ago.
I am making a WPF app that needs to download multiple files from online. Currently, when I call the event which leads to the file download, my app hangs for a while as it downloads the file and only returns to functioning when the download is complete. Is there a way to make it such that the file downloads in the background and the application doesn't hang? I'm relatively new at this so the most information possible would be excellent. Thank you!
Edit: I should have been more clear. I have a function called downloadFile(), and when the main event is triggered, I need to download multiple files. Here is my code:
private void DownloadAll()
{
foreach(string moddiekey in moddiekeys)
{
download_file_by_moddiekey(moddiekey );
}
}
private async void Event(){
await Task.Run(() => { DownloadAll(); });
}
But this still hangs my application.
Edit 2: Showing download functions
private void download_file_by_url(string url)
{
try
{
string[] splits = url.Split('/');
string filename = splits[splits.Length - 1];
WebClient wc = new WebClient();
wc.DownloadFile(url, filename);
wc.Dispose();
}
catch { }
}
private void download_file_by_moddiekey(string moddiekey)
{
...
download_file_by_url("github link/" + moddict[moddiekey]);
...
}
I have a couple suggestions that may help. First looking at your code my assumption is that Event() is tied to a UI event. Calling await on a UI thread will always freeze the UI. One approach to preventing this is to use ContinueWith(). Implementing this in your case would look like:
private async void Event(){
DownloadAll().ContinueWith(OnDownloadComplete);
}
private void OnDownloadComplete(Task t)
{
if (t.IsCanceled || t.IsFaulted)
{
// indicate to user that download has failed
}
// optionally perform a task after download has been completed
}
Because the await is removed from the call to DownloadAll() the UI can continue doing it's thing well the tasks complete. After the task is completed the OnDownloadComplete() method will be called.
If you want to update the UI (e.g. display download status) from one of these methods (which would be running on a non-UI thread) you will need to send those updates back to the UI thread:
private void UpdateStatus(string message)
{
Dispatcher.Invoke(() => {
MyLabel.Text = message;
});
}
then in your download_file() method you could do something like:
private async void download_file_by_url(string url)
{
// code to download file omitted
UpdateStatus($"Downloaded file from {url}!");
}
if that doesn't work I would trying adding a ConfigureAwait call to your task. E.g:
private async void SomeMethodThatUsesAwait()
{
await DoSomething().ConfigureAwait(false);
}

Inform that a long running async task is in progress - the right way

I have a console program which sends async HTTP requests to an external web API. (HttpClient.GetAsync());)
These tasks can take several minutes to complete - during which I'd like to be able to show to the user that the app is still running - for example by sending Console.WriteLine("I ain't dead - yet") every 10 seconds.
I am not sure how to do it right, without the risk of hiding exceptions, introducing deadlocks etc.
I am aware of the IProgress<T>, however I don't know whether I can introduce it in this case. I am await a single async call which does not report progress. (It's essentially an SDK which calls httpClient GetAsync() method
Also:
I cannot set the GUI to 'InProgress', because there is no GUI, its a console app - and it seems to the user as if it stopped working if I don't send an update message every now and then.
Current idea:
try
{
var task = httpClient.GetAsync(uri); //actually this is an SDK method call (which I cannot control and which does not report progress itself)
while (!task.IsCompleted)
{
await Task.Delay(1000 * 10);
this.Logger.Log(Verbosity.Verbose, "Waiting for reply...");
}
onSuccessCallback(task.Result);
}
catch (Exception ex)
{
if (onErrorCallback == null)
{
throw this.Logger.Error(this.GetProperException(ex, caller));
}
this.Logger.Log(Verbosity.Error, $"An error when executing command [{action?.Command}] on {typeof(T).Name}", ex);
onErrorCallback(this.GetProperException(ex, caller));
}
Let me tidy this code up a bit for you
async Task Main()
{
var reporter = new ConsoleProgress();
var result = await WeatherWaxProgressWrapper(() => GetAsync("foo"), reporter);
Console.WriteLine(result);
}
public async Task<int> GetAsync(string uri)
{
await Task.Delay(TimeSpan.FromSeconds(10));
return 1;
}
public async Task<T> WeatherWaxProgressWrapper<T>(Func<Task<T>> method, System.IProgress<string> progress)
{
var task = method();
while(!task.IsCompleted && !task.IsCanceled && !task.IsFaulted)
{
await Task.WhenAny(task, Task.Delay(1000));
progress.Report("I ain't dead");
}
return await task;
}
public class ConsoleProgress : System.IProgress<string>
{
public void Report(string value)
{
Console.WriteLine(value);
}
}
You could have a never-ending Task as a beacon that signals every 10 sec, and cancel it after the completion of the long running I/O operation:
var beaconCts = new CancellationTokenSource();
var beaconTask = Task.Run(async () =>
{
while (true)
{
await Task.Delay(TimeSpan.FromSeconds(10), beaconCts.Token);
Console.WriteLine("Still going...");
}
});
await LongRunningOperationAsync();
beaconCts.Cancel();
You are looking for System.Progress<T>, a wonderful implementation of IProgress.
https://learn.microsoft.com/en-us/dotnet/api/system.progress-1
You create an object of this class on the "UI thread" or the main thread in your case, and it captures the SynchronizationContext for you. Pass it to your worker thread and every call to Report will be executed on the captured thread, you don't have to worry about anything.
Very useful in WPF or WinForms applications.

Wait before execute next code in loop wpf

I have a list of download links I am using cefsharp to download. I can't use webclient in order to download the files, the user must be logged in.
Here is my code:
foreach (var lnk in collection) {
Console.WriteLine(lnk);
await TestAsync(lnk);
}
private Task TestAsync(string lnk) {
return Task.Run(() => TaskToDo(lnk));
}
private async void TaskToDo(string lnk) {
await wb.GetBrowser().MainFrame.EvaluateScriptAsync(String.Format("window.location.href = '{0}'", lnk));
}
In my loop, I have a Console.WriteLine to print the links. In my program output, it prints all the links, then it downloads a file. The problem is that it only downloads the last file. I need it to wait and download the next file.
How can I solve this?
Your problem is
async void
That will NOT block by the caller, which is why your doing a Task.Run when you don't need to.
foreach (var item in collection)
{
Console.WriteLine(lnk);
await TestAsync(lnk);
}
private Task TestAsync(string lnk)
{
return TaskToDo(lnk);
}
private Task TaskToDo(string lnk)
{
return wb.GetBrowser().MainFrame.EvaluateScriptAsync(String.Format("window.location.href = '{0}'", lnk));
}
You are not using await keyword properly. In this case, you are telling that you want to wait each time you iterate loop. It is better to return Task from TestAsync(lnk) and put on the task list.
So, instead await method in foreach loop you should use Task.WhenAll() for all tasks like in following example:
List<Task> taskList = new List<Task>();
foreach (var lnk in collection)
{
Console.WriteLine(lnk);
var task = TestAsync(lnk);
taskList.Add(task);
}
await Task.WhenAll(taskList.ToArray());
Regarding:
The problem is that it only downloads the last file. I need it to wait
and download the next file.
How can I solve this?
I suspect that you cannot download file with window.lotion.href asynchronously because you are changing the location of window sequentially and before the previous operation complete!
Basically, while you are in middle of downloading LINK1 another task, example LINK3 will start to download and LINK1 will be stopped. This is the reason why you have only one file downloaded. Try to use window.open() or different some different technique.
Your code should be async all the way and you should not block on async code. Please refer to the #Stephen Cleary's MSDN article for more information: https://msdn.microsoft.com/en-us/magazine/jj991977.aspx
The issue here is that your TestAsync method starts a new Task, which will be executed on a background thread, and returns immediately before the task has actually finisihed.
You should make the TestAsync method async and change the return type of the TaskToDo method to Task to be able to await this one:
foreach (var lnk in collection) {
Console.WriteLine(lnk);
await TestAsync(lnk);
}
private async Task TestAsync(string lnk)
{
await TaskToDo(lnk);
}
private async Task TaskToDo(string lnk)
{
await wb.GetBrowser().MainFrame.EvaluateScriptAsync(String.Format("window.location.href = '{0}'", lnk));
}

Async upload to Azure isn't blocking even though calling Task.WaitAll(task)

I am uploading a file to Azure. The code uploads the file fine, but my page refreshes before it's finalized and shows the old image. I can refresh the page manually and it shows the new image. Why isn't my method waiting for the task to finish?
public static bool Upload(Stream image, String id)
{
try {
var key = String.Format("{0}.png", id);
image.Position = 0;
var container = new CloudBlobContainer(new Uri(string.Format("{0}/{1}", Host, Container)), Credentials);
var blob = container.GetBlockBlobReference(key);
blob.Properties.ContentType = "image/png";
Task task = Task.Run(() => { blob.UploadFromStreamAsync(image); });
Task.WaitAll(task);
}
catch {
return false;
}
return true;
}
ANSWER: So thanks to aleksey.berezan. The answer turned out to be not even using the task.
So this:
Task task = Task.Run(() => { blob.UploadFromStreamAsync(image); });
Task.WaitAll(task);
Became this:
Task.WaitAll(blob.UploadFromStreamAsync(image));
And everything worked perfectly!
This guy:
blob.UploadFromStreamAsync(image);
starts new task.
Hence this guy:
Task.Run(() => { blob.UploadFromStreamAsync(image); });
just starts task which starts task. So that this code:
Task task = Task.Run(() => { blob.UploadFromStreamAsync(image); });
Task.WaitAll(task);
will just wait until upload-task gets fired(which happens kinda immediately) but not for the completion of upload-task.
To fix the situation you'll have to write:
Task.WaitAll(blob.UploadFromStreamAsync(image));
You're waiting for the task started with Task.Run. You want to wait for UploadFromStreamAsync. In fact I don't see why you need Task.Run here. It only makes things slower. You transfer work to the thread-pool, then wait for it to complete.
Just call the synchronous version of UploadFromStreamAsync if there is one. Or, call Wait on the task that UploadFromStreamAsync returns (less preferable).
You might want to revise your exception handling. You'll never find out about bugs in this method because all exceptions are thrown away.

Categories