Xamarin/Tizen: Executing Navigation.PopAsync() crashes the app - c#

I have a navigation page that sets up three pages. The first page loads, the user has to pick an option from a listview and then it loads the second page with PushAsync(). At this point the user can now navigate between the three pages by turning the clock face. If I call PopToRootAsync() on the second page it works fine. If the rotary clock face is turned clockwise it loads a third page via PushAsync().
The problem is if I call PopAsync() on that third page OR I change the PopToRootAsync() on the second page to a PopAsync(), the app crashes. I have no way to determine what the error is either as I just get segmentation fault and nothing is written to the Tizen log that is seemingly indicative of why it crashed.
Is there some reason that a PopAsync() would cause this? I know I saw some other articles this could occur if the MainPage is not loaded into a NavigationPage but I'm doing that. I've been looking through stuff and writing debug logs for days but have nothing to show for it. Any help would be more than appreciated. Thank you!
App.cs
public App()
{
MainPage = new NavigationPage(new ServerSelectionPage());
}
ServerSelection.cs
private void ServerSelection_OnItemTapped(object sender, ItemTappedEventArgs args)
{
App.SERVER = (Server)args.Item;
Navigation.PushAsync(new ArrowsPage());
}
PageBase.cs
public async void Rotate(RotaryEventArgs args)
{
Page _currentPage = Page.REMOTE_BUTTONS;
if (this.GetType() == typeof(ButtonsPage))
_currentPage = Page.REMOTE_BUTTONS;
else if (this.GetType() == typeof(ArrowsPage))
_currentPage = Page.REMOTE_ARROWS;
else
_currentPage = Page.SERVER_SELECTION;
// When rotating (previous rotation is ongoing, do nothing)
if (_rotating)
{
return;
}
_rotating = true;
if (!(App.SERVER is null))
{
if (_currentPage == Page.SERVER_SELECTION)
{
if (args.IsClockwise)
await Navigation.PushAsync(new ArrowsPage());
}
else if (_currentPage == Page.REMOTE_DIRECTIONAL)
{
if (args.IsClockwise)
await Navigation.PushAsync(new ButtonsPage());
else
await Navigation.PopToRootAsync();
}
else
{
try
{
if (!args.IsClockwise)
await Navigation.PopAsync(); // This will crash the app
}
catch(Exception ex)
{
Log.Debug(ex.Message);
}
}
}
_rotating = false;
}

After reading the comment by #vin about checking if the Navigation object is null I suddenly had the thought that it may not be the Navigation page but the RotaryFocusObject. So I changed
if (!args.IsClockwise)
await Navigation.PopAsync();
to
if (!args.IsClockwise)
{
RotaryFocusObject = null;
await Task.Delay(300);
await Navigation.PopAsync();
}
and it no longer crashes. The delay is necessary as if you call PopAsync right after setting the object to null it can still sometimes crash.
So apparently if you pop the current page it causes an error as the focus of the rotary dial is still set to the current navigation page. Why this error does not seemingly occur if you use the Navigation.PopToRootAsync() makes no sense to me.

Navigation.PopToRootAsync() and Navigation.PopToRootAsync() are causing destroy of Page renderer, and the Rotate handler is belong to Page Renderer,
If Page renderer was deleted before completion of Rotate Native Callback that is belong to Page renderer, it will be crashed.
await Task.Delay() let to returning native callback.

Related

Page navigation event

