Monotouch interactivePopGestureRecognizer and Navigation.popasync() blank view - c#

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;
}
}

Related

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

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.

Xamarin IOS Navcontroller exception

I'm running into an exception which I cant resolve myself due to my limited knowledge.
I use a slide out menu in my IOS app in order to navigate through my app ViewControllers.
Whenever I try to navigate to the same ViewController for the second time I run into an exception:
"NSInvalidArgumentException Reason: Pushing the same view controller instance more than once is not supported"
The code looks like this:
rolladeButton.TouchUpInside += (o, e) => {
if (NavController.TopViewController as Rolladecontroller == null)
NavController.PushViewController(rolladeController, false);
}
The first time I push rolladeButton it works, second time I run into the exception above.
It took me some hours and I came up with this 'solution'
lichtButton.TouchUpInside += (o, e) => {
if (NavController.TopViewController as ContentController == null && pushedlicht == 0) {
NavController.PushViewController(contentController, false);
SidebarController.CloseMenu();
pushedlicht++;
}
else
{
NavController.PopToViewController(contentController, false);
}
Now this may look stupid to you, but it gets the job done for me ... at least until I tried adding the third menu button.
Using this solution with currently 3 different ViewController I run into an exception again:
"Tried to pop to a view controller that doesn't exist." Even tho I already opened it once.
My guess: the ViewController that was already added previously was deleted from the stack. Because of my variable it tries to push it even tho it does not exist.
Why is (NavController.TopViewController as Rolladecontroller == null) always true?
Can anyone help me out here on how to do it properly?
For now I use this
lichtButton.TouchUpInside += (o, e) => {
try
{
NavController.PushViewController(contentController, false);
SidebarController.CloseMenu();
}
catch (Exception E)
{
NavController.PopToViewController(contentController, false);
SidebarController.CloseMenu();
}
startButton.SetBackgroundImage(UIImage.FromFile("start_buttoninactive"), UIControlState.Normal);
lichtButton.SetBackgroundImage(UIImage.FromFile("licht_buttonactive"), UIControlState.Normal);
RolladeButton.SetBackgroundImage(UIImage.FromFile("rollade_buttoninactive"), UIControlState.Normal);
};
But it don't think thats the proper way to do it and if I ever wanted to submit it for approval it would probably fail.

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)
{
}

Button is Clicking Before File Upload is Complete in Webbrowser C#

