I'm using c#/webforms for a form. The form also relies on UpdatePanels.
On the final step the event launches a long running (90 secs) HttpPost to a third party site.
At present when the submit button is pressed, I present a modal overlay while the process takes place, then redirect the user to another page on completion.
This is okay, but it doesn't refresh the update panel during the process, so the UI doesn't reflect the true picture - it's a step behind.
I've only just moved from Asp.Net 4.0 so not had the opportunity to investigate async await stuff until now.
I'm testing a new process but can't get it to work the way I want.
Here's the event that's called on the final step:
protected async void Submit_Click(object sender, EventArgs e)
{
if (Page.IsValid)
{
Task<bool> post = PostApplication();
var step = Int32.Parse(((LinkButton)sender).CommandArgument);
SetStepState(step, StepState.Complete);
upStepper.Update();
var result = await PostApplication();
Response.Redirect("/Result.aspx");
}
}
public async Task<bool> PostApplication()
{
await Task.Delay(3000);
return true;
}
The task does its job properly and a result is returned in 3 seconds, followed by the redirect.
What is not happening is the UI isn't being updated during the wait. The method SetStepState() updates a data-attribute on the current step, which in turn should change how the step displays, but it doesn't. I've also forced an update to the update panel immediately after changes but nothing changes.
I'm not sure whether what I'm attempting is possible so would appreciate any advice.
Related
I am using C#,.Net4.0 and Visual Studio 2010 and I am trying to get this behaviour from my windows form application when the user click on a button:
A GUI LED starts to blink
A long rung operation starts
When the operation at point 2 ends the LED stops blinking
private void Btn_WebService_Click(object sender, EventArgs e)
{
Thread_Blink = new Thread(() => { LED_Blink(LED.WS); });
Thread_Blink.Start();
// Do something that takes time.. Let's imulate with a sleep
Thread.Sleep(2000);
Thread_Blink.Abort();
}
I also tried using 3 different events and/or timers..
private void Btn_WebService_MouseDown(object sender, MouseEventArgs e)
{
Timer_Blink.Enabled = true;
}
private void Btn_WebService_Click(object sender, EventArgs e)
{
// Do something that takes time.. Let's imulate with a sleep
Thread.Sleep(2000);
}
private void Btn_WebService_MouseUp(object sender, MouseEventArgs e)
{
Timer_Blink.Enabled = false;
}
The result is always the same: The LED starts to blink only AT THE END of the long running operation (Thread.Sleep(2000);) and suddenly STOPS so that you can't see anything. Why does this happen and how can I get the desired behaviour?
I add further infos. I tried to use BackgroundWorked and implemented the wanted behavour in this way:
private void Btn_WebService_Click(object sender, EventArgs e)
{
BlinkWorker.RunWorkerAsync(LED.WS);
LedOn(LED.WS);
TestWebService(); // This method takes about 2 seconds to answer..
LedOff(LED.WS);
BlinkWorker.CancelAsync();
}
While the TestWebService() is running I get the LED off(LedOn(LED.WS);). When the TestWebService()has finished the LED comes on.
The BlinkWorker still does not work if started and cancelled inside the Click event.
FYI: If I make a button that starts the blinking and another button that stops it, it works perfectly.
The problem is, you're tying up the UI thread inside that Thread.Sleep. The UI thread is special - it's the only one that can make changes to the UI - so whilst it's busy sleeping, it's not able to service any attempts from your BackgroundWorker or LED_Blink timer callback to change the UI state.
So, you need to not tie up the UI thread. You need to put the Thread.Sleep code (or it's actual real equivalent) into the BackgroundWorkers DoWork handler, or use other means to avoid blocking the UI thread.
Common approaches today would be to make use of async/await if the real work you're trying to do already offers an asynchronous alternative (e.g. await Task.Delay(2000); would be the equivalent of your current Thread.Sleep(2000);). Unfortunately, using async and await would require you to move to a later version of .NET/Visual Studio - but you ought to be considering that anyway. 2010 is quite dated (and also, IMO, probably the worst one to stop on - it was notoriously slow), and .NET 4.0 (as opposed to .NET 4.5.2 or later) is no longer supported.
I would propose that you take a look at the BackgroundWorker class and the ReportProgress method.
I am using Modern UI for WPF template in a .NET 4.0 app where one page needs to execute an async command before it navigates back to another page. At the same time, UI thread must be unlocked while command is running. If I do this:
public void OnNavigatingFrom(FirstFloor.ModernUI.Windows.Navigation.NavigatingCancelEventArgs e)
{
TaskEx.Run(() =>
{
//Replace Sleep call by the async command execution
System.Threading.Thread.Sleep(5000);
}).Wait();}
The OnNavigatingFrom waits before navigating back, but the UI is blocked during this time.
Any ideas on how to execute the async code in a different context and make OnNavigatingFrom runs "synchronously"?
EDIT: There is a similar thread with a workaround and no conclusive answer.
one page needs to execute an async command before it navigates back to another page
This is the problem. User interfaces are not asynchronous, period. You can't just stick an asynchronous operation into the mix while the UI is doing a transition.
Think about it this way: as a user, if you decide to navigate back, and there's a network hiccup or something and the program freezes for a few seconds while navigating back, that's a bad user experience, right? The UI simply can't wait for asynchronous work during an update.
What you can do is do the work before the user is allowed to navigate back. You can, for example, capture the user's intent to navigate back and show some kind of "please wait" UI while the operation is going, and then after the operation completes do the actual navigation.
Here is what you need to do:
public void OnNavigatingFrom(NavigatingCancelEventArgs e)
{
// Fire and forget.
Task.Run(() => MyCommandAsync());
}
The above is a fire and forget approach, instead it is preferred to use async and await:
public async void OnNavigatingFrom(NavigatingCancelEventArgs e)
{
await MyCommandAsync();
}
Just await the Task instead of using .Wait().
See Using async-await on .net 4 for how to await in .NET 4.0
Use OnNavigatingFrom to do your stuff. e.Cancel prevent going back. Use Frame.GoBack() after your operation has finished.
protected override async void OnNavigatingFrom(NavigatingCancelEventArgs e)
{
if (Current.HasModifications())
{
e.Cancel = true;
await Current.Save().ContinueWith((t) =>
{
//Go Back after the Operation has finished
Frame.GoBack();
}, TaskScheduler.FromCurrentSynchronizationContext());
}
else
{
base.OnNavigatingFrom(e);
}
}
I have a live WCF service hosted in US. I want to access it from a Windows Forms application that can be used from anywhere in the whole world. Currently I am testing it in India and trying to access my US hosted web-service. This application can be used from any connection speed and at any distance from the server. So speed of response from the server may vary. What I decided is that when user performs some function, my application will first update the UI and then it will perform the server update task in background. This will prevent the application from freezing or hanging. Following is one of these scenarios. But the problem is that even I am using Asynchronous functions and threads to update UI and server, the application is still freezing.
Actually I have a button that acts as a toggle between like and unlike. When a user clicks it, it should change to unlike and then it runs a thread that updates the server in background. Following is the code for the Button's click event:
async private void btnLike_Click(object sender, EventArgs e)
{
new System.Threading.Thread(new System.Threading.ThreadStart(changeLike)).Start();
await LikeContent();
}
changeLike function:
private void changeLike()
{
if(btnLike.Text.Equals("Like"))
btnLike.Text="Unlike";
else
btnLike.Text="Like";
}
LikeContent function:
async Task<int> LikeContent()
{
await Global.getServiceClient().addContentLikeAsync(cid, uid);
System.Threading.Thread t = new System.Threading.Thread(new System.Threading.ThreadStart(reloadLikes));
t.Start();
return 0;
}
addContentLikeAsync() function is a WCF web-service function that updates user likes on server.
reloadLikes() updates the number of likes from the server after user liked the content.
Please tell me how can I modify my code so that application instantly updates the LIKE button instead of freezing for some time and then updating the LIKE button? Because this "sometime" will create a bad impression on users having less internet speed.
First, I assume the btnLike_Click method is called on the UI thread. In this case, do not call changeLike() on a separate thread.
async private void btnLike_Click(object sender, EventArgs e)
{
changeLike();
await LikeContent();
}
Second, be sure to marshal back to the UI thread before reloadLikes()
async Task<int> LikeContent()
{
await Global.getServiceClient().addContentLikeAsync(cid, uid);
// Ensures this occurs on the UI thread - WinForms
Invoke((Action)reloadLikes);
return 0;
}
So here is my problem with my Windows Phone application.
I have Page 1 that can navigate to Page 2. In Page 2 OnNavigateTo I make a async call.
This seems ok the first time I run the application, the async call creates a new worker thread and does work for me which is cool. But I realize that if I go back to Page 1 and re-invoke Page 2 the problem appears: Now I have a new worker thread from the async call while the old one was not terminated. So there is a race between the two worker threads and cause a problem to my application. I don't have direct control to the threads since they are implicitly created by async methods.
So in this case, anyone has suggestion on how to deal with it or is there a common pattern of dealing with this issue?
It depends on how you're issuing the async request. If you're using say, WebClient to do something like DownloadStringAsync you'll see that your WebClient instance has a method CancelAsync that will set the cancelled property in your Completed event handler to true. Just call CancelAsync when you leave your page and test for this in your handler and you should be good.
// in some button handler or whereever
webClient.DownloadStringAsync("http://url.com")
void OnLeavePage(object sender, EventArgs e) {
webClient.CancelAsync();
}
void OnCompleted(object sender, DownloadStringCompletedEventArgs e) {
if (e.Cancelled) {
return;
}
// do your handling
}
If you don't have CancelAsync you can pass in a UserState object that has a Cancelled property to emulate the behaviour (set it to true when you leave and test in your handler).
Here is how I am trying to do:
void Fetch()
{
//Code To Fetch Data
}
protected void btn_Click(object sender, EventArgs e)
{
lblSatus.Text = "Fetching Data...";
Thread t = new Thread(Fetch);
t.Start();
t.Join();
lblSatus.Text = "Got Data";
}
But on click of button it directly display GotData.
Any idea what I am doing wrong here.
Thanks
After the t.Start(); you need something that does a check ...along the lines of:
while (!t.Completed) // pseudo
{
// wait
Thread.Sleep(10);
}
Otherwise, the thread spins up and begins execution while the Main thread simply falls through.
I think you're misunderstanding the relationship between threads on the server-side when processing an ASP.NET request, and the experience on the browser-side. Specifically, for a given HttpRequest (in your example, the Request is a postback, which triggers the btn_Click event), there will be one and only one Response. This is true, regardless of what thread-manipulation you're doing on the server-side.
In your example, it looks like you're expecting the server to respond to the browser, with lblSatus's value as "Fetching data...". Then a little later when the operation has completed, the server would "respond" to the browser again, with lblSatus's value as "Got Data". This will not work, due to the "handshake" nature of Http communication: one request will result in one response (and thus, only one value for lblSatus).
The thing you're trying to do (give the end user a logical "progress bar" during a long-running operation) is a common requirement. Unfortunately, I know of no easy built-in way to do this in ASP.NET. I suspect most people do it via a polling model: the browser initiates the action, the server kicks it off in some sort of asynchronous fashion that does NOT delay the response being sent back to the browser. Then the browser periodically (via javascript, most likely) reaches back to the server to get an update on the operation's progress.