I was reading trough the documentation for UWP and I got stuck a little.
I have few pages that connect to WCF service taking some information of it few of them download pictures and take few seconds to load.
So I've decided to implement a loading screen while they load however when I try using
this.Frame.Navigate(typeof(page));
I get stuck in a deadlock state everything freezes while the new page is loading I've tried putting on pageloading event on the other page but this is not helping much since its still locked on the last form.
Does anyone know the right event that I need to call when calling this.Frame.Navigate() so I can initialize my loading control while the new frame is loaded?
Navigate to Loading Screen
this.Frame.Navigate(typeof(LoadingScreen));
In OnNavigatedTo event in LoadingScreen "download pictures"
protected override async void OnNavigatedTo(NavigationEventArgs e)
{
await DownloadPictures();
//After downloading, navigate to the next page
this.Frame.Navigate(typeof(page));
}
try launching the view on a separate window like this
try
{
CoreApplicationView Nv= CoreApplication.CreateNewView();
var z = CoreApplication.MainView;
int id= 0;
await
Nv.Dispatcher.RunAsync(Windows.UI.Core.CoreDispatcherPriority.Normal, () =>
{
Frame frame = new Frame();
frame.Navigate(typeof(page));
Window.Current.Content = frame;
// You have to activate the window in order to show it later.
Window.Current.Activate();
id= ApplicationView.GetForCurrentView().Id;
});
bool viewShown = await ApplicationViewSwitcher.TryShowAsStandaloneAsync(id);
}
catch (Exception eee)
{
Windows.UI.Popups.MessageDialog errorBox =
new Windows.UI.Popups.MessageDialog("Couldn't Create New
Window: " + eee.Message);
await errorBox.ShowAsync();
}

Monotouch interactivePopGestureRecognizer and Navigation.popasync() blank view

I just noticed a odd behavior when using the interactivePopGestureRecognizer to go back in my app.
Case scenario:
1) User Drags view from left to right he goes back one view "interactivePopGestureRecognizer".
2) User swipes up or down "await Navigation.PopAsync(false);" is called and user goes back one view.
3) if User does action "1" and then calls a new view and tries to go-back using action 2 a blank view is displayed.
This error is only appearing if the user uses action 1 and then tries to use action 2; app works fine if action 1 is never used or if only action 1 is used, no both.
I am using Xamarin.Forms and I tried to used "interactivePopGestureRecognizer.enabled = false", but I get an error every-time I tried. is there a difference between the two back navigations?
-------------UPDATE----------
After reading a lot and looking in the internet I found out that ~interactivePopGestureRecognizer.enabled = false only works if you use it inside the ~ViewWillAppear I created a custom renderer that applies this to every tableView in my app. I would still want to figure out why is the back swipe acting this way.
----UPDATE 2---
Just pressing the back button and then trying to call navigation.popasync is giving me a blank page too. this seems to be an error between Xamarin.Navigation and the iOS back function.
Page:
MessagingCenter.Subscribe<string>(this, "UpSwipe", async (sender) =>
{
try
{
//await Navigation.PopAsync(false);
Navigation.RemovePage(this);
}
catch (Exception e)
{
await DisplayAlert("IsLoading", e.ToString(), "OK");
}
});
MessagingCenter.Subscribe<string>(this, "DownSwipe", async (sender) =>
{
try
{
//await Navigation.PopAsync(false);
Navigation.RemovePage(this);
}
catch (Exception e)
{
await DisplayAlert("IsLoading", e.ToString(), "OK");
}
});
Renderer:
private void UpdateUp()
{
// Insert view of DetailLeft element into subview
// Add button to open Detail to parent navbar, if not yet there
// Add gesture recognizer for left swipe
//Console.WriteLine ("Left swipe");
if (!buttons[2].Selected && !buttons[1].Selected)
{
MessagingCenter.Send("Swiped Up", "UpSwipe");
}
}
private void UpdateDown()
{
// Insert view of DetailLeft element into subview
// Add button to open Detail to parent navbar, if not yet there
// Add gesture recognizer for left swipe
//Console.WriteLine ("Left swipe");
if (!buttons[2].Selected && !buttons[1].Selected)
{
MessagingCenter.Send("Swiped Down", "DownSwipe");
}
}
After hours of debugging I fixed my issue by adding a return statement inside my renderer. I was checking and adding some gestureRecognizers in my renderer so a return was not needed until this bug came out.
protected override void OnElementChanged (ElementChangedEventArgs<LRMasterDetailPage> e)
{
base.OnElementChanged (e);
if (e.OldElement != null)
{
return;
}
}

How to set a "First-Launch-View" in C#

