Reputation: 1707
I display a ProgressBar on the UI thread and then kickoff a logon process on another thread. This works flawlessly, I get the ProgressBar "circle" which lets the user know something is going on. The problem is I need to return any logon errors, like invalid username/password to the user via the UI. I changed my ProgressBar class to contain an ErrorMessage
property, instantiate it and pass that as a parameter to the logon process. On any errors I set the ErrorMessage
property. If it's not null, I display the error. The problem is the logon thread doesn't finish before I get to the if
that checks for null. This is all done on a button click event. My code:
MyButton_Submit.Click += async (sender, e) =>
{
ManualResetEvent resetEvent = new ManualResetEvent(false);
ProgressBarHandler myprogressbar = new ProgressBarHandler(this);
myprogressbar.show();
var thread = new System.Threading.Thread(new ThreadStart(async delegate
{
await SignOn(myprogressbar);
resetEvent.Set();
}));
thread.Start();
// based on @Giorgi Chkhikvadze comment below I changed: resetEvent.WaitOne(); to:
await Task.Run(() => resetEvent.WaitOne());
// works perfectly
while (thread.ThreadState == ThreadState.Running)
{
await Task.Delay(100);
}
myprogressbar.hide();
if (myprogressbar.ErrorMesage != null)
{
Context context = Application.Context;
string text = myprogressbar.ErrorMesage ;
ToastLength duration = ToastLength.Short;
var toast = Toast.MakeText(context, text, duration);
toast.Show();
}
};
}
The line resetEvent.WaitOne();
is obviously blocking the UI because when I comment it out, the progressbar is displayed, when I execute it, it does not. How can I fix this?
* EDIT - Added SignOn Code *
private async Task SignOn(ProgressBarHandler MyProgress)
{
Boolean error = false;
// Hide the keyboard ...
InputMethodManager imm = (InputMethodManager)GetSystemService(Context.InputMethodService);
imm.HideSoftInputFromWindow(aTextboxPassword.WindowToken, 0);
// Check permissions
var mypermission = ApplicationContext.CheckCallingOrSelfPermission(Android.Manifest.Permission.Internet);
if (ApplicationContext.CheckCallingOrSelfPermission(Android.Manifest.Permission.Internet) != Android.Content.PM.Permission.Granted)
{
int MY_REQUEST_CODE = 0;
//int x = 0;
RequestPermissions(new String[] { Android.Manifest.Permission.Internet },
MY_REQUEST_CODE);
//x = 1;
}
mypermission = ApplicationContext.CheckCallingOrSelfPermission(Android.Manifest.Permission.Internet);
if (ApplicationContext.CheckCallingOrSelfPermission(Android.Manifest.Permission.AccessNetworkState) != Android.Content.PM.Permission.Granted)
{
int MY_REQUEST_CODE = 0;
RequestPermissions(new String[] { Android.Manifest.Permission.AccessNetworkState },
MY_REQUEST_CODE);
}
MyUser.Username = aTextboxUsername.Text.Trim();
MyUser.Password = aTextboxPassword.Text.Trim();
try
{
ConnectivityManager connectivityManager = (ConnectivityManager)GetSystemService(ConnectivityService);
NetworkInfo activeConnection = connectivityManager.ActiveNetworkInfo;
bool isOnline = (activeConnection != null) && activeConnection.IsConnected;
if (isOnline)
{
ServicePointManager.SecurityProtocol = SecurityProtocolType.Tls12;
OMLDataInterfaceWeb.OMLDataInterface myService = new OMLDataInterfaceWeb.DataInterface();
try
{
result = myService.Logon(MyUser.Username, MyUser.Password);
}
catch (Exception ex)
{
MyProgress.ErrorMesage = "Logon attempt failed due to error: " + ex.Message;
}
}
else
{
MyProgress.ErrorMesage = "There is no internet connection or cell phone connection. Connect to a network or connect to a cellular network.";
};
}
catch (Exception ex)
{
MyProgress.ErrorMesage = "Connectivity Manager failed to create a connection due to error: " + ex.Message;
};
if (result == "CONNECTED")
{
PopulateMyUser();
StoreUsernamePassword();
var usertype = MyUser.Usertype.ToUpper();
if (usertype == "ADMIN")
{
Intent intent = new Intent(this, typeof(Drawer));
Bundle bundlee = new Bundle();
bundlee.PutParcelable("MyUser", MyUser); // Persist user class to next activity
intent.PutExtra("TheBundle", bundlee);
StartActivity(intent);
}
}
else
{
try
{
error = true;
errormsg = "Logon Error: Invalid Username or Password.";
}
catch (Exception ex)
{
MyProgress.ErrorMesage = "An error occured while attempting to set the error message, the error is: " + ex.Message;
}
};
try
{
if (error)
{
MyProgress.ErrorMesage = "An error occured during the logon process (2), The error is: " + errormsg;
}
}
catch (Exception ex)
{
MyProgress.ErrorMesage = "An error occured during the logon process (2), The error is: " + ex.Message;
}
}
* NOTE: * After posting this code I see I need to do some more work with permissions, the permissions dialog is not likely to appear to the user on this thread so I'll need to move it out of this process.
Upvotes: 0
Views: 1332
Reputation: 694
await Task.Run(() => resetEvent.WaitOne());
should do the trick. ManualResetEvent
is not awaitable and can't use await keyword directly on it, so you need to wrap it in task
Upvotes: 0
Reputation: 457382
You don't need a separate thread; await
will work just fine on its own. You also don't need ManualResetEvent
as a "signal" that the work is done; using await
on a Task
works fine for this.
Equivalent, simplified code:
MyButton_Submit.Click += async (sender, e) =>
{
ProgressBarHandler myprogressbar = new ProgressBarHandler(this);
myprogressbar.show();
await SignOn(myprogressbar);
myprogressbar.hide();
if (myprogressbar.ErrorMesage != null)
{
...
}
};
You should never the Thread
type in a Xamarin app. There is always a simpler, better way. The Thread
type is a "strong yellow" flag in general, but a red flag in Xamarin.
Upvotes: 1