Xamarin forms DisplayAlert not Awaiting - xamarin.forms

I have an event that fires on Android Hardware back button pressed. This is how its implemented in the AppShell class:
public event EventHandler<CancelEventArgs> BackButtonPressed;
protected override bool OnBackButtonPressed()
{
var cancelArgs = new CancelEventArgs();
BackButtonPressed?.Invoke(this, cancelArgs);
return (cancelArgs.Cancel) ? true : base.OnBackButtonPressed();
}
Elsewhere I have subscribed to this event in a view model. Here is the code:
private async void _AppShell_BackButtonPressed(object sender, CancelEventArgs e)
{
if (!await App.Current.MainPage.DisplayAlert(
"My App",
"Are you sure you want to cancel, you have unsaved changes",
"ok",
"cancel"))
{
e.Cancel = true;
}
}
Some other stuff happens before DisplayAlert, but for simplicity I have removed all that.
The issue is that when DisplayAlert is called, the execution returns to OnBackButtonPressed and because of this the cancel argument is not set accordingly. So it seems that DisplayAlert is not waiting for the user to respond. How can this problem be solved?
Apologies if I've been unclear, I can provide further clarification.. just ask:)
Any help on this would be much appreciated.

By design my solution is not feasible. The answer is to always cancel out of the OnBackButtonPressed() method and raise a MessagingCenter event like so:
protected override bool OnBackButtonPressed()
{
MessagingCenter.Send(this, "ANDROID_HARDWARE_BACK_BUTTON_TAPPED");
return true;
}
Then when you handle the event call:
await Shell.Current.Navigation.PopAsync(true);
instead of:
await Shell.Current.SendBackButtonPressed
From this I ascertain the best practice is to always use PopAsync instead of SendBackButtonPressed to save you from unwanted recursion.

What about using Display Alert by overriding the OnAppearing() method?
protected async override void OnAppearing() {
var result = await App.Current.MainPage.DisplayAlert(
"My App",
"Are you sure you want to cancel, you have unsaved changes",
"ok",
"cancel"
)
base.OnAppearing();
if (!result)
{
e.Cancel = true;
}
}

Related

Xamarin.Forms MvvM Prism Software and Hardware Back Button

I have problem with implementation Code which resolve problem with confirm software nad hardware button back. I need confirm and save state page field. When confirm is true and save state have no error I want close page and when confirm is false or save state have error I want stop closing page. I use xamarin.forms with mvvm prism.
If you mean want to custom the method of Software and Hardware Back Button of Android device, you could override OnOptionsItemSelected and OnKeyDown method in MainActivity.cs to achieve that.
Software Back Button code in MainActivity.cs:
protected override void OnCreate(Bundle savedInstanceState)
{
...
Android.Support.V7.Widget.Toolbar toolbar = FindViewById<Android.Support.V7.Widget.Toolbar>(Resource.Id.toolbar);
SetSupportActionBar(toolbar);
SupportActionBar.SetHomeButtonEnabled(true);
}
public override bool OnOptionsItemSelected(IMenuItem item)
{
if(item.ItemId == Android.Resource.Id.Home)
{
Console.WriteLine("software back button press");
return false;
}
else
{
return base.OnOptionsItemSelected(item);
}
}
Hardware Back Button code in MainActivity.cs:
public override bool OnKeyDown([GeneratedEnum] Keycode keyCode, KeyEvent e)
{
if(e.Action == KeyEventActions.Down && keyCode == Keycode.Back)
{
Console.WriteLine("hardware back button press");
}
return false;
}

Why ActivityIndicator changes state after entire method is completed?

I would like to show ActivityIndicator object after user tap the login button on page. Unfortunately there is small problem to do that because it seems like ActivityIndicator change state after entire method is completed. This is code I wrote so far:
private void Login(object sender, EventArgs ev)
{
BusyIndicator.IsVisible = true; //<- here I want to show indicator
try
{
//some input validation, connection opening etc
ConnectionHandler.OpenConnection(ServerIP, "dmg", false);
}
catch (Exception e)
{
Logging.Error(e.Message, "Connection", e);
}
}
When I set breakpoint after BusyIndicator.IsVisible = true; there is absolutely no change in app. However I noticed that when method is completed then indicator is shown. Is this a correct behavior of this control?
Why I need this? Because field validation and connecting with server takes some time and I need to show to user that something happens in background. Login function takes ~1 sec so indicator show and hide quickly I can't even see any change.
How can I show indicator immediately after user tap a button?
Your problem is that Login() method is being executed in the UI thread. So, despite setting BusyIndicator.IsVisible = true;, the thread continues tio execute the method to get data, so the UI does not respond.
Solution, run the OpenConnection in a different thread:
private async void Login(object sender, EventArgs ev)
{
BusyIndicator.IsVisible = true; //<- here I want to show indicator
try
{
//some input validation, connection opening etc
await Task.Run(() => { ConnectionHandler.OpenConnection(ServerIP, "dmg", false);});
}
catch (Exception e)
{
Logging.Error(e.Message, "Connection", e);
}
}

Save & Restore Page in Xamarin Forms

