Update UI while running async REST call on windows phone 8 - c#

In short, I want to display a progress indicator, make a REST service call, and hide the indicator.
My progress indicator is bound to a property which when set to true or false, show or hides it. My indicator is a little spinning wheel and I obviously don't want my UI to hang when I make a the web service call, so this is done by calling an Async function.
I found some code earlier on which works great to achieve what I want to do, however there is a but!
Here is the simplified version code I'm using:
try
{
ThreadPool.QueueUserWorkItem(async data =>
{
var dispatcher = Deployment.Current.Dispatcher;
dispatcher.BeginInvoke(() =>
{
this.IsRunning = true;
});
configModel = LoginUser(loginId, password);
dispatcher.BeginInvoke(() =>
{
this.IsRunning = false;
});
});
}
catch (Exception ex)
{
IDialogService dialogService = this.GetService<IDialogService>();
dialogService.Show("Unhandled Exception", ex.Message);
dialogService = null;
}
The problem occurs when my web service fails for whatever reason i.e. blocked by firewall for example. Even though I have a try catch, it doesn't get caught and crashes my app.
Is there another way to achieve this without using the threadpool? What's the cleanest way to achieve this?
I really need to call the this.IsRunning as this looks after showing and hiding my progress indicator but this obviously needs to be running on the UI thread, while the rest call needs to be running on another thread.

You need to try/catch the actual service call, not the QueueUserWorkItem:
try
{
configModel = LoginUser(loginId, password);
}
catch (...)
{
}
dispatcher.BeginInvoke(() =>
{
this.IsRunning = false;
});
Otherwise, your IsRunning = false will never execute.
However
If you want the cleanest way, then you make LoginUser asynchronous so you can await it.
this.IsRunning = true;
try
{
configModel = await LoginUser(loginId, password);
}
catch (...)
{
// report error here
}
this.IsRunning = false;

Related

Blazor WASM UI updates inconsistent

I have a simple login function in my app. When login starts, a boolean is set to true to activate a loading animation, and upon completion it is set to false to hide it. The problem is the UI does not react to the second part, but does react to first.
This is the function:
private async void AttemptLogin()
{
//This updates
LoginInProgress = true;
try
{
var result = await _loginPageViewModel.Login(Username, Password, exception => { Console.WriteLine(exception.Message); }); //TODO add notifier
if (result)
{
Console.WriteLine("success");
_navigationManager.NavigateTo("./");
}
else
{
Console.WriteLine("Failure");
}
}
catch (Exception e)
{
Console.WriteLine(e);
throw;
}
finally
{
//This does not update
LoginInProgress = false;
}
Console.WriteLine("Login attempt finished");
}
I am using blazorise as a component framework, this is how the value is bound (+ an extra testing checkbox, checking and unchecking updates everything as expected):
<Button Clicked="AttemptLogin" #bind-Loading="LoginInProgress">Login</Button>
<Check TValue="bool" #bind-Checked="LoginInProgress">Test</Check>
For now, StateHasChanged at the LoginInProgress = false; seems to work, but I do not like that I have to put it at some places and not others. What am I doing wrong?
Blazor will perform an internal "StateHasChanged" re-render of current component upon the completion of any event handlers in the component.
So, under normal circumstances having caught the "Clicked" event and handled it in "AttemptLogin" method then Blazor engine would re-render after AttemptLogin has completed and take the variable "LoginInProgress = false" into account. No need to manually call "StateHasChanged.
However, as your method is Async, then the engine will wait for completion. Normally, you'd return a Task that the engine will hold onto and wait until completion of that Task before performing the re-rendering.
In your case, you're returning a void instead of a Task. The result is that as soon as your AttempLogin method calls it first await, then control is returned to the caller (the Blazor engine) and it will consider the method complete.
var result = await _loginPageViewModel
.Login(Username, Password, exception =>
{ Console.WriteLine(exception.Message); });
If you'd returned a Task, it would wait until completion of the method (a result is provided by the Task), but because you return a void, then engine expects no further response from the method and starts re-rendering immediately. This re-rendering will happen before this line is executed:
LoginInProgress = false;
Hence, the re-rendering will be applied whilst LoginInProgress is still true.
Change your return type to Task and the re-rendering will be delayed until the event handler is complete.

client.GetStreamAsync(url) is freezing my UI

