How to tell parent to pop current modal and redirect to NavigationPage - xamarin

I have a Xamarin application, where my MainPage is a navigationpage (contentpage). In this navigationpage I open modals.
In the modals I open in my MainPage, I want to use Navigation.PushAsync but this requires the NavigationPage context which a modal page is not.
I've read that the flow would be:
Tell parent/caller to pop current visible modal
Tell parent/caller
to navigate to the page I want, using Navigation.PushAsync
However, how do you do this? How do you tell the parent to do this and actually do the navigation?

I would use event handlers to get this job done:
Here is an example code:
App.cs
public class App : Application
{
public App()
{
MainPage = new NavigationPage(new PushedNavigationPage());
}
}
PushedNavigationPage Page:
public class PushedNavigationPage: ContentPage
{
public PusehdNavigationPage()
{
}
public void DoPushModalOnButtonClick()
{
var pushedModalPage = new PushedModalPage();
pushedModalPage.DoPush += HandleDoPush;
Navigation.PushModalAsync(pushedModalPage);
}
private void HandleDoPush(object sender, EventArgs e)
{
Navigation.PopModalAsync();
Navigation.PushAsync(//the page you want to push)
}
}
PushedModalPage
public class PushedModalPage : ContentPage
{
public event EventHandler DoPush;
//call this on putton click or whenever you want
private void OnDoPush()
{
if (DoPush!= null)
{
DoPush(this, EventArgs.Empty);
}
}
}
I have not awaiting the async methods but it is better to do so and this is just an example, but I think you should get the idea..

Related

Override Manifest file using xamarin android

Is there any possibility of the overriding manifest with a view to set at run time after the user logs in?
You could add it in your App.xaml.cs.
[Application(UsesCleartextTraffic =true)]
public partial class App : Application
{
public App()
{
InitializeComponent();
MainPage = new NavigationPage(new Page2());
}
}

How to disable Carousel Page scrolling in Android

Using a custom renderer one can disable the swiping gesture of an CarouselPage on iOS like so:
using UIKit;
using Xamarin.Forms;
using Xamarin.Forms.Platform.iOS;
[assembly: ExportRenderer(typeof(CarouselPage), typeof(CustomCarouselPageRenderer))]
namespace App.iOS
{
public class CustomCarouselPageRenderer : CarouselPageRenderer
{
protected override void OnElementChanged(VisualElementChangedEventArgs e)
{
base.OnElementChanged(e);
UIView view = this.NativeView;
UIScrollView scrollView = (UIKit.UIScrollView)view.Subviews[0];
scrollView.ScrollEnabled = false;
}
}
}
How to accomplish the same on Android?
using Android.Content;
using XamFormsApp.Droid.Renderers;
using Xamarin.Forms;
using Xamarin.Forms.Platform.Android;
[assembly: ExportRenderer(typeof(CarouselPage), typeof(CustomCarouselPageRenderer))]
namespace StixApp.Droid.Renderers
{
public class CustomCarouselPageRenderer : VisualElementRenderer<CarouselPage>
{
public CustomCarouselPageRenderer(Context context) : base(context)
{
}
protected override void OnElementChanged(ElementChangedEventArgs<CarouselPage> e)
{
base.OnElementChanged(e);
var view = this.RootView;
X
X
}
}
}
There appears to be no way to access Subviews in the same way. One can access Children like so
Android.Views.View view = (Android.Views.View)GetChildAt(i);
How to know which Child is ScrollView if any?
Using a loop to check for this, like so,
for (int i = 0; i < ChildCount; ++i)
{
Android.Views.View view = (Android.Views.View)GetChildAt(i);
if (view is ScrollView)
{
}
}
Yields the following: "The given expression is never of the provided (ScrollView) type"
So! How to disable CarouselPage swipe/scrolling as is done in iOS quite elegantly?
UPDATE: Please see sample solution.
A couple of things.
For Android the view you are looking for is not a ScrollView but a ViewPager.
This can be found under the index 0 with the GetChildAt method.
Also, why are you using VisualElementRenderer<CarouselPage> as the parent class of your CustomCarouselPageRenderer. Instead use the CarouselPageRenderer as you did with iOS.
One last thing is that on Android the Scroll of a ViewPager cannot be disabled. To get this behavior you can listen to the Touch event. Setting the Handled property of TouchEventArgs to true will prevent the scrolling from happening.
Your whole class would look something like:
[assembly: ExportRenderer(typeof(CarouselPage), typeof(CustomCarouselPageRenderer))]
namespace StixApp.Droid.Renderers
{
public class CustomCarouselPageRenderer : CarouselPageRenderer
{
private bool _canScroll = false;
public CustomCarouselPageRenderer(Context context) : base(context)
{
}
public CustomCarouselPageRenderer()
{
}
protected override void OnElementChanged(ElementChangedEventArgs<CarouselPage> e)
{
base.OnElementChanged(e);
if (this.ChildCount > 0 && this.GetChildAt(0) is ViewPager viewPager)
{
viewPager.Touch -= ViewPagerTouched;
viewPager.Touch += ViewPagerTouched;
}
}
private void ViewPagerTouched(object sender, TouchEventArgs e)
{
e.Handled = !_canScroll;
}
}
}
Just change the value of _canScroll to true to allow the scrolling.
Hope this helps.-
Overridden Methods in ViewPager class:
public class NonSwipeableViewPager : ViewPager
{
public override bool OnTouchEvent(MotionEvent e)
{
return false;
}
public override bool OnInterceptTouchEvent(MotionEvent ev)
{
return false;
}
public override bool ExecuteKeyEvent(KeyEvent ev)
{
return false;
}
}
Changes to CarouselPageRenderer:
In class declaration:
public class MyCarouselPageRenderer : VisualElementRenderer<CarouselPage>
{
NonSwipeableViewPager _viewPager;
}
In OnElementChanged:
protected override void OnElementChanged(ElementChangedEventArgs<CarouselPage> e)
{
_viewPager = new NonSwipeableViewPager(Context);
}
Note: CarouselPageAdapter, CarouselPageRenderer, MeasureSpecFactory, ObjectJavaBox, and PageContainer all had to be copied from the Xamarin.Forms github to enable a custom CarouselPageRenderer implementation. All of this is in the github sample but hopefully this spells it out more clearly for future visitors.
Note2: I would like to stress that the true behavior we were trying to achieve is probably best done with a NavigationPage as this allows us to easily pop and push any and all pages at any time without having to address the swipe issue. That being said, hopefully this solution serves to aid anyone who need this behavior on a CarouselPage.

Cant find page in Xamarin.forms

i have some trouble finding the page that i wanna navigate to in Xamarin.forms. I've been looking everywhere and it seems i'm the only one experiencing the issue.
I have these 3 files:
And yet i keep getting an error on the new HomePage (could not be found). The class is right there, it just cant find it..
public partial class MainPage : ContentPage
{
public MainPage()
{
InitializeComponent();
}
void LoginButton_Clicked(object sender, EventArgs e)
{
bool isIdEmpty = String.IsNullOrEmpty(idEntry.Text);
bool isPasswordEmpty = String.IsNullOrEmpty(passwordEntry.Text);
if(isIdEmpty || isPasswordEmpty)
{
} else
{
Navigation.PushAsync(new HomePage());
}
}
}
}
Check to see if both pages are in the same namespace. For instance, if MainPage is in the YourApp namespace:
namespace YourApp
{
public class MainPage
{
// ... Your code
}
}
and the HomePage is in the YourApp.Pages namespace, then you need to add a using statement to the top of your MainPage:
using YourApp.Pages;
// Probably other ones here
namespace YourApp
{
public class MainPage
{
// ... Your code
}
}
Or, you can specify the full namespace in your declaration: Navigation.PushAsync(new YourApp.Pages.HomePage());

