Webview Session Storage lost when navigate between Xamarin pages - xamarin

I have a Xamarin.Forms App with Two Pages, one with a WebView and other with just text.
Session Storage lost data when I navigate between pages.
//APage.xaml
<WebView x:Name="webView" WidthRequest="1000" HeightRequest="1000" Navigated="webView_Navigated">
<WebView.Source>
<UrlWebViewSource />
</WebView.Source>
</WebView>
//BPage.xaml
<Span Text="Hi B Page"/>
//APage.xaml.cs
...
public BPage()
{
InitializeComponent();
(webView.Source as UrlWebViewSource).Url = "mytestsite.com";
}
private async void webView_Navigated(object sender, WebNavigatedEventArgs e)
{
var webView = sender as WebView;
var token = await webView.EvaluateJavaScriptAsync("window.sessionStorage.getItem('token')");
System.Console.WriteLine(token); //Every time I Go to BPage and back, it is null.
}
My website return a "Token not found in sessionstorage" error.

//Every time I Go to B Page and back, it is null.
When you navigate from B to A, page B will be released and every time you navigate from A to B, you are navigating to a new PageB(not the same PageB every time).
Each webView you use is under different Page B and that's why you get null.
Here is the document about Navigation.
You can use Xamarin.Essentials: Preferences to save token in apps.
Save:
Preferences.Set("my_key", "my_value");
Read:
var myValue = Preferences.Get("my_key", "default_value");

Related

UI freezes for 2-3 seconds when loading a carouselview

I have a carouselview binded to a viewmodel, on the previous page (call it first page) user can select 2 arguments and with the help of those, the next view (call it second page) is generated accordingly. However, I can't wrap my head around why my view won't load asynchronously.
So my problem: When I click the button on the first page the UI would freeze for like a solid 2-3 seconds, and then start load (asynchronously?) and once it's done it's all good.
Also I couldn't really figure out a better way to inherit values from first page to second so if someone has an idea please let me know.
Any help on how can I fix this I would really appreciate.
The viewmodel for second page
public class DataSelectionViewModel : BaseViewModel
{
public ObservableCollection<Items> FilteredData { get; set; }
public UserSelectionViewModel()
{
_dataStore = DependencyService.Get<IDataStore>();
LoadData= new AsyncAwaitBestPractices.MVVM.AsyncCommand(FilterData);
FilteredData = new ObservableRangeCollection<Items>();
}
public async Task FilterData()
{
FilteredData.Clear();
var filtereddata = await _dataStore.SearchData(Hard, Subject).ConfigureAwait(false);
foreach (var data in filtereddata)
{
FilteredData.Add(data);
}
}
}
The carouselview in second page
<ContentPage.BindingContext>
<db:DataSelectionViewModel/>
</ContentPage.BindingContext>
...
<!-- DataTemplate for carouselview has radiobuttons, label and button all in a grid -->
<CarouselView ItemsSource="{Binding FilteredData}">
Second Page c#
public partial class SecondPage : ContentPage
{
public Coll(bool hard, string subject)
{
InitializeComponent();
var vm = (BaseViewModel)BindingContext;
vm.Hard = hard;
vm.Subject = subject;
/* had to set "hard" and "subject" here again, otherwise data won't load */
}
protected override async void OnAppearing()
{
var vm = (DataSelectionViewModel)BindingContext;
base.OnAppearing();
await vm.LoadData.ExecuteAsync().ConfigureAwait(false);
}
}
The first page view containing the button
<Button x:Name="Start" Pressed="ButtonClick"/>
First page c# --> Here I also tried doing it with a command and a pressed at the same time, because I couldn't come up with a way to save variables to second page viewmodel, that's why I use pressed here
private async void ButtonClick(object sender, EventArgs e)
{
var vm = (BaseViewModel)BindingContext;
vm.Hard = HardButtonSelected == Hard;
vm.Subject = vm.Subject.ToLower();
await Navigation.PushAsync(new SecondPage(vm.Hard, vm.Subject));
}
I have tried not using the OnAppearing method to get my data, but then it wouldn't bind to the page and it would not show, if I were to previously fill the ObservableCollection with my data and then load the page although I would love to be able to do this because it would allow me to create a loading popup also.

How do I close all pages after changing an item's data?