So I'm listening to an Server side event with my code to just write it on the console (for now) but it seems that this is making my window's form UI freeze
The code in question (which I'm calling from the main form's function)
static async Task hello()
{
HttpClient client = new HttpClient();
//client.Timeout = TimeSpan.FromSeconds(5);
while (true)
{
try
{
Console.WriteLine("Establishing connection");
using (var streamReader = new StreamReader(await client.GetStreamAsync(url)))
{
while (!streamReader.EndOfStream)
{
var message = await streamReader.ReadLineAsync();
Console.WriteLine(message.ToString());
}
}
}
catch (Exception ex)
{
//Here you can check for
//specific types of errors before continuing
//Since this is a simple example, i'm always going to retry
Console.WriteLine($"Error: {ex.Message}");
Console.WriteLine("Retrying in 5 seconds");
await Task.Delay(TimeSpan.FromSeconds(5));
}
}
}
Thanks in advance
I've solved the problem, it appears that async/await task freezes the GUI. To stop this from happening you need to use Task.Run(() => your_function()); when you call an async function
This question might be a possible duplicate of: GUI freezes when using async/await ... so go there if you want to find a bit more knowledge about the subject

Is this the correct way of using Tasking & Dispatcher in WPF?

First of all, I want to mention that I am kinda new to WPF, I just start learning it a few months ago and I am using code behind atm, going to MVVM later...
So, Im trying to create a simple program for my discord bot but I want to keep it responsive whenever a database action is going on, meaning my program cannot block while i am -for example- logging into my program.
I have a way to archive this but it feels like i am doing something wrong or using Tasking wrong.
I use await Task.Run(() => { code here }) but then inside this task I also need to use the Dispatcher every time I want to update a control and there is where I wonder if i do this all the correct way, is it normal to use the dispatcher inside a Task.Run every time you need to update controls or am I overusing this / doing it wrong?
Currently it works as intented, but I am worried I am overdoing something or going into the wrong direction because for me it seems silly to use the dispatcher every time for updating a control but maybe this is just the way it has to be, i am still new to WPF, this is also my first program i ever did in WPF
Sorry if my english is not so good, i try my best to explain this
I have a sort of solution but the use of Dispatcher inside Tasking seems to me not the correct way, this is the first time im doing this in WPF, I googled around a lot but see people using background workers or threading instead but this still seems to block my UI
// This button I use to -for this example- login in my program
// I load a ContentPresenter over my program showing its 'loading'
// Then I try login
private async void BtnLogin_Click(object sender, RoutedEventArgs e)
{
cpLoader.Visibility = Visibility.Visible;
await TryLoginAsync();
}
// Then here i do the login with tasking
// But here i also need to use dispatcher when updating controls
// So I wonder if this is done correctly
private async Task TryLoginAsync()
{
var username = txtUsername.Text.Trim();
var password = txtPassword.Password.Trim();
if (!string.IsNullOrWhiteSpace(username) && !string.IsNullOrWhiteSpace(password))
{
await Task.Run(() =>
{
try
{
var programAdmin = _dbAdmins.FindBy(x => x.Username.ToLower() == username.ToLower());
if (programAdmin != null)
{
if (string.Equals(EncryptionManager.Decrypt(BotDBConfiguration.EncryptionKey, programAdmin.Password), password))
{
programAdmin.DateLastLogin = DateTime.Now;
programAdmin.TotalLogins += 1;
_dbAdmins.Update(programAdmin);
Dispatcher.BeginInvoke(new Action(() =>
{
var firstTimeLoginPanel = new UpdatePasswordWindow(programAdmin);
firstTimeLoginPanel.Show();
Close();
}));
}
else
{
Dispatcher.BeginInvoke(new Action(() =>
{
cpLoader.Visibility = Visibility.Collapsed;
AlertManager.ShowAlertMessage("Password error", "The given password is incorrect, please try again...");
}));
}
}
else
{
Dispatcher.BeginInvoke(new Action(() =>
{
cpLoader.Visibility = Visibility.Collapsed;
AlertManager.ShowAlertMessage("Admin not found", "The given username cannot be found in the admin database, please try again...");
}));
}
}
catch (Exception)
{
Dispatcher.BeginInvoke(new Action(() =>
{
cpLoader.Visibility = Visibility.Collapsed;
AlertManager.ShowAlertMessage("Connection error", "Cannot connect to the database, please try again later...");
}));
}
});
}
else
{
cpLoader.Visibility = Visibility.Collapsed;
AlertManager.ShowAlertMessage("Missing information", "Please fill in all the required information...");
}
}
I think you have the right idea, but I agree that your implementation could use a bit of work.
In your sample above, it seems like you're doing one "background" activity (validating the username and password) and then either continuing with the program if it succeeds, or displaying one of several error messages. To make things cleaner, reduce code duplication and minimize the number of places where you jump between threads, I would just use a string variable to store the error message you want to show, and then at the end, tell the main thread the result of the background activity and have it either continue or display the error:
private async Task TryLoginAsync()
{
var username = txtUsername.Text.Trim();
var password = txtPassword.Password.Trim();
string errorTitle;
string errorDetail;
/*Your Type*/ object programAdmin;
bool result;
if (!string.IsNullOrWhiteSpace(username) && !string.IsNullOrWhiteSpace(password))
{
result = await Task.Run(new Func<bool>(() =>
{
try
{
programAdmin = _dbAdmins.FindBy(x => x.Username.ToLower() == username.ToLower());
if (programAdmin != null)
{
if (string.Equals(EncryptionManager.Decrypt(BotDBConfiguration.EncryptionKey, programAdmin.Password), password))
{
programAdmin.DateLastLogin = DateTime.Now;
programAdmin.TotalLogins += 1;
_dbAdmins.Update(programAdmin);
return true;
}
else
{
errorTitle = "Password error";
errorDetail = "The given password is incorrect, please try again...";
}
}
else
{
errorTitle = "Admin not found";
errorDetail = "The given username cannot be found in the admin database, please try again...";
}
}
catch (Exception)
{
errorTitle = "Connection error";
errorDetail = "Cannot connect to the database, please try again later...";
}
return false;
}));
}
else
{
errorTitle = "Missing information";
errorDetail = "Please fill in all the required information...";
}
if (result)
{
var firstTimeLoginPanel = new UpdatePasswordWindow(programAdmin);
firstTimeLoginPanel.Show();
Close();
}
else
{
cpLoader.Visibility = Visibility.Collapsed;
AlertManager.ShowAlertMessage(errorTitle, errorDetail);
}
}
Notice I changed the the internal Task to a run a Func<bool> instead of an Action. This way it can return a value bool to indicate the result. Because you are using a lambda function, you can also access variables like errorTitle from both threads, which I am using to pass the details of any error back to the main thread. Instead of sharing variables between threads, you could also change the return type of Task to be a more complex class or a Tuple.
These changes don't really alter the functionality of the code, but in my opinion they make it more readable/maintainable by separating out the main-thread code from the background-thread code.
There is one simple rule in WPF when it comes to UI controls:
All changes must happen on the UI thread.
By running a background worker, thread, or Task you are getting off the UI thread (which is the whole point, so you can avoid blocking it and making your program unresponsive). So if you need to run UI logic inside a task then marshalling back to the UI thread is necessary. This is what the Dispatcher does.
That said, you almost never have to do this in practice (at least when using Task) because of how async/await work. When you use await it automatically returns control to the calling thread after the Task completes, so if you structured your code as:
// Do some UI stuff
await dbTask;
// Do more UI stuff
instead of one giant task, you would find that your need for Dispatcher goes down dramatically.
As noted in the comments, this post provides a lot of super useful general information on dealing with async/await and tasks: How to run and interact with an async Task from a WPF gui

