Xamarin - Cannot use PopModalAsync - c#

I am trying to use PopModalAsync to remove the modal page. However, the Navigation.ModalStack.Count is 0. If I use PopModalAsync, it will throw an exception:
System.ArgumentOutOfRangeException: Index was out of range. Must be non-negative and less than the size of the collection.
Parameter name: index
I am using Xamarin.Forms. Here is some sample code:
App.cs (Potable)
public class App : Application
{
public App()
{
// The root page of your application
MainPage = new View.LoginPage();
}
}
LoginPage.xaml.cs (Potable)
public partial class LoginPage : ContentPage
{
public INavigation _Navigate;
public LoginPage()
{
InitializeComponent();
_Navigate = Navigation;
}
async void LoginBtnClicked(object sender, EventArgs args)
{
await _Navigate.PushModalAsync(new AuthenicationBrowser());
//await _Navigate.PopModalAsync(); it is work at here
Debug.WriteLine("Navigation.NavigationStack LoginBtnClicked ===> {0}", Navigation.NavigationStack.Count); //getting 0
Debug.WriteLine("Navigation.ModalStack LoginBtnClicked ===> {0}", Navigation.ModalStack.Count); // getting 1
}
public async void PopModal()
{
Debug.WriteLine(Navigation.NavigationStack.Count);
await Navigation.PopModalAsync();
}
}
AuthenicationBrowser.cs (Potable) * Edited: Put PopModalAsync *
public partial class AuthenicationBrowser : ContentPage
{
public AuthenicationBrowser()
{
InitializeComponent();
}
public async void PopModal()
{
Debug.WriteLine("Navigation.ModalStack AuthenicationBrowser .PopModal===> {0}", Navigation.ModalStack.Count); // getting 0
await Navigation.PopModalAsync();
}
}
BrowserView.cs (Potable)
public class BrowserView : WebView
{
public BrowserView()
{
}
}
AuthenicationBrowserRenderer.cs (Droid) * Edited: Calling PopModal *
[assembly: ExportRenderer(typeof(BrowserView), typeof(AuthenicationBrowserRenderer))]
namespace App.Droid
{
class AuthenicationBrowserRenderer : WebViewRenderer
{
... // Doing some Auth in OnElementChanged and using JavaScriptCallBack class after received json in Webview
}
public class JavaScriptCallBack: Java.Lang.Object, IValueCallback
{
public JavaScriptCallBack()
{
}
public async void OnReceiveValue(Java.Lang.Object result)
{
Java.Lang.String json = (Java.Lang.String)result;
string raw_json = json.ToString();
Debug.WriteLine("raw_json ====>>> {0}", raw_json);
var login_page = new LoginPage();
var auth_page = new AuthenicationBrowser();
Debug.WriteLine(login_page.Navigation.ModalStack.Count); // getting 0
Debug.WriteLine(auth_page.Navigation.ModalStack.Count); // getting 0
auth_page.PopModal(); // Trying to do PopModalAsync
}
}
}

Finally, I may get the answer that App.Current.MainPage.Navigation.PopModalAsync(); can do the trick. The reason is that the new LoginPage() is called as a new Content Page not existing page.
If I call it from the App.Current.MainPage (The existing LoginPage), it can get the existing modal from Modal Stack.
So the solution can be :
public partial class LoginPage : ContentPage
{
public LoginPage()
{
InitializeComponent();
}
async void LoginBtnClicked(object sender, EventArgs args)
{
await Navigation.PushModalAsync(new AuthenicationBrowser());
}
public async void PopModal()
{
Debug.WriteLine("Navigation.ModalStack PopModal ===> {0}", App.Current.MainPage.Navigation.ModalStack.Count);
await App.Current.MainPage.Navigation.PopModalAsync();
}
}

As it seems you had the wrong Navigation object. In my case I also had this wrong.
I displayed a modal page, which had it's own navigation bar (or Navigation object). So when I wanted to dismiss this modal page I got the mentioned exception, because there were no other pages on the navigation stack. It was the wrong object ...

For me I was calling PushModalAsync when I should have been calling PushAsync there was no items to push for PushModelAsync

Related

C# - How to reference my current instance of Form1 from another class in my winforms application?

