How to know the popped state of a NavigationPage in Xamarin - xamarin.forms

I want it to popup or not to popup by receiving the On and Off events.
I received the On event and popped it up. Off event has [await Navigation.PopAllPopupAsync(false); ] to clear the popup.
There are cases where the off event comes in twice. If there is no currently popped page, await Navigation.PopAllPopupAsync(false); will throw an exception. So I want to know if there is a flag to know if there is currently popped screen.
How to know which page is currently popped up

Fix #1:
You could make the exception harmless:
try
{
await Navigation.PopAllPopupAsync(false);
}
catch (Exception ex)
{
}
Fix #2:
You could keep track of it yourself:
public static bool PopupIsShowing;
// In On event, Just before (or just after) you show it.
PopupIsShowing = true;
// In Off event,
if (PopupIsShowing) {
PopupIsShowing = false;
await Navigation.PopAllPopupAsync(false);
}

Related

Xamarin Forms - limiting TapGestureRecognizer to a single tap

I have a TapGestureRecognizer on a Label. All works great with the following Command handler (using FreshMVVM PushPageModel which navigates to the next page)...
public Command OpenDailyEntry
{
get
{
return new Command(async (e) =>
{
await CoreMethods.PushPageModel<DailyEntryPageModel>(null);
});
}
}
The problem is the next screen takes a second or so to appear, and before that happens the user can tap multiple times causing the event to fire multiple times. On Android this results in the same page being opened each time the user taps the label.
Is there a way to prevent this without some global flag that disables the Command after the first tap?
You could add a second lambda for the CanExecute method of your Command.
If you add a property to your page navigation service, e.g. IsCurrentlyPushingPage, which keeps track of when the pushing to the navigation stack starts and when it ends, you can evaluate this property and deactivate the OpenDailyEntry command temporarily.
public Command OpenDailyEntry
{
get
{
return new Command(async (e) =>
{
await CoreMethods.PushPageModel<DailyEntryPageModel>(null);
},
() =>
{
return CoreMethods.IsCurrentlyPushingPage == false;
}
);
}
}

Continue code when awaited PushModalAsync Page is closed Xamarin.Forms