I'm looking to save the current navigation stack on the OnSleep Event in my Xamarin Forms page and restore it on the OnResume Event. Is it possible to do this?
Cheers!
I think you should not memorize all navigation stack. Your device decide to kill your app or to restart from the last page you have seen when it comes up from background. I think you can memorize if you are "Logged in" or not: if you are "Logged in" you can restart from the first page "after the login", otherwise start "from the login".
For this case you can take a look to this link and use Properties
public class App : Xamarin.Forms.Application
{
public App ()
{
}
protected override void OnStart()
{
// Handle when your app starts
Debug.WriteLine ("OnStart");
checkLogin();
}
protected override void OnSleep()
{
// Handle when your app sleeps
Debug.WriteLine ("OnSleep");
}
protected override void OnResume()
{
// Handle when your app resumes
Debug.WriteLine ("OnResume");
checkLogin();
}
}
void checkLogin(){
if (Application.Current.Properties.ContainsKey("IsLogged"))
{
var IsLogged = Application.Current.Properties ["IsLogged"] as bool;
// do something with IsLogged
if(IsLogged)
MainPage = new MyFirstPage();
else
MainPage = new MyLoginPage();
}
else
MainPage = new MyLoginPage();
}
then, when you have logged in
Application.Current.Properties ["IsLogged"] = true;

Vaadin button setEnabled(false) doesn't enable

Vaadin newbie: When a user presses a button, I like to disable it so he knows that he clicked it and there's some work going on in the background. When the (long) task is completed, I'd like to enable the button.
For this, I'm using 2 threads (background and work) but for some reason the button doesn't enabled at the end of the task.
In other words, once clicked it goes to enabled(false) and never coming back. Why? and how can I fix it?
button.addClickListener(new ClickListener()
{
public void buttonClick(ClickEvent event)
{
Thread background = new Thread(new Runnable(){
#Override
public void run()
{
Thread work = new Thread(new Runnable(){
#Override
public void run()
{
button.setEnabled(false);
try
{
Thread.sleep(2000); //long work here!
} catch (InterruptedException e)
{
e.printStackTrace();
}
button.setEnabled(true); //doesn't enable at the end of the long work!
}});
work.start();
try
{
work.join();
} catch (InterruptedException e)
{
e.printStackTrace();
}
}});
background.start();
}
});
Maybe the best approach would be to use Button.setDisableOnClick(true) for the button and do the processing directly in the event handler without a background thread. This will show the standard loading indicator to the user until processing is done.
Otherwise you need to enable server push (#Push) and remember to use UI.access() in the background thread before updating the UI. See https://vaadin.com/book/-/page/advanced.push.html

What's the best way to defer actions after a request in ASP.NET?

I'm writing an ASP.NET application. When a specific kind of request is being handled, I want to schedule a method to be called in a certain number of minutes after the request is handled. The postponed method does not need to communicate with the client that made the original request, it is just intended to do some "housekeeping" work. What is the best way to do this in an ASP.NET context? (It's ok to not fire the event if the application domain dies for some reason).
In Global.asax use this to check your incoming request:
protected void Application_BeginRequest(object sender, EventArgs e)
{
CheckRequest(HttpContext.Current.Request);
}
if your request is valid, register a cache entry:
private void CheckRequest(HttpRequest request)
{
if (request)
RegisterCacheEntry();
}
private void RegisterCacheEntry()
{
if (HttpRuntime.Cache[CacheItemKey] == null)
{
HttpRuntime.Cache.Add(CacheItemKey, "your key", null,
DateTime.Now.AddSeconds(60), //change to fire in whatever time frame you require
Cache.NoSlidingExpiration,
CacheItemPriority.NotRemovable,
new CacheItemRemovedCallback(CacheItemRemovedCallback));
}
}
then process your function in the callback:
private void CacheItemRemovedCallback(string key, object value, CacheItemRemovedReason reason)
{
// execute your function
}
You could start a timer (System.Timers.Timer) from one of the application event in global.asax.cs (e.g. in Application_BeginRequest) after checking that it is required for that request.
Then, in the handler of the timer's Elapsed event, make sure that you stop the timer.
E.g. put something like this into global.asax.cs:
System.Timers.Timer _timer = null;
void Application_BeginRequest(object sender, EventArgs e)
{
// check if cleanup must be initiated
bool mustInitCleanup = RequestRequiresCleanup();
if ((_timer == null) && mustInitCleanup)
{
_timer = new System.Timers.Timer(5000);
_timer.Elapsed += new System.Timers.ElapsedEventHandler(_timer_Elapsed);
_timer.Start();
}
}
void _timer_Elapsed(object sender, System.Timers.ElapsedEventArgs e)
{
_timer.Stop();
_timer = null;
// do cleanup task
}
Simply create a new thread to do the housekeeping work and at its beginning have it sleep for however long you want the server to wait before doing the action.
For example, somewhere in that specific request you want to call DoSomething:
aNewThread = new Thread(Foo);
aNewThread.Start();
public void Foo()
{
Thread.Sleep(5000);
DoSomething();
}

Resources