ObservableCollection made of strings Not updating

Ok, so, I'm trying to link an ObservableCollection from my Android project to my Cross-Platform Project::
I've got this so far...this is in my Cross-platform app
ObservableCollection<String> NewRef = DependencyService.Get<ISlateBluetoothItems>().test().testThing;
NewRef.CollectionChanged += TestThing_CollectionChanged;
listView.ItemsSource = NewRef;
private void TestThing_CollectionChanged(object sender, System.Collections.Specialized.NotifyCollectionChangedEventArgs e)
{
listView.ItemsSource = DependencyService.Get<ISlateBluetoothItems>().test().testThing;
Console.WriteLine("working");
}
The line "working" is never printed even if I make changes to the ObservableCollection on the android portion of my app...
Here's the interface I'm using for the DependencyService:
using System.Collections.ObjectModel;
namespace ThoughtCastRewrite.BluetoothX
{
public interface ISlateBluetoothItems
{
BluetoothItems test();
}
}
Here's the class I use to expose the list:
namespace ThoughtCastRewrite.BluetoothX
{
public class BluetoothItems
{
public ObservableCollection<String> testThing;
public BluetoothItems()
{
testThing = new ObservableCollection<String>();
testThing.Add("wtf?");
}
public void AddThis()
{
testThing.Add("ok");
}
}
}
This is in the Android portion of my app, it implements the ISlateBluetoothItems interface
BluetoothItems bluetoothItems = new BluetoothItems();
then I call
bluetoothItems.AddThis();
but "ok" is not added to my list! I don't get the CollectionChanged event firing off! What's the deal guys? What's the deal?
You should assign your ObservableCollection as a source of your listview only once, not after each change. Changes to the collection will be automaticcly propagated to the listview.