Apologies if this doesn't make sense! Fairly new to WinForms and this is for a uni assessment. My main form is as below with the method I want to call in another class. I've renamed from Form1 to LoginPage.
public partial class LoginPage : Form
{
public LoginPage()
{
InitializeComponent();
Customer.LoadCustomerDB();
}
public string PinText
{
get { return PINNumTxt.Text; }
set { PINNumTxt.Text = value; }
}
}
My other class looks to verify the PinText which I've made accessible with the function above.
class BankCard
{
// Attributes
private Customer customer;
private int pinAttempts = 0;
private bool cardUnusable = false;
// Member functions
public void VerifyPIN()
{
LoginPage loginPage = new LoginPage();
foreach (Customer c in Customer.customers)
{
if (c.GetID().ToString() == loginPage.AccountNum())
{
customer = c;
}
}
if (cardUnusable == true)
{
MessageBox.Show("This card is currently blocked. Please contact your bank.");
Environment.Exit(0);
}
else if (loginPage.PinText == customer.GetPIN())
{
MessageBox.Show("Success!");
}
else if (pinAttempts < 2)
{
MessageBox.Show("Incorrect PIN attempt " + (pinAttempts + 1) + "/3");
loginPage.PinText = "";
pinAttempts += 1;
}
else
{
MessageBox.Show("3 failed PIN attempts. Please contact your bank.");
cardUnusable = true;
Environment.Exit(0);
}
}
}
My issue is that where I have the following:
LoginPage loginPage = new LoginPage();
This creates a new instance of the main page, doubles up the CustomerDB being loaded in and causes errors in my VerifyPin() function.
Is the issue that I need to somehow have LoginPage loginPage = current instance of LoginPage? And if so, how would I code that?
Thanks for any help
Wyck's comment got it to run without any errors. I had to do:
LoginPage loginPage = Application.OpenForms.OfType<LoginPage>().FirstOrDefault();
Thanks all
In most winforms projects, there is a Program class that has Application.Run(new Form1()); (or whatever your main form is called, LoginPage in your case). In the Program class you can create a static variable public static LoginPage loginPage;
then in the Main function:
static void Main()
{
loginPage = new LoginPage();
Application.Run(loginPage);
}
then when you want the reference to loginPage in any of your classes you can just use Program.loginPage

WPF application doesn't run (or appear) if api not available

I have a simple WPF application that doesn't appear if api is not available. How do I handle it so screen at least shows up saying no api is available?
public partial class MainWindow : Window
{
public MainWindow()
{
InitializeComponent();
GetWeather();
}
private void GetWeather()
{
using (var client = new WebClient())
{
var content = client.DownloadString($"http://localhost:50054/WeatherForecast");
var data = JsonConvert.DeserializeObject<IEnumerable<WeatherForecast>>(content);
message4Label.Text = data.ToList()[0].Summary;
}
}
}
You must move the call from the constructor of the form because if there's an exception it will close the form without any message. Also, protecting the function with a try/catch is always a good idea.
public partial class MainWindow : Window
{
public MainWindow()
{
InitializeComponent();
}
public void MainWindow_Loaded(object sender, RoutedEventArgs e)
{
if(!GetWeather())
//Present a message to the user
}
private bool GetWeather()
{
try
{
using (var client = new WebClient())
{
var content = client.DownloadString($"http://localhost:50054/WeatherForecast");
var data = JsonConvert.DeserializeObject<IEnumerable<WeatherForecast>>(content);
message4Label.Text = data.ToList()[0].Summary;
}
return true;
}
catch{ return false; }
}
}

Showing Google Admob Interstitial ad in Xamarin Forms without delay

