XamarinForms: how use a Custom Navigation Page - xamarin

I order to have a cutom navigation on a specific view on my app,
I create a Custom Navigation ios:
public class CustomNavigationPageRenderer : NavigationRenderer
{
public override void ViewDidLoad()
{
base.ViewDidLoad();
UINavigationBar.Appearance.SetBackgroundImage(new UIImage(), UIBarMetrics.Default);
UINavigationBar.Appearance.ShadowImage = new UIImage();
UINavigationBar.Appearance.BackgroundColor = UIColor.Clear;
UINavigationBar.Appearance.TintColor = UIColor.White;
UINavigationBar.Appearance.BarTintColor = UIColor.Clear;
UINavigationBar.Appearance.Translucent = true;
}
}
and a common interface class:
public partial class CustomPage : NavigationPage
{
public CustomPage(): base()
{
InitializeComponent();
}
public CustomPage(Page root) : base(root)
{
InitializeComponent();
}
public bool IgnoreLayoutChange { get; set; } = false;
protected override void OnSizeAllocated(double width, double height)
{
if (!IgnoreLayoutChange)
base.OnSizeAllocated(width, height);
}
}
Now, In my specific view, how have I use it?
I need to set on false the original navigation? (NavigationPage.SetHasNavigationBar(this, false);​)
public MySpecificViewNeedCustoNAv()
{
CustomPage myNavigationPage = new CustomPage();
...

need to set on false the original navigation?
No , you can refer the following code.
For example
Creating the Root Page in App.cs
public App ()
{
MainPage = new CustomPage (new xxxpage());
}
Pushing Pages to the Navigation Stack in your page
async void ButtonClicked (object sender, EventArgs e)
{
await Navigation.PushAsync (new xxxpage());
}

Related

CarouselPage change current page

App.xaml.cs
MainPage = new NavigationPage(new Home());
Home.xaml.cs
public partial class Home : CarouselPage
{
public Home()
{
Children.Add(new CarPage1());
Children.Add(new CarPage2());
CurrentPage = new CarPage2();
}
}
CarPage2.xaml.cs
public partial class CarPage2 : ContentPage
{
public CarPage2()
{
InitializeComponent();
}
private void Btn_Clicked(object sender, EventArgs e)
{
//Do something
//I wanna change screen to CarPage1 without use Navigation.PushAsync(new CarPage1());
}
}
How to change current screen to CarPage1 not use Navigation.PushAsync ?
I have done a sample to test and setting the CurrentPage property worked correctly, you can try the following code:
public partial class CarPage2 : ContentPage
{
public CarPage2()
{
InitializeComponent();
}
private void Btn_Clicked(object sender, EventArgs e)
{
var home = this.Parent as CarouselPage;
home.CurrentPage = home.Children[0];
}
}

Xamarin Forms Map not adding pins on new pin created

I have a Xamarin Forms map that shows a collection of Pins. When I add a new location it doesn't show the newly created pin until I click 'Refresh'.
I originally had the AddLocation subscribe in the constructor of the ViewModel and it worked, but added the pin 3 times to the database and map. Its since moving the AddLocation subscribe and unsubscribe to their own methods in the ViewModel and calling then OnAppearing() and OnDisappearing() that the new pin doesnt show.
The map is on a page called ItemPage.xaml
<ContentPage.ToolbarItems>
<ToolbarItem Text="Add" Clicked="ToolbarItem_Clicked" />
<ToolbarItem Text="Refresh" Clicked="ToolbarItem_Clicked_1" />
</ContentPage.ToolbarItems>
<ContentPage.Content>
<!--The map-->
<ContentView Content="{Binding Map}" />
</ContentPage.Content>
and the Code Behind -
[DesignTimeVisible(false)]
public partial class ItemPage : ContentPage
{
ItemViewModel viewModel;
public ItemPage()
{
InitializeComponent();
BindingContext = viewModel = new ItemViewModel();
}
private void Button_Clicked(object sender, EventArgs e)
{
}
protected override void OnAppearing()
{
base.OnAppearing();
}
async void ToolbarItem_Clicked(object sender, EventArgs e)
{
await Navigation.PushModalAsync(new NavigationPage(new NewLocationPage()));
}
private void ToolbarItem_Clicked_1(object sender, EventArgs e)
{
viewModel.LoadLocationsCommand.Execute(null);
}
}
The View Model -
public class ItemViewModel : BaseViewModel
{
public ObservableCollection<MapPinModel> Locations { get; set; }
public ObservableCollection<ItemModel> Items { get; set; }
public Command LoadLocationsCommand { get; set; }
public Command LoadItemsCommand { get; set; }
public Map Map { get; private set; }
public ItemViewModel()
{
Title = "Items";
Locations = new ObservableCollection<MapPinModel>();
Items = new ObservableCollection<ItemModel>();
LoadLocationsCommand = new Command(async () => await ExecuteLoadLocationsCommand());
LoadItemsCommand = new Command(async () => await ExecuteLoadItemsCommand());
Map = new Map(MapSpan.FromCenterAndRadius(
new Xamarin.Forms.Maps.Position(53.70251232638285, -1.8018436431884768),
Distance.FromMiles(0.5)))
{
IsShowingUser = true,
VerticalOptions = LayoutOptions.FillAndExpand
};
this.LoadLocationsCommand.Execute(null);
}
public void UnsubscribeMessages()
{
MessagingCenter.Unsubscribe<NewLocationPage, ItemLocationModel>(this, "AddLocation");
}
public void SubscribeMessages()
{
MessagingCenter.Subscribe<NewLocationPage, ItemLocationModel>(this, "AddLocation", async (obj, item) =>
{
await ItemDataStore.AddItemLocationAsync(item);
});
}
async Task ExecuteLoadItemsCommand()
{
if (IsBusy)
return;
try
{
Items.Clear();
var items = await ItemDataStore.GetItemsAsync(true);
foreach (var item in items)
{
Items.Add(item);
}
}
catch (Exception ex)
{
Debug.WriteLine(ex);
}
finally
{
IsBusy = false;
}
}
async Task ExecuteLoadLocationsCommand()
{
Map.Pins.Clear();
var locations = await ItemDataStore.GetLocationsAsync(true);
foreach (Feature feature in locations.Features)
{
if (!feature.Geometry.Type.Equals(GeoJSONObjectType.Point))
continue;
Point point = feature.Geometry as Point;
GeoJSON.Net.Geometry.Position s = point.Coordinates as GeoJSON.Net.Geometry.Position;
MapPinModel pin = new MapPinModel
{
PopupContent = feature.Properties["popupContent"].ToString(),
ScientificName = feature.Properties["scientificname"].ToString(),
ItemDescription = feature.Properties["description"].ToString(),
AddedBy = feature.Properties["username"].ToString(),
Latitude = s.Latitude.ToString(),
Longitude = s.Longitude.ToString()
};
Xamarin.Forms.Maps.Position position = new Xamarin.Forms.Maps.Position(Convert.ToDouble(pin.Latitude), Convert.ToDouble(pin.Longitude));
Pin newPin = new Pin
{
Label = pin.PopupContent,
Type = PinType.Place,
Position = position
};
Map.Pins.Add(newPin);
}
}
}
Finally the New Location page -
[DesignTimeVisible(false)]
public partial class NewLocationPage : ContentPage
{
ItemViewModel viewModel;
public ItemLocationModel Location { get; set; }
public NewLocationPage()
{
InitializeComponent();
Location = new ItemLocationModel();
BindingContext = viewModel = new ItemViewModel();
BindingContext = this;
viewModel.SubscribeMessages();
}
async void Save_Clicked(object sender, EventArgs e)
{
var location = await Geolocation.GetLastKnownLocationAsync();
Location.Latitude = location.Latitude.ToString();
Location.Longitude = location.Longitude.ToString();
Location.DatePosted = DateTime.Now;
Location.ItemId = ((ItemModel)itemsPicker.SelectedItem).Id;
Location.SecretLocation = secretLocation.IsToggled;
MessagingCenter.Send(this, "AddLocation", Location);
await Navigation.PopModalAsync();
Location = null;
}
async void Cancel_Clicked(object sender, EventArgs e)
{
await Navigation.PopModalAsync();
}
protected override void OnAppearing()
{
viewModel.SubscribeMessages();
base.OnAppearing();
if (viewModel.Items.Count == 0)
viewModel.LoadItemsCommand.Execute(null);
itemsPicker.ItemsSource = viewModel.Items;
}
protected override void OnDisappearing()
{
viewModel.LoadLocationsCommand.Execute(null);
viewModel.UnsubscribeMessages();
base.OnDisappearing();
}
}

How can I share the value of a field between back-end C# and a renderer?

My C# looks like this:
public App()
{
InitializeComponent();
MainPage = new Japanese.MainPage();
}
public partial class MainPage : TabbedPage
{
public MainPage()
{
InitializeComponent();
var phrasesPage = new NavigationPage(new PhrasesPage())
{
Title = "Play",
Icon = "ionicons-2-0-1-ios-play-outline-25.png"
};
public partial class PhrasesPage : ContentPage
{
public PhrasesFrame phrasesFrame;
public PhrasesPage()
{
InitializeComponent();
NavigationPage.SetHasNavigationBar(this, false);
App.phrasesPage = this;
}
protected override void OnAppearing()
{
base.OnAppearing();
App.dataChange = true;
phrasesFrame = new PhrasesFrame(this);
phrasesStackLayout.Children.Add(phrasesFrame);
}
public partial class PhrasesFrame : Frame
{
private async Task ShowCard()
{
if (pauseCard == false)
..
and I have an iOS renderer for a tab page
public class TabbedPageRenderer : TabbedRenderer
{
private MainPage _page;
private void OnTabBarReselected(object sender, UITabBarSelectionEventArgs e)
{
...
pauseCard = false;
...
My problem is there is no connection between the two and I would like to know how I can make it so that pauseCard could be set in one place and read in another.
Here is a simple custom Entry example using a bindable bool property that gets changed from the renderer every time the text changes in the entry.
Entry subclass w/ a bindable property called OnOff (bool)
public class CustomPropertyEntry : Entry
{
public static readonly BindableProperty OnOffProperty = BindableProperty.Create(
propertyName: "OnOff",
returnType: typeof(bool),
declaringType: typeof(CustomPropertyEntry),
defaultValue: false);
public bool OnOff
{
get { return (bool)GetValue(OnOffProperty); }
set { SetValue(OnOffProperty, value); }
}
}
iOS Renderer
Note: I keep a reference to the instance of the CustomPropertyEntry passed into OnElementChanged so later I can set its custom property when needed.
public class CustomPropertyEntryRenderer : ViewRenderer<CustomPropertyEntry, UITextField>
{
UITextField textField;
CustomPropertyEntry entry;
protected override void OnElementChanged(ElementChangedEventArgs<CustomPropertyEntry> e)
{
base.OnElementChanged(e);
if (Control == null)
{
textField = new UITextField();
SetNativeControl(textField);
}
if (e.OldElement != null)
{
textField.RemoveTarget(EditChangedHandler, UIControlEvent.EditingChanged);
entry = null;
}
if (e.NewElement != null)
{
textField.AddTarget(EditChangedHandler, UIControlEvent.EditingChanged);
entry = e.NewElement;
}
}
void EditChangedHandler(object sender, EventArgs e)
{
entry.OnOff = !entry.OnOff;
}
}
XAML Example:
<local:CustomPropertyEntry x:Name="customEntry" Text="" />
<Switch BindingContext="{x:Reference customEntry}" IsToggled="{Binding OnOff}" />

How to navigate deeper inside a xamarin detailpage

I have a project with a MasterDetailPage as root page. When I navigate deeper inside the Detailpage, I have the problem, that the DetailPage navigation overrides my actionbar for the MasterPage. Can I have both in the actionbar, the burgermenuicon and the backbutton?
before navigation:
after navigation:
public partial class MasterPage : MasterDetailPage
{
public MasterPage()
{
Master = SetMasterContentPage();
Detail = new NavigationPage(new TaxonomyOverviewPage());
}
ContentPage SetMasterContentPage()
{
var masterPage = new ContentPage { Title = "Test"};
masterPage.Content = new StackLayout
{
Children = {
new Label{Text="Label1"},
new Label{Text="Label2"},
new Label{Text="Label3"}
}
};
return masterPage;
}
protected override void OnAppearing()
{
base.OnAppearing();
}
}
Problem is solved.
I used a public static MasterDetailPage and referenced it to the MainPage in App.cs. Now I can access the IsPresented property of the MasterDetailPage.
public partial class App : Application
{
public static MasterPage masterdetail;
public App()
{
InitializeComponent();
}
protected override void OnStart()
{
masterdetail = new MasterPage();
Device.BeginInvokeOnMainThread(() => {
MainPage = masterdetail;
});
}
}
Finally, I add a menuicon to the right side of the actionbar.
protected override void OnAppearing()
{
base.OnAppearing();
ToolbarItems.Add(new ToolbarItem("Menu", "menuicon.png", () => { App.masterdetail.IsPresented = true; }));
}

Xamarin forms navigation BarBackgroundColor issue

When I click on Login for the first time than the barbackground of mainpage becomes the right color, but when I logout and than login again the color of the barbackground doesn't change??
LoginPage (View)
public partial class LoginPage : ContentPage
{
LoginPageViewModel vm;
public LoginPage ()
{
vm = new LoginPageViewModel ();
BindingContext = vm;
InitializeComponent ();
}
public void OnClickLogin(object o, EventArgs e)
{
vm.Login ();
}
public void OnClickPasswoordVergeten(object o, EventArgs e)
{
vm.PasswoordVergeten ();
}
public void OnClickContactUs(object o, EventArgs e)
{
vm.ContactUs ();
}
}
LoginPageViewModel (ViewModel)
public class LoginPageViewModel: INotifyPropertyChanged
{
public LoginPageViewModel ()
{
}
public void Login()
{
App.Current.MainPage = new Dharma.MainPage();
}
}
MainPage (View)
public partial class MainPage : MasterDetailPage
{
public MainPage ()
{
InitializeComponent ();
masterPage.ListView.ItemSelected += OnItemSelected;
var page = new NavigationPage (new ListPage());
page.BarBackgroundColor = Color.FromRgb(26,179,148);
Detail = page;
masterPage.ListView.SelectedItem = null;
IsPresented = false;
}
void OnItemSelected (object sender, SelectedItemChangedEventArgs e)
{
var item = e.SelectedItem as MasterPageItem;
if (item != null) {
var page = new NavigationPage ((Page)Activator.CreateInstance (item.TargetType));
page.BarBackgroundColor = Color.FromRgb(26,179,148);
Detail = page;
masterPage.ListView.SelectedItem = null;
IsPresented = false;
}
}
}
Set the color to your master page.
public void Login()
{
var mainPage = new Dharma.MainPage();
mainPage.BarBackgroundColor = Color.FromRgb(26,179,148);
App.Current.MainPage = mainPage ;
}

Resources