I searched everywhere, but i can't find a tutorial for my problem. I want to set an page to be shown, when the App is launched for the first time. something like th:
First launch:
Greeting.xaml>Setting.xaml>MainPage.xaml
Regular launch goes directly to MainPage.
how can i do this?
I didn't mean a Splashscreen, I mean a page, which is shown only the first time you launch the App, something like a little tutorial.
Your typical template-generated App.xaml.cs has something like this in its OnLaunched method:
if (rootFrame.Content == null)
{
rootFrame.Navigate(typeof(MainPage), e.Arguments);
}
This is where you navigate to your first page. To special-case a first run, do something like this instead:
if (rootFrame.Content == null)
{
IPropertySet roamingProperties = ApplicationData.Current.RoamingSettings.Values;
if (roamingProperties.ContainsKey("HasBeenHereBefore"))
{
// The normal case
rootFrame.Navigate(typeof(MainPage), e.Arguments);
}
else
{
// The first-time case
rootFrame.Navigate(typeof(GreetingsPage), e.Arguments);
roamingProperties["HasBeenHereBefore"] = bool.TrueString; // Doesn't really matter what
}
}
The greetings page should then navigate to your settings page, which should navigate to your main page.
And by using the roaming settings, the user won't see the first-time screen when she logs in to a different machine.
You can set the "first" page within the App.xaml.cs. Search for the OnLaunched void and change rootFrame.Navigate(typeof(MainPage)); to rootFrame.Navigate(typeof(Greeting)); or whtatever you like to call it.
The next step would be to check if the app launches for the first time. You can set an app setting to do that.
1. create the OnnavigatedTo void for your Greeting.xaml (just type "protected override void onna", IntelliSense will suggest it to you) and make is asynchron by inserting "async" after "protected", 2. use this code:
if (ApplicationData.Current.LocalSettings.Values.ContainsKey("isFirstLaunch"))
{
// if that's the first launch, stay, otherwise navigate to Settings.xaml
if (!(bool)ApplicationData.Current.LocalSettings.Values["isFirstLaunch"])
{
await Dispatcher.RunAsync(CoreDispatcherPriority.Normal, () => Frame.Navigate(typeof(Settings)));
}
}
else
{
ApplicationData.Current.LocalSettings.Values["isFirstLaunch"] = false;
}
I haven't tested the code but it should work. If it doesn't, just ask me.
Edit: here's a much better solution :D https://stackoverflow.com/a/35176403/3146261
I just wanted a Disclaimer to be accepted via a MessageBox
IPropertySet roamingProperties = ApplicationData.Current.RoamingSettings.Values;
if (!roamingProperties.ContainsKey("DisclaimerAccepted"))
{
var dialog = new MessageDialog(strings.Disclaimer);
dialog.Title = "Disclaimer";
dialog.Commands.Clear();
dialog.Commands.Add(new UICommand { Label = "Accept", Id = 0 });
dialog.Commands.Add(new UICommand { Label = "Decline", Id = 1 });
var result = await dialog.ShowAsync();
if ((int)result.Id == 1)
Application.Current.Exit();
roamingProperties["DisclaimerAccepted"] = bool.TrueString;
}
I placed it in App.xaml.cs inside of:
if (e.PrelaunchActivated == false)
{
<Inside here>
if (rootFrame.Content == null)
{
}

Windows Phone 8.1 MediaCapture freezes the phone

I want to make a simple app that will allow me to check few parameters of every frame of preview, but I got stuck at running and stopping preview.
/// <summary>
/// An empty page that can be used on its own or navigated to within a Frame.
/// </summary>
public sealed partial class MainPage : Page
{
MediaCapture _MediaCapture;
bool _recording;
public MainPage()
{
this.InitializeComponent();
this.NavigationCacheMode = NavigationCacheMode.Required;
}
/// <summary>
/// Invoked when this page is about to be displayed in a Frame.
/// </summary>
/// <param name="e">Event data that describes how this page was reached.
/// This parameter is typically used to configure the page.</param>
protected override async void OnNavigatedTo(NavigationEventArgs e)
{
var devices = await Windows.Devices.Enumeration.DeviceInformation.FindAllAsync(DeviceClass.VideoCapture);
var rearCamera = devices[0];
if (devices.Count > 0)
{
rearCamera = devices.Single(currDev =>
currDev.EnclosureLocation.Panel == Windows.Devices.Enumeration.Panel.Back
);
}
_MediaCapture = new MediaCapture();
await _MediaCapture.InitializeAsync(new MediaCaptureInitializationSettings() { VideoDeviceId = rearCamera.Id });
// this is CaptureElement
xCapture.Source = _MediaCapture;
_recording = false;
}
protected override async void OnNavigatedFrom(NavigationEventArgs e)
{
if(_MediaCapture != null)
{
await _MediaCapture.StopPreviewAsync();
await _MediaCapture.StopRecordAsync();
_MediaCapture.Dispose();
_MediaCapture = null;
xCapture.Source = null;
}
base.OnNavigatedFrom(e);
}
// button click handler
private async void StartMeasure(object sender, RoutedEventArgs e)
{
if (_recording)
{
//await _MediaCapture.StopPreviewAsync();
_MediaCapture.VideoDeviceController.TorchControl.Enabled = false;
_recording = false;
}
else
{
//await _MediaCapture.StartPreviewAsync();
_MediaCapture.VideoDeviceController.TorchControl.Enabled = true;
_recording = true;
}
}
}
In this form it works perfectly.
If I uncomment those preview lines it works, but only once.
If I press the button three times: on, off and on again I get exception at line with enabling TorchControl.
System.Exception: Exception from HRESULT: 0xE801000D at Windows.Media.Devices.TorchControl.put_Enabled(Boolean value) at Pulsometr3.MainPage.d__d.MoveNext()
The HRESULT varies.
Whats even more weird, it sometimes freezes the phone (like 2 out of 3 times) and I need to hold Power + Volume Down.
I tried decorating all methods with [STAThread], but it didn't help (http://technet.microsoft.com/en-ca/br226599).
What's even more more interesting, when I hold operations by debbuger using F10 to step over lines I am able to toggle preview as many times as I possibly want. It's werid, since debugger hold all threads, right? So in theory there is no difference?
Also, phone sometimes freezes on deploy... And that's just annoying.
Any ideas?
I've got exactly into this...for some reason microsoft does not care much for it's successor OS to WP8, which makes me really sad. But it was also a half year ago during summer, I've tried this, maybe you can give a shot to googling on application consents and also double check your app manifests, if you have front/rear camera and webcam ticked in :) Besides that if it won't work, then bad luck, you are ought to stick with wp 8.0 version, which works exactly the same on wp 8.1 so do not worry :) also other libs like facebook stuff or parse.com won't work on wp 8.1 C# :)
I think your problem is the page cache enabled. Try to remove this line in your code this.NavigationCacheMode = NavigationCacheMode.Required;
if I understand correctly the button has a handler StartMeasure which is an async method and awaits for Start/StopPreviewAsync().
The problem might be that if you click the button more than once the one action might be still awaited(in progress) and the other one is also called, this might cause some issues because it will try to start and stop the preview at the same time which will probably lead to some race conditions.
You could check this by adding a lock to manage the access to the capture manager in order to test this. Also checking the bool and assigning it after an awaited operation is for sure not an atomic operation so that could lead to race conditions too.
private object locker;
private async void StartMeasure(object sender, RoutedEventArgs e)
{
lock (locker)
{
if (_recording)
{
await _MediaCapture.StopPreviewAsync();
}
else
{
await _MediaCapture.StartPreviewAsync();
}
_recording = !_recording;
_MediaCapture.VideoDeviceController.TorchControl.Enabled = _recording;
}
}