I'm working on Xamarin forms project and i have implemented Interstitial ad on all three platforms (Google admob doesn't support UWP). Ads are working fine but they are showing with a delay of 4 to 5 seconds. All i want is to finish that delay so they can show immediately.
PCL class.
public interface IAdmobInterstitial
{
void Show(string adUnit);
}
Droid Code.
public class InterstitialAdListener : AdListener
{
readonly InterstitialAd _ad;
public InterstitialAdListener(InterstitialAd ad)
{
_ad = ad;
}
public override void OnAdLoaded()
{
base.OnAdLoaded();
if (_ad.IsLoaded)
_ad.Show();
}
}
public class AdmobInterstitial : Controls.IAdmobInterstitial
{
InterstitialAd _ad;
public void Show(string adUnit)
{
var context = Android.App.Application.Context;
_ad = new InterstitialAd(context);
_ad.AdUnitId = adUnit;
var intlistener = new InterstitialAdListener(_ad);
intlistener.OnAdLoaded();
_ad.AdListener = intlistener;
var requestbuilder = new AdRequest.Builder().AddTestDevice("302E90D530B2193F59FDD7F22A11B45A");
_ad.LoadAd(requestbuilder.Build());
}
}
iOS Code.
public class AdmobInterstitial : IAdmobInterstitial
{
Interstitial _adInterstitial;
public void Show(string adUnit)
{
_adInterstitial = new Interstitial(adUnit);
var request = Request.GetDefaultRequest();
_adInterstitial.AdReceived += (sender, args) =>
{
if (_adInterstitial.IsReady)
{
var window = UIApplication.SharedApplication.KeyWindow;
var vc = window.RootViewController;
while (vc.PresentedViewController != null)
{
vc = vc.PresentedViewController;
}
_adInterstitial.PresentFromRootViewController(vc);
}
};
_adInterstitial.LoadRequest(request);
}
}
Calling interstitial ad on PCL page.
DependencyService.Get<IAdmobInterstitial>().Show("(id will come here)");
Navigation.PushAsync(new Page());
Ad is showing perfectly but with a delay. I want to show the ad first and then the page.
So i just solved this.
Here is the code.
PCL Class. Add another function to your interface.
public interface IAdmobInterstitial
{
void Show(string adUnit);
void Give();
}
Droid code. Put _ad.Show() in the new function.
public class InterstitialAdListener : AdListener
{
readonly InterstitialAd _ad;
public InterstitialAdListener(InterstitialAd ad)
{
_ad = ad;
}
public override void OnAdLoaded()
{
base.OnAdLoaded();
//if (_ad.IsLoaded)
// _ad.Show();
}
}
public class AdmobInterstitial : Controls.IAdmobInterstitial
{
InterstitialAd _ad;
public void Show(string adUnit)
{
var context = Android.App.Application.Context;
_ad = new InterstitialAd(context);
_ad.AdUnitId = adUnit;
var intlistener = new InterstitialAdListener(_ad);
intlistener.OnAdLoaded();
_ad.AdListener = intlistener;
var requestbuilder = new AdRequest.Builder().AddTestDevice("302E90D530B2193F59FDD7F22A11B45A");
_ad.LoadAd(requestbuilder.Build());
}
public void Give()
{
if (_ad.IsLoaded)
_ad.Show();
}
}
Now, call the function Show() in the constructor of your MainPage in PCL and call the function Give() when you press the button for the next page.
public MainPage()
{
InitializeComponent();
DependencyService.Get<IAdmobInterstitial>().Show("ca-app-pub-3940256099942544/1033173712");
}
private void button_Clicked(object sender, EventArgs e)
{
DependencyService.Get<IAdmobInterstitial>().Give();
Navigation.PushAsync(new Percentage());
}
This is only for Android. Same goes for iOS as well.
I hope it helps the other programmers. :)

Routine call in other namespace

I wanted to use a DLL library code and link / reference it with my c# code. The reference seems to be working, when defining var instance = new ProjectM1() without any problems.
But in the following line appears an error that ProjectM1 does not contain a definition for ProjectM1 and it couldn't find a extension method (Maybe a using directive or assembly reference is missing)
namespace GanttC
{
public class ProjectM1 : ProjectM1<Task, object>
{
}
public class ProjectM1<T, R> : IProjectM1<T, R>
where T : Task
where R : class
{
public ProjectM1() (routine to call !)
{
Now = 0;
Start = DateTime.Now;
TimeScale = GanttC.TimeScale.Day;
}
}
}
Here now the main code to call routine ProjectM1 in public class ProjectM1:
using GanttC
namespace WindowsApp2
{
public partial class Form4 : Form
{
Form2 fh;
public Form4(Form2 caller)
{
fh = caller;
InitializeComponent();
}
private void Form4_Load(object sender, EventArgs e)
{
var instance = new ProjectM1();
instance.ProjectM1(); (error !!!)
}
}
}

Service variables reset when calling activity closed in android

I am having this issue for a couple of days now, i did a lot of searching in the Internet, but did not find something that works in my case.
I have a service :
namespace Demo_Service_Name
{
public static class MyGlobals
{
...
public static int DurSelection;
}
class Service_Connection : Java.Lang.Object, IServiceConnection
{...}
public class Demo_Service : Service
{
public void Setup_Service(int DSel)
{
MyGlobals.DurSelection = DSel;
}
public int Get_Duration_Selection()
{
return MyGlobals.DurSelection;
}
...
}
}
and my Activity goes like this:
using Demo_Service_Name
namespace Demo_Activity
{
public class Activity1 : Activity
{
protected override void OnCreate(Bundle bundle)
{
....
ThisService = new Intent(this, typeof(Demo_Service));
if (isMyServiceRunning())
{
demoServiceConnection = new Neural_Service_Connection(this);
isBound = BindService(ThisService, demoServiceConnection, Bind.none);
int servVar = binder.GetDemoService().Get_Duration_Selection();
}
}
protected void OnStartMyService()
{
....
StartService(ThisService);
isBound = BindService(ThisService, demoServiceConnection, Bind.none);
binder.GetDemoService().Setup_Service(A_Certain_Number);
}
}
}
So what I want to do is to start the service from the activity, bind to it, pass a value, close the activity, reopen the activity and read the variable back into the activity if the service is running.
All works like it should, except that when i close the activity, the value in the service variable is reset.
What do you think am I missing?
PS. I am sure the Service.OnDestroy has not been called

Categories