I have a doubt in Xamarin forms, I have an application that I would like after finishing the training, it closes all the training screens and only the main screen remains, today I got a cod that does this, but when I restart the app and I login,
when he closes the screens instead of going back to the main screen he goes back to the login, I'm not using masterpage, could that be it?
source:
private async void BtnVoltar_Clicked(object sender, EventArgs e)
{
int numModals = Application.Current.MainPage.Navigation.ModalStack.Count;
// Pop each modal in the stack
for (int currModal = 0; currModal < numModals; currModal++)
{
await Application.Current.MainPage.Navigation.PopModalAsync();
}
await Navigation.PushModalAsync(new TabbedPageMenu());
}
Image Link
https://imgur.com/pOVVYBu
Another doubt would be after updating the data of an item, if it is possible to close the two screens and update it on the main screen
image link2
https://imgur.com/ZpevVNg
You can use the method to navigate to the different pages instead of close the pages.
The code in xaml you can add the command to do the navigation.
Command="{Binding NavigateCommand}"
CommandParameter="{x:Type views:targetpagename}"
The code in xaml.cs or viewmodel
public ICommand NavigateCommand { get; private set; }
public MainPage()
{
InitializeComponent();
NavigateCommand = new Command<Type>(
async (Type pageType) =>
{
Page page = (Page)Activator.CreateInstance(pageType);
await Navigation.PushAsync(page);
});
BindingContext = this;
}

ZXingScannerView throws A method was called at an unexpected time caused by StopScanningAsync when navigating back

Using Xamarin Forms 3.6 with Prism, Fody and ZXing and running UWP app.
A navigation stack of MainPage/Navigation Page/ViewA/ViewB/ViewC?selectedTab=Tab3.
Tab3 has a ZXingScannerView control on it.
If I try to back button away to ViewB then the app crashes with the error 'A method was called at an unexpected time.'
The exception shows that it is trying to wait for the ZXing StopScanningAsync() method.
The page sequence is this:
MainPage which is a Master Detail showing View A initially.
View A -> View B -> View C which is a Tabbed Page
On the tab are 4 views defined as Content Pages, all navigable with Tab1 shown initially.
Tab1 is a simple data entry form
Tab2 has an input field, a button and an instance of CustomControl
Tab3 has a button,a ZXingScannerView control and an instance of CustomControl
Tab4 has a button, an Image and an instance of CustomControl
So when the user arrives at ViewC the navigation uri should be MainPage/Navigation Page/ViewA/ViewB/ViewC?selectedTab=Tab1
The problem is the back button action.
Using Prism Navigation I can trap the OnNavigatedFrom, OnNavigatingTo, OnNavigatedTo.
None of these events occur when moving between tabs.
There is a problem with the ZXingScannerView on Tab3.
I can click on each of the tabs and go forward and backward to show each page.
The back button (back to ViewB) works as long as I do NOT display Tab3 with the ZXing view. Scanning must be initiated by clicking the button so the control has IsScanning=false when the page is loaded.
If I tab to Tab3 (and any other tab before or after) and then try to back button away to ViewB then the app crashes with the error 'A method was called at an unexpected time.'
The exception shows that it is trying to wait for the ZXing StopScanningAsync() method.
The markup is this:
<Grid
HorizontalOptions="FillAndExpand"
IsVisible="{Binding IsScanning}"
VerticalOptions="FillAndExpand">
<zxing:ZXingScannerView
x:Name="zxing"
IsAnalyzing="{Binding IsAnalyzing}"
IsScanning="{Binding IsScanning}"
ScanResultCommand="{Binding OnScanResult}" />
<zxing:ZXingDefaultOverlay
BottomText="Place the red line over the barcode you'd like to scan."
HeightRequest="20"
TopText="Accepts EAN-8 or EAN-13 barcodes" />
</Grid>
And the view model code is like this:
public Tab3PageViewModel(INavigationService navigationService,
IEventAggregator eventAgregatorService,
IPageDialogService dialogService
)
: base(navigationService, eventAgregatorService)
{
_DialogService = dialogService;
this.IsScanning = false;
this.IsAnalyzing = false;
}
public void Handle_BeginScan()
{
IsScanning = true;
IsAnalyzing = true;
}
public void Handle_OnScanResult(Result result)
{
MainThread.BeginInvokeOnMainThread(async () =>
{
if (result != null)
{
var payload = new PublishedEventPayload()
{
Code = result.Text,
ViewName = this.ViewName
};
this.AgregatorService.GetEvent<FindByBarcodeEvent>().Publish(payload);
}
else
{
await _DialogService.DisplayAlertAsync("Not found", "Unfortunately the ISBN code could not be resolved. Please try again.", "OK");
}
});
}
On Tab3 I have put an OnNavigatedFrom event to ensure that IsScanning= false, but this made no difference.
The app would crash before it got to that.
Prism Navigation Service says that it will add new views to the stack when NavigateAsync() is called.
I have tried explicitly setting the full navigation path when going from ViewB to ViewC with no effect on the problem. The nav stack seems to be right when viewed.
It seems to be a navigation issue but I can't work out why the ZXing control is interferring.
Any suggestions anyone?
I'm not sure if this will help or not, but I've run into issues where ZXingScanningView does not respect setting IsAnalyzing and IsScanning when navigating. I was running into an issue that saw the camera continue to scan barcodes even after navigating from the page. It seems your view is also continuing to scan. My solution was to completely remove the ZXingScanningView in the OnDisappearing method then to re-add it in OnAppearing if needed.
Ex. I have my ZXScanningView housed in a grid that simply contains the ZXingScanningView and ZXingDefaultOverlay. When I set own my bindable property ScanningEnabled to false it will clear all children of the grid.