On a Windows Form, I am using a Webbrowser control in C#. It's job is to upload a file and then press the submit button. My only problem is that my code tries to press the submit button before the file is finished uploading. I tried using:
System.Threading.Thread.Sleep(1000);
In between the two tasks (commented out below). This seems to pause the entire process so that didn't work. Can anyone tell me what the best way to do this is?
private void imageBrowser_DocumentCompleted(object sender, WebBrowserDocumentCompletedEventArgs e)
{
this.imageBrowser.DocumentCompleted -= imageBrowser_DocumentCompleted;
Populate().ContinueWith((_) =>
{
//MessageBox.Show("Form populated!");
}, TaskScheduler.FromCurrentSynchronizationContext());
//System.Threading.Thread.Sleep(10000);
try
{
var buttons = imageBrowser.Document.GetElementsByTagName("button");
foreach (HtmlElement button in buttons)
{
if (button.InnerText == "done")
{
button.InvokeMember("click");
}
}
}
catch
{
//debug
}
}
async Task Populate()
{
var elements = imageBrowser.Document.GetElementsByTagName("input");
foreach (HtmlElement file in elements)
{
if (file.GetAttribute("name") == "file")
{
file.Focus();
await PopulateInputFile(file);
}
}
}
async Task PopulateInputFile(HtmlElement file)
{
file.Focus();
// delay the execution of SendKey to let the Choose File dialog show up
var sendKeyTask = Task.Delay(500).ContinueWith((_) =>
{
// this gets executed when the dialog is visible
SendKeys.Send("C:\\Users\\00I0I_c0OlVXtE6FO_600x450.jpg" + "{ENTER}");
System.Threading.Thread.Sleep(1000);
SendKeys.Send("{ENTER}");
}, TaskScheduler.FromCurrentSynchronizationContext());
file.InvokeMember("Click"); // this shows up the dialog
await sendKeyTask;
// delay continuation to let the Choose File dialog hide
await Task.Delay(500);
//SendKeys.Send("{ENTER}");
}
Is the WebBrowser loading a local file? Can you post the html code as well?
I came across such a situation when I was working with google-maps-api-3. I was setting some markers on the form in the WebBrowser_DocumentCompleted but was getting a null object exception. So I moved the code for set marker to a .NET Button control. I noticed that the exception was not thrown when I set the marker after the map tiles completed loading. DocumentCompleted was firing before the tiles got loaded and I was getting a null object exception.
So what I did was to use a tilesLoaded event in my javascript. In this event, I set a property back in C# code which set the markers in the OnPropertyChanged event.
I know what I am posting here is not a solution. But if you post your html code, I can give you answer with some code.
I solved this. The code I was using to click the button was in the wrong spot. The code now looks like so:
private void imageBrowser_DocumentCompleted(object sender, WebBrowserDocumentCompletedEventArgs e)
{
this.imageBrowser.DocumentCompleted -= imageBrowser_DocumentCompleted;
try
{
Populate().ContinueWith((_) =>
{
var buttons = imageBrowser.Document.GetElementsByTagName("button");
foreach (HtmlElement button in buttons)
{
if (button.InnerText == "done")
{
button.InvokeMember("click");
}
}, TaskScheduler.FromCurrentSynchronizationContext());
}
catch
{
//debug
}
}
My mistake was thinking in terms of having a certain amount of seconds pass before executed the next line of code, when I should have been thinking in terms of having the next line of code execute when the previous task was complete.

How to manage back stack when navigating to page from secondary tile

I have implemented secondary tiles in my application, so a user is able to pin a secondary tile to the start screen, and then navigate to a respective page in my application accordingly. This implementation works ok, except for when the user hits the back hardware button after navigating to a specific page from the pinned secondary tile, nothing happens? In fact, for a quick second the previous page in the application is actually shown, although the user is coming from the start screen. What would be the proper method to actually return to the start screen as the user would expect would happen (I am assuming this would be the proper back stack navigation)?
What I have is as follows, but only works during normal page navigation scenarios, not when the user is navigating to the SharePage from the start screen pinned tile.
MainPage.xaml.cs
protected override void OnNavigatedTo(NavigationEventArgs e)
{
base.OnNavigatedTo(e);
string _title = null;
NavigationContext.QueryString.TryGetValue("Param", out _title);
if (_title != null)
{
switch (_title)
{
case "status":
this.NavigationService.Navigate(new Uri("/Views/ShareStatusPage.xaml", UriKind.Relative));
break;
case "link":
this.NavigationService.Navigate(new Uri("/Views/ShareLinkPage.xaml", UriKind.Relative));
break;
}
}
}
private void CreateLiveTile(TileItem item)
{
string tileParameter = "Param=" + item.Title.ToString();
ShellTile Tile = CheckIfTileExist(tileParameter); // Check if Tile's title has been used
if (Tile == null)
{
try
{
var LiveTile = new StandardTileData
{
Title = item.TileName,
//BackgroundImage = ((System.Windows.Media.Imaging.BitmapImage)hubtile.Source).UriSource,
BackgroundImage = new Uri(item.ImageUri.ToString(), UriKind.Relative),
//Count = 1,
BackTitle = item.TileName,
//BackBackgroundImage = new Uri("", UriKind.Relative),
BackContent = item.Message,
};
ShellTile.Create(new Uri("/MainPage.xaml?" + tileParameter, UriKind.Relative), LiveTile); //pass the tile parameter as the QueryString
}
catch (Exception)
{
MessageBox.Show("This tile could not be pinned", "Warning", MessageBoxButton.OK);
}
}
else
{
MessageBox.Show("This tile has already been pinned", "Notice", MessageBoxButton.OK);
}
}
private ShellTile CheckIfTileExist(string tileUri)
{
ShellTile shellTile = ShellTile.ActiveTiles.FirstOrDefault(tile => tile.NavigationUri.ToString().Contains(tileUri));
return shellTile;
}
SharePage.xaml.cs
protected override void OnBackKeyPress(System.ComponentModel.CancelEventArgs e)
{
base.OnBackKeyPress(e);
//return to the previous page in the phones back stack?
if (NavigationService.CanGoBack)
{
e.Cancel = true;
NavigationService.GoBack();
}
//else
//{
// ??
//}
}
So far, CreateLiveTile() method creates the secondary tile and then when that tile is pressed, MainPage is navigated to and then the querystring is checked in the MainPage OnNavigatedTo event and the respective page is then loaded based on what secondary tile was clicked. Once this is performed and the respective pages has loaded, I can no longer press the back button to get back to the start screen to follow the standard backstack behavior. How can I fix this?
According to your new update, lets have a look at this method:
private void CreateLiveTile(TileItem item)
{
string tileParameter = "Param=" + item.Title.ToString();
//...
if (Tile == null)
{
try
{
var LiveTile = new StandardTileData
{
//...
};
ShellTile.Create(new Uri("/MainPage.xaml?" + tileParameter, UriKind.Relative), LiveTile); //pass the tile parameter as the QueryString
//blah-blah-blah
}
Here you create a new tile and pass tileParameter to a MainPage. So, you navigate to main page, then detect the text in tile parameter, and navigate to the ShareLink or ShareStatus pages. That's why you have a dirty navigation stack.
Let me suggest you a way to avoid this situation:
private void CreateLiveTile(TileItem item)
{
var title = item.Title.ToString();
//...
if (Tile == null)
{
try
{
var LiveTile = new StandardTileData
{
//...
};
string page;
switch (title)
{
case "status":
page = "/Views/ShareStatusPage.xaml";
break;
case "link":
page = "/Views/ShareLinkPage.xaml");
break;
}
if(string.IsNullOrEmpty(page))
{
//handle this situation. for example: page = "/MainPage.xaml";
}
ShellTile.Create(new Uri(page, UriKind.Relative), LiveTile);
//blah-blah-blah
}
When user taps on your secondary tile he will be navigated directly to ShareLink or ShareStatus page. And a NavigationStack will be clean. When the user press Back button, the application will be closed and user will see a start screen (this is a right back button behaviour for secondary tiles).
p.s. Don't forget to start all your services or load all resources if you have ones. Because MainPage won't be created! Anyway every page of your application has to be able to itnitialize whole application, because you must support restoring from tombstoned state.
Feel free to ask for details if you need.
else you can use NavigationService.Navigate(homepageUri)
Why do you cancel a navigaion? Just remove OnBackKeyPress. You don't need it in this scenario.
Create a variable to track whether the navigation to the main page came from a navigation through a secondary tile. On MainPage.Load check that variable, and if the variable came through a secondary tile, remove the prior page from the back stack. It makes sense to leave from the main page back to the start menu, rather than go back through the secondary tile page. This is how MyStocks Portfolio (no, not one of my apps, but one I l
Here's a good blog on back button use:
http://blogs.msdn.com/b/ptorr/archive/2011/10/06/back-means-back-not-forwards-not-sideways-but-back.aspx

Categories

Resources