Xamarin Forms WebView.CanGoBack always returns false on UWP

Using the Xamarin Forms WebView control, I'm overriding the OnBackButtonPressed() and finding that the CanGoBack always returns false in UWP.
I don't see this problem in Android.
Is this a XF bug or am I doing something wrong?
Note: I'm running XF v2.3.3.193
EDIT: I upgraded to XF 2.3.4.247 and the problem persists.
I have created a code sample and reproduce your issue when the WebView browse several website. And I have found reason in the Xamarin.Forms source code.
void UpdateCanGoBackForward()
{
((IWebViewController)Element).CanGoBack = Control.CanGoBack;
((IWebViewController)Element).CanGoForward = Control.CanGoForward;
}
The CanGoBack property will be changed when UpdateCanGoBackForward method invoked. And UpdateCanGoBackForward method was called only when the native NavigationCompleted event was invoked. So if some website could not be loaded quickly, the CanGoBack property would not be changed.
You can improve this design by custom WebView. And you could follow the code below.
CustomWebView.cs
Add the new property for CustomWebView.
public class CustomWebView : WebView
{
public bool CCanGoBack { get; set; }
public CustomWebView()
{
}
}
CustomWebViewRenderer.cs
And change the property when the ContentLoading event invoked.
[assembly: ExportRenderer(typeof(CustomWebView), typeof(CustomWebViewRenderer))]
namespace CustomWebViewTest.UWP
{
public class CustomWebViewRenderer : WebViewRenderer
{
protected override void OnElementChanged(ElementChangedEventArgs<WebView> e)
{
base.OnElementChanged(e);
if (Control != null)
{
Control.ContentLoading += Control_ContentLoading;
}
}
private void Control_ContentLoading(Windows.UI.Xaml.Controls.WebView sender, Windows.UI.Xaml.Controls.WebViewContentLoadingEventArgs args)
{
(Element as CustomWebView).CCanGoBack = Control.CanGoBack;
}
}
}
MainPage.cs
private void backClicked(object sender, EventArgs e)
{
if (Browser.CCanGoBack)
{
Browser.GoBack();
}
}

Resources