I have a method like this:
public async Task BtnLoad_OnClick()
{
MediaPage galleryPage = new MediaPage();
await Application.Current.MainPage.Navigation.PushModalAsync(galleryPage);
try
{
//some logic here;
}
catch (Exception ex)
{
//
}
}
My intention was to open MediaPage() and wait until it is closed before the try{}catch{} follows.
As of now, as soon as the MediaPage opens, the try{}catch{} is executed straight away, and this is not what I intend.
How can I wait until the PushModalAsync(galleryPage) is closed?
OK.
I found the solution:
galleryPage.Disappearing += (sender2, e2) => {//after closing logic here.}

Xamarin forms: UWP app breaks when the code execution comes to displayalert line

I am using displayalert in my project and it is working fine on android and ios devices. But for the UWP part it is not working in some pages. When the code execution comes to displayalert line the UWP app breaks and redirecting to App.g.i.cs. Code control comes to the following if block.
#if DEBUG && !DISABLE_XAML_GENERATED_BREAK_ON_UNHANDLED_EXCEPTION
UnhandledException += (sender, e) =>
{
if (global::System.Diagnostics.Debugger.IsAttached) global::System.Diagnostics.Debugger.Break();
};
#endif
If I mouse over the e showing {Windows.UI.Xaml.UnhandledExceptionEventArgs}
I am using the following code for display alert:
await DisplayAlert("Alert", "A verification code has been sent to your email.", "OK");
If I changed the above lines like below, no issue will come.
Device.BeginInvokeOnMainThread(async () =>
{
await DisplayAlert("Alert", "A verification code has been sent to your email.", "OK");
});
Is there any solution for this issue without adding Device.BeginInvokeOnMainThread? And what is the reason behind this issue?
Sample code: Following code is a button clicked code. When the code execution comes to any of the display alert line, getting the above error.
async void ButtonClicked(Object sender, EventArgs e)
{
UserDialogs.Instance.ShowLoading("Signing in...");
var name = usernameEntry.Text;
var password = passwordEntry.Text;
if (Utility.IsInternet())
{
if (!string.IsNullOrWhiteSpace(name) && !string.IsNullOrWhiteSpace(password))
{
UserLogin(name, password);
}
else
{
UserDialogs.Instance.HideLoading();
await DisplayAlert("Alert", "Please enter all details", "Ok");
}
}
else
{
UserDialogs.Instance.HideLoading();
await DisplayAlert("Alert", "No internet connection", "Ok");
}
}
Depending on where this line is:
await DisplayAlert("Alert", "A verification code has been sent to your email.", "OK");
You can do it without Device.BeginInvokeOnMainThread. But I think it is important to know what Device.BeginInvokeOnMainThread does first.
Whenever you execute something on a different thread than the main thread, it will run in the background. The main thread is the only thread that can interact with the UI, simply because else it will become a mess with race conditions and unpredictable UI behavior. This is, as far as I know, true for every programming language.
That means, that whatever code is surrounding your alert is running in a background thread. That is why you are getting this exception. To execute something on the main thread (and thus have access to the UI), you can wrap it in a Device.BeginInvokeOnMainThread call.
Since we don't know what the rest of your code looks like, it is hard to tell if you can show an alert without wrapping is in the Device.BeginInvokeOnMainThread call. But hopefully, with this information, you can figure that out yourself.

View event details in Firebase

I am sending app usage analytics events to Fabric and Firebase. Together with the event, I am also sending another value (an example event type is font_selection and the value I pass is which font the user selects - this is a number that tells me which font was used). I was using Fabric events and I could see which fonts were being used more or less when I selected the font_selection event (I could see numbers for each different font).
Since the Fabric functionality is being moved to Firebase, I started checking the Analytics section in Firebase. Unfortunately I cannot find the above information in Firebase > Analytics > Events. I can see the event, font_selection but when I click on it I do not get the additional information I used to get in Fabric. Is there something I am missing or has this additional information been removed from Firebase?
This is still an issue for me. Here is how I'm sending the event into Firebase:
protected void Report(string id, Severity severity, string message = null, Exception exception = null)
{
try
{
var processedId = id ?? severity.ToString().ToLowerInvariant();
var values = new Dictionary<string, string>();
values.Add("severity", severity.ToString().ToLowerInvariant());
if (!string.IsNullOrWhiteSpace(message))
{
values.Add("message", message);
}
if (exception != null)
{
values.Add("exception", exception.Message);
values.Add("trace", exception.StackTrace);
}
SendEvent(values, processedId);
}
catch
{
// do nothing.
}
}
protected override void SendEvent(Dictionary<string, string> eventData, string id)
{
var firebaseAnalytics = FirebaseAnalytics.GetInstance(Android.App.Application.Context);
var bundle = new Android.OS.Bundle();
foreach(var pair in eventData)
{
bundle.PutString(pair.Key, pair.Value);
}
firebaseAnalytics.LogEvent(id, bundle);
}
During runtime, I call this successfully and I can see these event popping up in Firebase console:
But how do I display the rest of the properties that I have bundled with it? Here is what the console shows me in events:
I feel like I must be using it wrong or something. There is no UI to shows me a simple chronologically sorted table with events as they came in with properties they came with. I frankly don't understand what good is this tool to me.

Xamarin.Forms Call to message center increases in number each time

I've a problem where I send message once and Subscriber is called once but next time it is called twice and so on... Here's my code.
This is message sender
public void OnSuccess(Java.Lang.Object result)
{
UploadTask.TaskSnapshot taskSnapShot = (UploadTask.TaskSnapshot)result;
string downloadURL = taskSnapShot.DownloadUrl.ToString();
string fileName = taskSnapShot.Metadata.Name;
GBPaperReceipt.Model.ImageFile imageFile = new Model.ImageFile
{
FileName = fileName,
FilePath = downloadURL
};
MessagingCenter.Send((App)Xamarin.Forms.Application.Current, MessageStrings.ImageUploadEvent, imageFile);
//save this live storage image url in receipt table
//MessagingCenter.Send<Xamarin.Forms.Application, string>((Xamarin.Forms.Application)Xamarin.Forms.Application.Current, ChatModuleConstant.UploadMediaEvent, downloadURL);
}
This is message receiver
MessagingCenter.Subscribe<App, ImageFile>((App)Application.Current, MessageStrings.ImageUploadEvent,async (a, imageFile) =>
{
_viewModel.Receipt.ImagePath = imageFile.FilePath;
_viewModel.Receipt.ImageName = imageFile.FileName;
try
{
await DependencyService.Get<IReceiptService>().SaveReceipt(_viewModel.Receipt);
}
catch (Exception ex)
{
await DisplayAlert(
"Error!", ex.Message, "OK");
}
DependencyService.Get<ICamera>().DeletePhoto(_viewModel._imageToBeDeletedOnSaveCommand);
Dialogs.HideLoading();
Application.Current.MainPage = new NavigationPage(new DashboardPage());
});
Unsubscription
protected override void OnDisappearing()
{
base.OnDisappearing();
MessagingCenter.Unsubscribe<App, string>((App)Application.Current, MessageStrings.ErrorEvent);
MessagingCenter.Unsubscribe<App, string>((App)Application.Current, MessageStrings.ImageUploadEvent);
}
Especially when using your page inside a navigationpage, your subscription event will be added whenever the page comes into view. If you navigate back and forward a couple of times, your subscription to the messagingcenter will be added several times causing your event to double fire.
The safest way is to subscribe in the page constructor and even in that case it can be necessary to unsubscribefirst and then subscribe.
Your Appearing/Disappearing approach might work as well, however I am not entirely sure if the Appearing/Disappearing methods give you any guarantee to fire.
However, you also might try moving your unsubscriptions in front of base.OnDisappearing(), since you should unsubscribe before calling the base class to do the internal dismantling of your page.
If that doesn't work, subscribe in the constructor.

Resources