Remove multiple pages from stack- xamarin.forms - xamarin

I have a xamarin.forms app which has several screens. The navigation of my pages is like this.
1 --> 2 --> 3 --> 4 --> 5 --> 6
The 6th page is popup created using Rg.plugin.popup.User can navigate to and forth as many times in this hierarchy. But when they reach 6th page there is a button. In the button click it should navigate user to 2nd page. How can I remove all the pages ie; 3,4,5 from stack and go to 2nd page.
What I tried
on button click :
for (var i = 1; i < countPagesToRemove; i++)
{
Navigation.RemovePage(Navigation.NavigationStack[Navigation.NavigationStack.Count - 2]);
}
await Navigation.PopAsync();
This link ask same question but it doesn't work for me.
Then I tried like this
await PopupNavigation.Instance.PopAsync();
Application.Current.MainPage = new NavigationPage(new NCDashboard());
It will navigate to 2nd page but will not show back button to first page due to nothing in the stack. So how can I solve this problem? Any help is appreciated.

You're calling RemovePage from the 6th page, which is Rg.Popups PopupPage. As we've discussed in the comments, Rg.Popups PopupPage doesn't go onto the same stack as regular Xamarin.Forms pages. You need to get reference to the stack where the pages from 1st to 5th sits and execute RemovePage on it. Try out the following workaround:
var mainPage = (Application.Current.MainPage as NavigationPage);
for (var i = 1; i < countPagesToRemove; i++)
{
mainPage.Navigation.RemovePage(mainPage.Navigation.NavigationStack[mainPage.Navigation.NavigationStack.Count - 2]);
}
await Navigation.PopAsync();

Updated:
The code on Page6 works well.
protected override void OnAppearing()
{
base.OnAppearing();
for (var i = 1; i <= 3; i++)
{
Navigation.RemovePage(Navigation.NavigationStack[Navigation.NavigationStack.Count - 1]);
}
}
private async void Button_Clicked(object sender, EventArgs e)
{
await PopupNavigation.PopAsync();
}

Related

How to set the navigation controller to take the user to a different view in xamarin ios

My initial view has this code
if (CLLocationManager.Status == CLAuthorizationStatus.Denied ||
CLLocationManager.Status == CLAuthorizationStatus.Restricted ||
CLLocationManager.Status == CLAuthorizationStatus.NotDetermined)
{
Core.FirstLaunch = true;
NavigationController.PushViewController(new LocationServicesVerifyViewController(), false);
}
else
{
NavigationController.PushViewController(new JobListViewController(), false);
}
If the location is not enabled, the location services verify controller is shown, however, the menu has a back button which is taking the user to license activation screen.
How can I change the back button to Job list and clicking it will take the user to job list controller instead?
e.g.,
Solution 1: You need to replace the backbutton and associate an action handler. You could either use text or an image for the Back Button. To do this, in your ViewDidLoad override, add this:
var _backButton = new UIBarButtonItem()
{
Title = "Back",
// Image = UIImage.FromBundle("back_navbar").OriginalRendering(), Need to add the image in Resources for this to work
TintColor = UIColor.FromRGB(0, 70, 113) // Change this to your app colours
};
_backButton.Clicked += _backButton_Clicked;
NavigationItem.SetLeftBarButtonItem(_backButton, animated: true);
And then handle the button press as shown:
void _backButton_Clicked(object sender, System.EventArgs e)
{
var controller = new JobListViewController();
this.PresentViewController(controller, true, null);
}
Solution 2: You could also just remove the unwanted LicenseActivation view controller from the navigation stack so going back won't go to that page. So when you are leaving the LicenseActivation page, in the ViewWillDisappear override, add this code:
var oldControllers = this.NavigationController.ViewControllers;
var newControllers = new UIViewController[oldControllers - 1];
int index = 0;
foreach (var item in oldControllers) {
if (item != this)
{
newControllers [index] = item;
index++;
}
}
this.NavigationController.ViewControllers = newControllers;
Solution 3:
Just use a modal for the LicenseActivation page, so that as soon as you Accept the LicenseActivation, it closes the modal and goes to the next page.
So you are pushing views on to the navigation stack, e.g.,
NavigationController.PushViewController(new LocationServicesVerifyViewController(), false);
so when you press back you are popping the stack and going to the last view. If you don't want this type of navigation you could do this:
Don't push the LocationServicesVerifyViewController to the navigation controller. Rather present it modally like this:
PresentModalViewController(new LocationServicesVerifyViewController(), false);
You will need a close button on LocationServicesVerifyViewController to dismiss.
I would present the JobListViewController by pushing to the navigation controller first then check for the location inside JobListViewController then present the modal LocationServicesVerifyViewController if needed.
OR
Do you need a whole view controller for this? You could just present a popup:
var locationServiceAlertController = UIAlertController.Create("Location Services are off", "Your company... Bla", UIAlertControllerStyle.Alert);
locationServiceAlertController.AddAction(UIAlertAction.Create("Enable", UIAlertActionStyle.Default, alert => Console.WriteLine ("Do your enable stuff")));
locationServiceAlertController.AddAction(UIAlertAction.Create("Cancel", UIAlertActionStyle.Cancel, alert => Console.WriteLine ("Cancel was clicked")));
PresentViewController(locationServiceAlertController, true, null);