Ui not updating while connecting to wifi?

I am trying to connect to wifi with the following code:
private static bool ConnectToWifi(string profileName, WlanClient.WlanInterface wlanIface, Wifi wifi, string profile)
{
try
{
wlanIface.SetProfile(Wlan.WlanProfileFlags.AllUser, profile, true);
}
catch (Exception e)
{
var ex = e;
return false;
};
// Task.Run()
wlanIface.Connect(Wlan.WlanConnectionMode.Profile, Wlan.Dot11BssType.Infrastructure, profileName);
Thread.Sleep(5000);
var status = wifi.ConnectionStatus;
var x = wlanIface.GetProfileXml(profileName);
if (status == WifiStatus.Disconnected)
{
return false;
}
return true;
}
I have kept a delay of 5000 ms to ensure the network is connected, but this is causing my UI to not show the loading icon when this code executes.
How I can make my UI also update at same time, instead of waiting for the connection?
You have two options:
(Both of which make it not possible to return a bool that indicates a successful connection without a bit more logic around it.)
Move your code to a separate thread (if the rest of it is thread-safe) and use the synchronous methods instead:
private static void ConnectToWifi(string profileName, WlanClient.WlanInterface wlanIface, Wifi wifi, string profile)
{
new Thread(()=>{
bool result = false;
try
{
wlanIface.SetProfile(Wlan.WlanProfileFlags.AllUser, profile, true);
wlanIface.ConnectSynchronously(Wlan.WlanConnectionMode.Profile, Wlan.Dot11BssType.Infrastructure, profileName, 5000);
var status = wifi.ConnectionStatus;
var x = wlanIface.GetProfileXml(profileName);
result = (status != WifiStatus.Disconnected);
}
catch (Exception e)
{
var ex = e;
}
finally
{
Dispatcher.BeginInvoke(new Action(()=>{WhateverYouDoWithYourResult(result);}));
}
}).Start();
}
Or subscribe to the WlanConnectionNotification (Not being able to connect might not be seen as a change, so you have to test that):
private static bool ConnectToWifi(string profileName, WlanClient.WlanInterface wlanIface, Wifi wifi, string profile)
{
try
{
wlanIface.WlanConnectionNotification += Interface_ConnectionStateChanged;
wlanIface.SetProfile(Wlan.WlanProfileFlags.AllUser, profile, true);
wlanIface.Connect(Wlan.WlanConnectionMode.Profile, Wlan.Dot11BssType.Infrastructure, profileName);
return true; //Just means the attempt was successful, not the connecting itself
}
catch (Exception e)
{
var ex = e;
return false;
}
}
private static void Interface_ConnectionStateChanged(Wlan.WlanNotificationData notifyData, Wlan.WlanConnectionNotificationData connNotifyData)
{
// Do something with that info, be careful - might not be the same thread as before.
}
I don't have access to a Wifi right now, so I haven't tested above code. It should work, but you better consider it pseudo-code instead of an actual ready-to-use solution.
Whenever you execute Thread.Sleep in the UI thread, you interrupt processing all UI messages, which makes your UI unresponsive. Hence, Thread.Sleep and any other long running operations should never be executed in the UI thread.
The solution is to execute these operations in a separate thread to allow the UI thread to continue UI operations. It is generally a good idea to let the UI thread do only UI operations.
In your case it means that the caller should execute the operation in a task:
private static bool ConnectToWifi(string profileName, WlanClient.WlanInterface wlanIface,
Wifi wifi, string profile, Action<bool> resultCallback, Dispatcher dispatcher)
{
//Your connect code
bool result;
if (status == WifiStatus.Disconnected)
{
result = false;
}
else
{
result = true;
}
dispatcher.BeginInvoke(() => resultCallback(result));
return result;
}
Another thing: Thread.Sleep is not a good idea in task, since you don't know which scheduler you are running on. You should use Task.Delay instead. In this case Thread.Sleep is generally not a good idea, since you just wait and hope your task is done in five seconds, which is not guaranteed. Also you might simply waste 5 seconds of the user's time in case it connects immediately. The better solution is to use a wait loop and check regularly if the connection is established. If you expect the connection to happen in rather short time, you can use SpinWait.SpinUntil (with a timeout of five seconds):
SpinWait.SpinUntil(() => wifi.ConnectionStatus == WifiStatus.Connected, 5000);

How to access UI thread in C#

I am trying to access the UI thread in C# for windows phone 8. So far I got this. However, once it runs SmartDispatcher, it jumps to finally with receipt == null.
I got the SmartDispatcher class from this website(http://www.jeff.wilcox.name/2010/04/propertychangedbase-crossthread/). I was wondering if other people had this problem and how to solve it.
private async void purchaseProduct()
{
try{
li = await Store.CurrentApp.LoadListingInformationAsync();
SmartDispatcher.BeginInvoke(async delegate()
{
receipt = await Store.CurrentApp.RequestProductPurchaseAsync(package_id, true);
});
}
catch
{
DispatchCommandResult(new PluginResult(PluginResult.Status.ERROR));
}
finally
{
if(receipt != null)
{
parseXML(package_id);
prepData();
httpPostData();
Store.CurrentApp.ReportProductFulfillment(package_id);
}
}
}
Of course it will jump there immediately, as you are commencing an asynchronous operation, which does not block, but returns immediately.
Look, if the object contains a method Invoke instead of BeginInvoke. Invoke will block until the operation is complete.
See this thread as a reference.

Categories