Is it possible to use modal page in Tabbed pages in Xamarin Forms?

As what my title says, is it possible to use modal page when you click a tab in Xamarin Forms?
What I would like to do it when I click one of my 5 tabs it would open a Modal page. If it's possible can someone please show my some example of how it is done or point me to the right way.
Sure, it is possibile but a bit tricky.
Create a blank page (for example FakePage) and add it to the TabbedPage.Children
In the TabbedPage code behind override OnCurrentPageChanged, check when your fakepage is selected, then open a Modal.
When the modal is opened tell your TabbedPage to go back to the previous tab (so when the user closes the modal it doesnt see a blank page)
Your tabbed page childrens
<views:Page1 Title="First tab" />
<views:Page2 Title="Second Tab" />
<views:FakePage Title="Modal tab" />
Tabbed page code behind
private Page _lastPage;
protected override async void OnCurrentPageChanged()
{
if (CurrentPage is FakePage)
{
await Navigation.PushModalAsync(new YourModalPage());
//Wait a bit for the modal to open, then reselect the last tab
//This is useful when a user close the modal: you dont want to show a blank page
await Task.Delay(150);
CurrentPage = _lastPage;
}
else
{
_lastPage = CurrentPage;
}
base.OnCurrentPageChanged();
}
Going off of what Giampaolo said, the following method might be a bit smoother:
protected override async void OnCurrentPageChanged()
{
if (CurrentPage is FakePage)
{
CurrentPage = _lastPage;
await Navigation.PushModalAsync(new YourModalPage());
}
else
{
_lastPage = CurrentPage;
}
base.OnCurrentPageChanged();
}
That way, you won't ever see the blank fake page during the transition.

Showing different toolbar buttons on each page with Xamarin Forms

I have 2 pages in my Xamarin Forms app. My first page has 4 icons in the toolbar. My second page is a login page and has a tick and a cross in the toolbar.
I can't get the login page to show any icons unless I make it a navigation page. I also have to clear ToolBarItems on the first page before calling PushAsync() otherwise it complains there are too many toolbar items.
If I call PopAsync() on the login page it does not return to the first page. I'm guessing this is due to their being 2 navigation pages. I also tried PopToRootAsync().The back button works however.
My question is - how do I show different toolbar icons on 2 different pages in a way that allows navigation to work?
I'm testing this on Windows Phone 8.0
Here is the code calling the login page:
private async void ShowLoginPage()
{
ToolbarItems.Clear();
var page = new NavigationPage(new LoginPage());
await Navigation.PushAsync(page);
}
and here is the code to return to the first page:
private void Cancel()
{
Navigation.PopToRootAsync();
}
I'm running Xamarin.Forms v1.2.2.6243
One thing you could try is to keep your Login Page inside of a NavigationPage, and then instead of running PopAsync() within the Login Page after they have logged in successfully, simply replace the MainPage with your old Navigation page:
In your App class:
public NavigationPage AppNavPage = new NavigationPage(new FirstPage());
public App() {
MainPage = AppNavPage;
}
In your FirstPage:
private async void ShowLoginPage() {
ToolbarItems.Clear();
var page = new NavigationPage(new LoginPage());
await Navigation.PushAsync(page);
}
In Login Page:
private async void OnCreateClicked(object sender, EventArgs e) {
bool loginInfoIsGood = CheckLoginInfo(); //Check their login info
if(loginInfoIsGood) {
Application.Current.MainPage = App.AppNavPage;
}
}
Otherwise, I have also done a custom renderer for the NavigationRenderer on iOS to insert toolbar items onto the right side of the Navigation Bar and have overridden some Menu related stuff on Android to change the icon text/colors.
One option that you have, and one that I implemented in my own app, is a custom renderer that removes the navigation header from the app and then you could build your own custom header. With this approach, you do lose some of the native feel of the app, and you have to implement much of the transitional functionality your self. However, it gives you alot more control over the look.
CustomRenderer that removes the navigationBar:
//add using statements
// add all view here that need this custom header, might be able to build a
//base page that others inherit from, so that this will work on all pages.
[assembly: ExportRenderer(typeof(yourView), typeof(HeaderRenderer))]
class HeaderRenderer : PageRenderer
{
public override void ViewWillAppear(bool animated)
{
base.ViewWillAppear(animated);
this.NavigationController.SetNavigationBarHidden(true, true);
}
}
After this you can build a header view that can be placed on the top of every page (I am using xaml) so I don't know if it is relevant in you application.
Edit: You might need to change this renderer for differnt page types.

Resources