Navigation in Xamarin Forms with Custom Movement between pages

Hi Xamarin Developers,
So I have one requirement where user go From Page1 -> Page2 -> Page3 -> Page4 and after Page4, He has to return back to Page2 and If he press back button he should navigate it back to Page1.
How to achieve this in Xamarin Forms.?
I tried like this,
Navigation.RemovePage(Navigation.NavigationStack[Navigation.NavigationStack.Count - 1]);
_ = Navigation.PopAsync(true);
But it's giving weird animation and not working properly.
Please help me on this.
First of all: If you are using
Navigation.RemovePage(Navigation.NavigationStack[Navigation.NavigationStack.Count - 1]);
from page4, Navigation.NavigationStack.Count - 1 = 3, which is the index of page4 in the NavigationStack (page1 has index 0!).
I think you should use
Navigation.RemovePage(Navigation.NavigationStack[Navigation.NavigationStack.Count - 2]);
instead...
Further comments:
Using Shell/Xamarin.Forms 4.0
Shell, available from Xamarin.Forms 4.0, is meant to easy the management of Navigation in an Application. XF 4.0 ships in a couple of weeks, so maybe it would be worth to wait for it (X.F 4.0 is at the moment in pre-release, and you can use Shell features now if you want to give it a chance!).
The traditional way
a) In order to go back to the first page (root page) you can use
Navigation.PopToRootAsync();
b) In order to pop from page4 to page2, i also use the technique with Navigation.RemovePage, but with the difference that i perform this removal immediately after i PushAsync to page4, so once you call PopAsync() from page4, it pops directly to page2 without having to remove anything at that time (see code below). Please try it this way and let me know if you succeed :)
private async Task PushMyPage4RemovingCurrentPageFromNavigationStack()
{
var currentPage = ((NavigationPage)mainPage).CurrentPage as MyPage3;
await currentPage.Navigation.PushAsync(new MyPage4());
await Task.Run(() =>
{
// At this point MyPage4 is already pushed, so it is now the CurrentPage.
var newCurrentPage = ((NavigationPage)mainPage).CurrentPage;
IReadOnlyList<Page> navStack = newCurrentPage.Navigation.NavigationStack;
// If not moved to main thread in iOS we get:
//
// UIKit.UIKitThreadAccessException: UIKit Consistency error: you are
// calling a UIKit method that can only be invoked from the UI thread.
//
if (Device.RuntimePlatform == Device.iOS)
Device.BeginInvokeOnMainThread(() => newCurrentPage.Navigation.RemovePage(navStack[navStack.Count - 2]));
else
newCurrentPage.Navigation.RemovePage(navStack[navStack.Count - 2]);
});
}

EvoPdf: how to get total page count or skip header/footer in last page

I am using EvoPDF for generating html to pdf. And I can skip headers/footers in first page. But I can't find any idea to skip them on last page. If I would get the pagecount, then I can do that. So, is there any way I can achieve?
Thanks.
In the BeforeRenderPdfPageEvent event handler you can hide the footer in last page like this:
HtmlToPdfConverter htmlToPdfConverter = new HtmlToPdfConverter();
htmlToPdfConverter.BeforeRenderPdfPageEvent += htmlToPdfConverter_BeforeRenderPdfPageEvent;
void htmlToPdfConverter_BeforeRenderPdfPageEvent(BeforeRenderPdfPageParams eventParams)
{
PdfPage page = eventParams.Page;
if (eventParams.PageNumber == eventParams.PageCount)
page.ShowFooter = false;
}

Adding script to a webpage to change the contents of a paragraph when the cursor hovers over an image on HTML5 canvas