WP8: Fast app resume + secondary tile + MainPage = 2 instances

I'm having the same problem posed here:
http://social.msdn.microsoft.com/Forums/wpapps/en-us/af8615e7-8e90-4069-aa4d-3c4a84a6a3d0/windows-phone-8-fast-app-resume-with-deeplinks?forum=wpdevelop
I'm no C# or WP expert, so please bear with me.
I have secondary tiles which link to "/MainPage.xaml?id=XX".
I have fast app resume enabled. (ActivationPolicy="Resume" in the app manifest)
I only have one page in my app: MainPage.xaml.
Problem: When I resume the app using a secondary tile ("/MainPage.xaml?id=XX"), I get a brief view of the previous instance (that would have resumed) and then the MainPage initializes again, creating a new instance. In effect, the app is loading from scratch after giving me a peek of what was previously open.
That is obviously undesired behavior. I want to use the existing instance to perform my task.
Attempt 1:
Use e.Cancel = true; to cancel the navigation to the MainPage.xaml:
(using the App.xaml.cs code from the official Fast App Resume sample to identify how the app was launched)
...
else if (e.NavigationMode == NavigationMode.New && wasRelaunched)
{
// This block will run if the previous navigation was a relaunch
wasRelaunched = false;
if (e.Uri.ToString().Contains("="))
{
// This block will run if the launch Uri contains "=" (ex: "id=XX") which
// was specified when the secondary tile was created in MainPage.xaml.cs
sessionType = SessionType.DeepLink;
e.Cancel = true; // <======================== Here
// The app was relaunched via a Deep Link.
// The page stack will be cleared.
}
}
...
Problem: In doing so, my OnNavigatedTo event handlers never fire, so my query string is never parsed.
protected override void OnNavigatedTo(System.Windows.Navigation.NavigationEventArgs e)
{
String navId;
if (e.NavigationMode != System.Windows.Navigation.NavigationMode.Back)
{
if (NavigationContext.QueryString.TryGetValue("id", out navId))
{
MessageBox.Show(navId.ToString()); // Not reached
}
}
...
Attempt 2:
Use e.Cancel = true; to cancel the navigation to the MainPage.xaml, AND pass the Uri to a method in MainPage:
// App.xaml.cs
...
else if (e.NavigationMode == NavigationMode.New && wasRelaunched)
{
// This block will run if the previous navigation was a relaunch
wasRelaunched = false;
if (e.Uri.ToString().Contains("="))
{
// This block will run if the launch Uri contains "=" (ex: "id=XX") which
// was specified when the secondary tile was created in MainPage.xaml.cs
sessionType = SessionType.DeepLink;
e.Cancel = true;
MainPage.GoToDeepLink(e.Uri); // <======================== Here
// The app was relaunched via a Deep Link.
// The page stack will be cleared.
}
}
...
// MainPage.xaml.cs
public static void GoToDeepLink(Uri uri) // <======================== Here
{
// Convert the uri into a list and navigate to it.
string path = uri.ToString();
string id = path.Substring(path.LastIndexOf('=') + 1);
MyList list = App.ViewModel.ListFromId(Convert.ToInt32(id));
pivotLists.SelectedItem = list;
}
Problem: I get an error that pivotLists is non-static and thus requires an object reference. I think that in order to get this to work I'd need to create a new instance of MainPage (MainPage newMainPage = new MainPage();) and call newMainPage.pivotLists.SelectedItem = list; -- BUT I don't know how to use newMainPage instead of the existing one/replace it... or if that's something I want/won't cause further problems/complications.
I don't know what the solution is to this problem, and I may be going in the completely wrong direction. Please keep all suggestions in simple terms with code examples if you can, I'm still learning.
Thanks for any help.
It seems that when you reopen your App from secondary tile, then it's reactivated and new instance of MainPage is created (even if there is one from previous run). If I understood you correctly, I've managed to do such a thing:
In app.xaml.cs:
I've added a variable which indicates if I should return to previous MainPage after Navigating from secondary tile - it needs to be static as I want to have access to it from MainPage
public static bool returnPage = false;
In RootFrame_Navigating I'm setting this variable to true in:
// ...
else if (e.NavigationMode == NavigationMode.New && wasRelaunched)
{
// This block will run if the previous navigation was a relaunch
wasRelaunched = false;
returnPage = true;
// ...
In ClearBackStackAfterReset - prevent from deleting the old Page, when returning:
// ...
if (e.NavigationMode != NavigationMode.New || returnPage)
return;
// ...
In MainPage.cs:
I've changed a little constructor, as I don't want to see a blink of a new Page:
public MainPage()
{
if (!App.returnPage)
InitializeComponent();
}
In MainPage I've also variable which is passed from secondary tile - it's also static, as I need only one instance of it:
private static string navId = "";
And the core of the trick - OnNavigatedTo:
protected override void OnNavigatedTo(NavigationEventArgs e)
{
if (App.returnPage)
{
App.returnPage = false;
NavigationContext.QueryString.TryGetValue("id", out navId);
NavigationService.GoBack();
}
else if (e.NavigationMode != NavigationMode.Reset)
{
// normal navigation
}
}
It works like this:
when you launch normally your App, returnPage is false, everything goes normal
when you activate it from secondary tile few things happen:
1. first goes navigation to your previous page with NavigationMode.Reset - we are not interested in it, so I switched it off - nothing should happen
2. then program tries to create new instance of MainPage, but returnPage is true, and because of the if statement, InitializeComponent won't run. Just after this, in OnNavigatedTo, program saves passed querystring and Navigates Back to previous instance of MainPage - from previous run
3. at last we are navigating to right MainPage with NavigationMode.Back and we have our querystring saved in static variable.
You must be aware of two things: first - probably it can be little rebuild (I'm not sure if wasRelaunched is needed and so on) - you need to debug it and see of what you can get rid off. Second - you will probably need to test your App with Tombstone case.
Hope this helps.

Categories

Resources