disable back button xamarin.forms - xamarin.forms

I am sorry, if this question is asked before, but I am unable to find my answer that is why I am asking again this question.
My Scenario is I have placed a back button on my axml views from which I am performing Navigation of Going back on previous views using GoBack() Method.
what I need I want to disable back button on my hardware so that my app should not go back to previous screens which are available in the navigation stack. I am using Prism MVVM for my app, so is there any possibility to disable this button or have some overrideable action method on my ViewModel from which I should stop it.
Hope you could understand my question.
B.R

I found the best way to do this was to use the provided override of OnBackPressed in the android MainActivity and then access a bool I saved somewhere. I use the settings plugin personally but the example below uses the built in application properties (it should work but I haven't tested it).
public class MainActivity : FormsApplicationActivity
{
protected override void OnCreate(Bundle bundle)
{
base.OnCreate(bundle);
Forms.Init(this, bundle);
LoadApplication(new App());
}
public override void OnBackPressed()
{
var disable = (bool) App.Current.Properties["isBackButtonDisabled"];
if (disable) return;
base.OnBackPressed();
}
}

Related

Proper Page.Loaded event in Xamarin.Forms

For the past 2 month I have been searching tirelessly for a way to implement a proper Page.Loaded event when using Xamarin.Forms but I couldn't implement or find a way to do it.
Most people suggest overriding Page.OnAppearing or adding an event handler for Page.Appearing both of which are not the answers or the proper way to achieve the desired effect and don't event behave as a real Page.Loaded event would.
I would like to know the following:
Why doesn't Xamarin.Forms have a built-in Page.Loaded event?
Is there's a work around?
Can I implement it from the native side?
Edit:
What I mean by "proper Page.Loaded" event is:
It must be called ONCE AND ONLY ONCE the page has loaded all of it's controls, laid them out, initialized them and rendered them for the first time.
It must NOT be called when returning from modal pages.
1.Why not load the data/controls in the constructor of the ContentPage? The constructor method is call only once and it is also called before Page.OnAppearing.
Can I implement it from the native side?
Yes, I think you can.
In iOS, override the ViewDidLoad method in custom renderer:
[assembly:ExportRenderer (typeof(ContentPage), typeof(MyPageRenderer))]
namespace App487.iOS
{
public class MyPageRenderer : PageRenderer
{
protected override void OnElementChanged(VisualElementChangedEventArgs e)
{
base.OnElementChanged(e);
}
public override void ViewDidLoad()
{
base.ViewDidLoad();
//call before ViewWillAppear and only called once
}
public override void ViewWillAppear(bool animated)
{
base.ViewWillAppear(animated);
}
}
}
In Android, try to override the OnAttachedToWindow method:
[assembly: ExportRenderer(typeof(ContentPage), typeof(MyPageRenderer))]
namespace App487.Droid
{
public class MyPageRenderer : PageRenderer
{
public MyPageRenderer(Context context) : base(context)
{
}
protected override void OnElementChanged(ElementChangedEventArgs<Page> e)
{
base.OnElementChanged(e);
}
protected override void OnAttachedToWindow()
{
base.OnAttachedToWindow();
}
}
}
Currently Xamarin.Forms doesn't not provide a proper/complete life cycle events to fulfill all specific requirements, but things are improving, the Dev team is currently working on to address this issue, below mentioned issues and recent pull request on the official GitHub Repos (you may follow, get ideas and maybe implement it yourself before they even ship it), they will for sure provide that in the future, although it is not clear when it will be ready.
Specification: Enhancement Add better life cycle events #2210.
Issue: LifeCycle events for controls #556.
Pull request: Life cycle events for controls
GitHub Branch where currently working on.
MAUI repo (Next evolution of Xamarin) Cross-Platform LifeCycle.
Specification Add Loaded/Unloaded to VisualElement.

How to navigate to page when on resume is called in app.xaml.cs for mvvm in xamarin forms app

I have an issue with navigation. When I click home screen button of device and get back to app I get app homescreen instead of pin page. Ideally it should show pin page and its working fine with back button of device.
OnStart() method has navigationasync but the same is not working with OnResume() method.
Do I have to go to each of the Platform project cs file and add the navigation there like for Android OnRestart()/OnResume() method?
If anyone knows the solution please let me know
Most commonly when writing your Xamarin Application with Prism you will have something like:
protected override void OnInitialized()
{
NavigationService.NavigateAsync("SomePage");
}
OnInitialized is called each time the App's ctor is invoked. This is an important consideration here because this means that any time that the native platform tombstones the app in the background or otherwise refreshes the app by calling OnCreate in your MainActivity or FinishedLaunching in your AppDelegate, then OnInitialized will be invoked resetting your App's Navigation stack to SomePage.
You can however override the OnStart/OnResume in PrismApplication and use whatever business logic you need to determine where to navigate and how you might want to restore your application.
public override void OnStart()
{
NavigationService.NavigateAsync("MainPage");
}
public override void OnResume()
{
if(someCondition)
{
NavigationService.NavigateAsync("SomePage");
}
else
{
NavigationService.NavigateAsync("AnotherPage");
}
}

Background Service in Xamarin.Forms, iOS, Android

I have a background service in android.
My code is as follows:
[Service]
public class PeriodicService : Service
{
public override IBinder OnBind(Intent intent)
{
return null;
}
public override StartCommandResult OnStartCommand(Intent intent, StartCommandFlags flags, int startId)
{
Device.StartTimer(TimeSpan.FromSeconds(5), () =>
{
// code
});
return StartCommandResult.Sticky;
}
}
The MainActivity class:
protected override void OnCreate(Bundle bundle)
{
base.OnCreate(bundle);
global::Xamarin.Forms.Forms.Init(this, bundle);
LoadApplication(new App());
StartService(new Intent(this, typeof(PeriodicService)));
}
Permission AndroidManifest.xml:
<uses-permission android:name="android.permission.PeriodicService" />
The problem is that my service only works in the background when the app is active or in the foreground but when I close the app it doesn't run in the background.
A possible solution is to follow along with what Fabio has written in his post about how to create The Never Ending Background Task.
The idea behind this is to create a BroadcastReceiver and Android Service in the manifest. The BroadcastReceiver will then activate the service with foreground priority as the application is being closed. If you are dealing with post Android 7 then he created an update to his blog showing The Fix.
Just to get a better understanding of how basic Android services operate, I'd recommend taking a look at this Android Services Tutorial
I also saw this other StackOverflow post that seems to be pretty similar so maybe there will be some useful info over there too.
I'm not too acclimated in this stuff, but I was able to follow along with the blog posts pretty easily so I hope this ends up helping :). If anyone finds a better solution I'd love to hear about it so tag be (if you would be so kind) so I can stay updated!

How to link button event.click with xamarin.driod from xamarin.form

Am new to xamarin.form having a blockage...
This is what I want to do.
I have a button in welcome.xaml in xamarin.form and I want to perform a click event but I want a method from xamarin.driod to be implemented in the click.event.
Those this makes sense to anyone?
You need to write a custom control.
So you could handle the click event in the Android or IOS.
Read here about how to create a custom controls in xamarin.forms
You could also assign a static class that exist in the shared project from your driod project.
I typically prefer using IOC and Interfaces to implement platform specific logic in the Core project, but simple way to do it may just be to set a static Action on your pages code behind.
public partial class WelcomePage
{
public static Action DroidAction { get; set; }
private void Button_OnClicked(object sender, EventArgs e)
{
if (DroidAction == null)
{
Debug.WriteLine("Action was not set");
}
DroidAction?.Invoke();
}
Your Droids OnCreate could be an option of where to do this.
protected override void OnCreate(Bundle bundle)
{
base.OnCreate(bundle);
WelcomePage.DroidAction = new Action(() => Debug.WriteLine("I a platform specific action!"));
}

Duplicate context menu icons when launching the app from background

I am testing my app with "Don't Keep Activities" options from developer options. When I try to background the app and launch it , am getting multiple duplicate icons added to the actionbar.
I am adding the context menu icon from the fragment. I have a string passed in a bundle to the fragment. When I background and launch the app, Android tries to recreate the activity and as setHasOptionsMenu(true); is called multiple time during this process and adds duplicate icons to the actionbar.
The following fixes this issue , but would like to know if this is the best approach
if(savedInstanceState == null) {
setHasOptionsMenu(true);
}
Maybe i'm a little late to answer your question, but it can be useful for future users.
The problem origin:
When you background the app and and launch it, the system will save and restore the state of your fragment. Also onCreate() method will be called and a new fragment will be added to your Activity, and you end up with multiple fragments running in your activity, each one added an item to the menu.
The solution:
(+) The correct fix: simply add a null check in your onCreate() method before adding your fragment (to avoid duplication)
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_layout);
if (savedInstanceState == null) {
Fragment fragment = ...
getSupportFragmentManager()
.beginTransaction()
.add(R.id.fragment_container, fragment)
.commit();
}
}
(+) Or a workaround1: Clear your menu inside onCreateOptionsMenu() method of your fragment
#Override
public void onCreateOptionsMenu(Menu menu, MenuInflater inflater) {
menu.clear();
inflater.inflate(R.menu.your_menu, menu);
super.onCreateOptionsMenu(menu, inflater);
}
(+) Or a workaround2: Call setHasOptionsMenu(true) only when savedInstanceState == null like you mentioned in OP.

Resources