I have an HTML5 canvas which is displaying a number of images. I also have some simple HTML <p></p> tags on my page below the canvas.
I want to update the contents of the <p></p> tags when the cursor hovers over these images, and I found a quick tutorial at: http://www.quirksmode.org/js/newmouseover.html which seemed to suggest it could teach you how to do this.
I've followed the tutorial, however, when I view my page in the browser now, I get a console error that says
getElementByTagName is not a function
I've not seen this function before, and I'm just wondering if it is actually a pre-defined function, or if it's one that the writer of the tutorial has defined themselves...? I couldn't see anything on that page where the author has defined the function, so I thought it might be a pre-defined one, but I'm not sure. Does anyone know?
Edit
Ok, so correcting the typo fixed it, and the function is now being called. However, I'm currently calling it from my window.onload function, so as soon as the page loads, the paragraph has already been updated- it's not actually conditional on the onmouseover event being called.
My window.onload function looks like this:
window.onload = function () {
var sources = [];
sources[0] = document.getElementById("building").src,
sources[1] = document.getElementById("chair").src,
sources[2] = document.getElementById("drink").src,
sources[3] = document.getElementById("food").src,
/*There are roughly 30 lines like this adding images in the same way */
if (document.getElementById) {
var x = document.getElementById('mouseovers')
.getElementsByTagName('IMG');
} else if (document.all) {
var x = document.all['mouseovers'].all.tags('IMG');
} else {
return;
}
for (var i = 0; i < x.length; i++) {
console.log("for loop adding onmouseovers is being called");
x[i].onmouseover = displayAssetDescriptionTip();
}
loadImages(sources, drawImage);
drawGameElements();
drawDescriptionBoxes();
stage.add(imagesLayer);
};
I tried moving the if statements into a function called displayAssetDescriptionTip(), and this function now looks like this:
function displayAssetDescriptionTip() {
if (document.getElementById) {
var x = document.getElementById('mouseovers')
.getElementsByTagName('IMG');
} else if(document.all) {
var x = document.all['mouseovers'].all.tags('IMG');
}else {
return;
}
for (var i = 0; i < x.length; i++) {
console.log("for loop adding onmouseovers is being called");
x[i].onmouseover = displayAssetDescriptionTip();
}
document.getElementById('tipsParagraph').innerHTML = "Assets are items that"
+ " can be bought or sold for cash.";
console.log("displayAssetDescriptionTip being called");
}
However, the onmouseover event doesn't appear to be firing when I hover the cursor over the images to which it's been added- any ideas why this is?
getElementByTagName is not a function.
getElementsByTagName is though :)
It's plural because it returns every element that matches the given tag.

KendoUI PanelBar remember expanded items

I try implement Kendo UI PanelBar (see http://demos.kendoui.com/web/panelbar/images.html) If I open some items (Golf, Swimming) and next click to "Videos Records", I have expanded items. But when I do refresh page (click on some link), all expanded structure is lost.
On KendoUI forum I found, that I can get only possition of selected item and after reload page I must calculate all noded. Is there any way, how can I have expanded items in my situation? If do not need, I don't want to use the html frames.
Best regards,
Peter
Thank you for your answer, was very usefull. I add here code of skeleton of jQuery which remember 1 selected item now. Required add jquery.cookie.js [https://github.com/carhartl/jquery-cookie]
function onSelect(e) {
var item = $(e.item),
index = item.parentsUntil(".k-panelbar", ".k-item").map(function () {
return $(this).index();
}).get().reverse();
index.push(item.index());
$.cookie("KendoUiPanelBarSelectedIndex", index);
//alert(index);
}
var panel = $("#panelbar").kendoPanelBar({
select: onSelect
}).data("kendoPanelBar");
//$("button").click(function () {
// select([0, 2]);
//});
function select(position) {
var ul = panel.element;
for (var i = 0; i < position.length; i++) {
var item = ul.children().eq(position[i]);
if (i != position.length - 1) {
ul = item.children("ul");
if (!ul[0])
ul = item.children().children("ul");
panel.expand(item, false);
} else {
panel.select(item);
}
}
}
// on page ready select value from cookies
$(document).ready(function () {
if ($.cookie("KendoUiPanelBarSelectedIndex") != null) {
//alert($.cookie("KendoUiPanelBarSelectedIndex"));
var numbersArray = $.cookie("KendoUiPanelBarSelectedIndex").split(',');
select(numbersArray);
}
else {
// TEST INIT MESSAGE, ON REAL USE DELETE
alert("DocumenReadyFunction: KendoUiPanelBarSelectedIndex IS NULL");
}
});
The opening of the panels happens on the client. When the page is refreshed, the browser will render the provided markup, which does not include any additional markup for the selected panel.
In order to accomplish this, you will need to somehow store a value indicating the opened panel. The easiest way to accomplish this would be with a cookie (either set by JavaScript or do an AJAX call to the server).
Then, when the panelBar is being rendered, it will use the value in the cookie to set the correct tab as the selected one.
You can use this block to work withe the selected. in this example, i am just expanding the panel item. You can do other things such as saving panel item in your dom for later use or may be saving it somewhere to use it later:
var panelBar = $("#importCvPanelbar").data("kendoPanelBar");
panelBar.bind("select", function(e) {
var itemId = $(e.item)[0].id;
panelBar.expand(itemId);// will expand the selected one
});

Resources