Custom tab item - xamarin

I need to add a custom tab item into my TabbedPage. That shouldn't be a page but an overlay view. It is "More" item for the bottom menu opening a more items menu over the currently shown page.
So far I found the following solution:
protected override void OnCurrentPageChanged()
{
base.OnCurrentPageChanged();
if (this.isInitialized)
{
if (CurrentPage.Title == "More")
{
CurrentPage = this.lastSelectedPage;
}
}
this.lastSelectedPage = CurrentPage;
}
It's enough to prevent opening the corresponding fake page. After this I need to show an overlay view and have no item how to do it from the tabbed page.
Another solution I'm working out now is to write a custom renderer for my tabbed page and manage all the custom work there. The question in this case is how to show my custom view over the existing content. I tried AddView (iOS) in the renderer but getting runtime exception.
public override void ItemSelected(UITabBar tabbar, UITabBarItem item)
{
base.ItemSelected(tabbar, item);
var view = new UILabel()
{
Text = "Test"
};
View.Add(view);
}

Related

How to create a custom WebView Control Xamarin forms

I am trying to create a custom control for webview, i am trying to get a checkbox inside a webview Reason :- we have a bunch of text to be displayed and unless the user reaches the end of the scroll he cannot move to the next page and at the end of the scroll there is a checkbox where user has to check the the checkbox and then he can process. here i have tried putting the checkbox and webview inside the stacklayout but the issue is webview have its own scroll bar and and stacklayout scroll bar does not work when a user try to scroll as the webview scroller scrolls out also when i try to close the Webview Page with back button the webview gets close and not the page
i am not sure what approach should i apply here.
i am getting my html data from my webapi.
anyone with some solution would be appreciable
here is my custom renderer which i have wrote but the piece missing here is how can i add another xamarin control inside this
public class CustomPdfViewRenderer : WebViewRenderer
{
public CustomPdfViewRenderer(Context context) : base(context)
{
}
protected override void OnElementChanged(ElementChangedEventArgs<WebView> e)
{
base.OnElementChanged(e);
if (Control != null)
{
Control.Settings.BuiltInZoomControls = true;
Control.Settings.DisplayZoomControls = false;
Control.Settings.LoadWithOverviewMode = true;
Control.Settings.UseWideViewPort = true;
}
}
}
You can try the following approach:
Add checkbox to HTML
When the user check checkbox call some JavaScript function
When JavaScript function is called, call C# function (Xamarin) which will enable the user to process to the next page (or some other Xamarin side stuff)
Here is how you can call C# function from JavaScript :HybridWebView

How to hide navigation Toolbar icon in xamarin?

I want to hide navigation bar button in xamarin. how can i do that using binding. Toolbar item doesn't have "IsVisible" property.
Following is my xaml code
please help me to sort out this issue.
I would suggest to build a bindable ToolBoxItem. That way you can control the visibility through a view model property.
An implementation could look like that:
public class BindableToolbarItem : ToolbarItem
{
public static readonly BindableProperty IsVisibleProperty = BindableProperty.Create(nameof(IsVisible), typeof(bool), typeof(BindableToolbarItem), true, BindingMode.TwoWay, propertyChanged: OnIsVisibleChanged);
public bool IsVisible
{
get => (bool)GetValue(IsVisibleProperty);
set => SetValue(IsVisibleProperty, value);
}
private static void OnIsVisibleChanged(BindableObject bindable, object oldvalue, object newvalue)
{
var item = bindable as BindableToolbarItem;
if (item == null || item.Parent == null)
return;
var toolbarItems = ((ContentPage)item.Parent).ToolbarItems;
if ((bool)newvalue && !toolbarItems.Contains(item))
{
Device.BeginInvokeOnMainThread(() => { toolbarItems.Add(item); });
}
else if (!(bool)newvalue && toolbarItems.Contains(item))
{
Device.BeginInvokeOnMainThread(() => { toolbarItems.Remove(item); });
}
}
}
As you have discovered yourself there is not IsVisible. So you will have to implement functionality like that yourself if you still want it.
Another way would be to handle it in the pages' code-behind and remove or add the toolbar item whenever needed.
Adding and removing is simple, just add and remove items to the ToolbarItems collection: ToolbarItems.RemoveAt(0); for instance will remove the first toolbar item.
Putting #Gerald answer in action, it would be done this way:
void Done_Clicked(System.Object sender, System.EventArgs e)
{
//Do somthing and hide the done item
ShowDoneToolbarItem(false, (ToolbarItem)sender);
}
void Entry_Focused(System.Object sender, Xamarin.Forms.FocusEventArgs e)
{
//Show the done item
ShowDoneToolbarItem(true);
}
void ShowDoneToolbarItem(bool show, ToolbarItem item = null)
{
if(show)
{
ToolbarItem done = new ToolbarItem();
done.Text = "Done";
done.Clicked += Done_Clicked;
ToolbarItems.Add(done);
}
else if(item != null)
{
ToolbarItems.Remove(item);
}
}
This is cleaner and works from the code behind.
Well we need the IsVisible property for the front end, as xamarin doesn't have it, you can use Device.RuntimePlatform to check in real time which device the application is running. Since my code is in .cs of the XAML file, we can use xaml .cs to insert items into the screen.I put if () to do the logic and check if my device is on which platform, because I don't want it to display in UWP a toolbar.
The code is in .cs of the XAML file:
public kingTest()
{
InitializeComponent();
if((Device.RuntimePlatform == "Android")||(Device.RuntimePlatform == "iOS"))
{
ToolbarItem toolbar = new ToolbarItem();
toolbar.IconImageSource = "ic_ToolBar.png";
this.ToolbarItems.Add(toolbar);
}
};
I've achieved this easily using overloaded constructors. Here's an example:
View (add the name property):
<ContentPage x:Name="ContentPage"
<!-- rest of the tag -->
/>
Code-behind (add the toolbar items):
public partial class ExamplePage : ContentPage
{
public ExamplePage()
{
InitializeComponent();
BindingContext = this;
var saveToolbarItem = new ToolbarItem { Text = "Save" };
saveToolbarItem.Clicked += YourMethodToBeRan;
ContentPage.ToolbarItems.Add(saveToolbarItem);
}
public ExamplePage(Object object)
{
InitializeComponent();
BindingContext = this;
var updateToolbarItem = new ToolbarItem { Text = "Update" };
updateToolbarItem.Clicked += YourMethodToBeRan;
var deleteToolbarItem = new ToolbarItem { Text = "Delete" };
deleteToolbarItem.Clicked += YourMethodToBeRan;
ContentPage.ToolbarItems.Add(updateToolbarItem);
ContentPage.ToolbarItems.Add(deleteToolbarItem);
}
// rest of the class
}
The above pseudocode will add the "Save" toolbar item when the class is instantiated with no parameter, or the "Update" and "Delete" when a parameter is provided.
This isn't as elegant as IsEnabled / IsVisible booleans but it's a step in the right direction. Following this train of thought, you could modify the children of your toolbar during runtime to "show" and "hide" by adding and removing them as children.
Good luck!
I don't know if #tequila slammer's solution fully worked on Xamarin, but for us it only kind of works in .Net Maui (the evolution of Xamarin) and binding the IsVisible property to a variable.
Once the BindableToolbarItem is removed from the ContentPage's list of ToolbarItems, it is disconnected from the object that IsVisible is bound to forever.
For example: We want to use this control to hide or show a ToolbarItem that navigates to the admin screen, if I log in as the administrator on app launch, the item is there...great. If I then log out and log in as a non-admin, the item is not there...perfect. If I then log out and log in as an admin, the item is not there (the propertyChanged: OnIsVisibleChanged never fired)...:-(.
Not a big deal for us, if you want admin access then stopping the app and starting the app to log in as the admin is not a big ask.
In the newest release with .Net 7 the workaround works never more !
The reason is because the toolbar item which revomed will destoyed !

ARCGis Xamarin - MapView from previous page displays after navigating to new page

I have a 'main' map page from which the user can either add data to the map or click on existing data to see the details. This is done with PushModalAsync() where I navigate to a new page. My problem is in both of those new pages I have a new map with a new MapView that is 'underneath' the MapView from the 'main' page. Both the MapView and Map itself are new objects on the secondary pages.
After navigating to my new pages, the previous MapView shows in the area of the screen where it existed (on 'main' page) where my new MapView also exists. In other words, on my new map pages my map is laid out differently and partially sits higher in the layout than the 'main' map does. The part that sits higher is the only part of the new map that is visible and I can interact with. The part that sits on the same area is the 'main' map view and I cannot interact with it and it blocks my new map.
I tried this with PushAsync as well (not modal) and nothing changed. How is one supposed to have multiple maps in an app across multiple pages? Is this even possible with ArcGIS? This app used to be with Google Maps/Apple and the exact same layout worked just fine.
Edit: Code here (with irrelevant parts removed) This page is navigated to from another page with a map on it wherein this map is covered if it is laid out in the same area of the first map. From this page I navigate to a 3rd page where both maps show up. Removal works successfully, however when I try to add I get an error saying I cannot add a null child to a view group:
public partial class EventDetails : ContentPage
{
public Xamarin.Forms.Grid MapGrid = new Xamarin.Forms.Grid()
{
RowDefinitions =
{
new RowDefinition { Height = 220 },
new RowDefinition { Height = GridLength.Auto }
},
ColumnDefinitions =
{
new ColumnDefinition { Width = 500 }
},
HorizontalOptions = LayoutOptions.Center
};
public Map DetailMap;
public MapView MyDetailMapView = new MapView();
public EventDetails(MapLog eventDetails, string strMapType)
{
DetailMap = new Map(BasemapType.ImageryWithLabels, eventDetails.Latitude, eventDetails.Longitude, iMapDetail);
MyDetailMapView.Map = DetailMap;
MyDetailMapView.ViewpointChanged += MyMapView_ViewpointChanged;
var scrollView = new ScrollView
{
Content = mainStack
};
MapGrid.Children.Add(MyDetailMapView, 0, 0);
MapGrid.Children.Add(scrollView, 0, 1);
Content = MapGrid;
}
#if __ANDROID__
protected override void OnAppearing()
{
if (!MapGrid.Children.Contains(MyDetailMapView))
{
try
{
MapGrid.Children.Add(MyDetailMapView);
}
catch (Exception ex)
{
DisplayAlert("Err", ex.Message, "Ok");
}
}
base.OnAppearing();
}
protected override void OnDisappearing()
{
if (MapGrid.Children.Contains(MyDetailMapView))
MapGrid.Children.Remove(MyDetailMapView);
base.OnDisappearing();
}
#endif
}
EDIT*: I updated my Xamarin Forms version and ARCGIS Runtime and now the solution to remove/add the map does work without issue.
I had other similar problems with having two maps for android in Xamarin Forms ArcGIS and the solution was removing the MapView from the visual tree when you navigate to the new page.
You need to remove the MapView before you Navigate to the second page by:
[your parent layout].Children.Remove(MyMapView);
await Navigation.PushAsync(new SecondPage());
And in your first page you need to make sure to add the MapView back to the Visual Tree when you come back to the page:
protected override void OnAppearing()
{
if (!GridMap.Children.Contains(MyMapView))
GridMap.Children.Add(MyMapView);
base.OnAppearing();
}
That solved all my problems with two maps. Thanks to this post in ESRI forum
Navigation between 2 MapViews in Xamarin Android

Event handler ordering in Xamarin

I have a list view that I am popping up in Xamarin forms, that I want to hide if someone taps outside of the box. I have a tap gesture recognizer on the parent layout for the list view that handles that. In Android, it all works good. If I click off, it closes, but if I click on an element in the list view, it properly selects it. In iOS, the opposite happens. The gesture handler on the layout fires first and closes the list view without properly selecting the item.
So my question, is there a way to change the order on how the events are fired? If not, is there a better alternative to how I'm trying to accomplish this? Thanks!
If you are using ListView.ItemSelected or ListView.ItemTapped then I ran into the exact same issue the other day. The fix for me was to not use either of those and instead attach a TapGestureRecognizer to the ViewCell that is within the ListView. I also added an IsSelected property to the object that the ViewCell is being bound to so that I could change the background color of the item once it has been clicked.
public class SomePage : ContentPage {
private SomeModel _selectedModel; //It would be best to put this into your ViewModel
...
public SomePage() {
ListView list = new ListView {
ItemTemplate = new DataTemplate(() => {
ViewCell cell = new ViewCell {
View = new ContentView()
};
cell.View.GestureRecognizers.Add(new TapGestureRecognizer {
Command = new Command(() => {
if(_selectedModel != null) { _selectedModel.IsSelected = false; }
SomeModel model = (SomeModel)cell.BindingContext;
model.IsSelected = true;
_selectedModel = model;
})
}
return cell;
}
}
}
}

How do I override the Xamarin Forms TabbedPage item fonts for iOS?

Wanting to achieve a consistent look for my Xamarin Forms app, I need to know how to change the font for the tabbed page tab bar icons. Using UITabBarItem.Appearance as the iOS API would suggest does not appear to have any effect. What's necessary to do this?
U need to write a custom renderer like this one , take a clue from below code ! it has what u r seeking
[assembly: ExportRenderer(typeof(ExtendedTabbedPage), typeof(TabbedPageCustom))]
namespace App.iOS
{
public class TabbedPageCustom : TabbedRenderer
{
public TabbedPageCustom ()
{
TabBar.TintColor = UIKit.UIColor.White;
TabBar.BarTintColor = UIKit.UIColor.White;
TabBar.BackgroundColor = UIKit.UIColor.Red;
}
protected override void OnElementChanged (VisualElementChangedEventArgs e)
{
base.OnElementChanged (e);
// Set Text Font for unselected tab states
UITextAttributes normalTextAttributes = new UITextAttributes();
normalTextAttributes.Font = UIFont.FromName("ChalkboardSE-Light", 20.0F); // unselected
normalTextAttributes.TextColor = UIKit.UIColor.Blue;
UITabBarItem.Appearance.SetTitleTextAttributes(normalTextAttributes, UIControlState.Normal);
}
public override UIViewController SelectedViewController {
get {
UITextAttributes selectedTextAttributes = new UITextAttributes();
selectedTextAttributes.Font = UIFont.FromName("ChalkboardSE-Bold", 20.0F); // SELECTED
if (base.SelectedViewController != null)
{
base.SelectedViewController.TabBarItem.SetTitleTextAttributes(selectedTextAttributes, UIControlState.Normal);
}
return base.SelectedViewController;
}
set {
base.SelectedViewController = value;
foreach (UIViewController viewController in base.ViewControllers)
{
UITextAttributes normalTextAttributes = new UITextAttributes();
normalTextAttributes.Font = UIFont.FromName("ChalkboardSE-Light", 20.0F); // unselected
normalTextAttributes.TextColor = UIKit.UIColor.Blue;
viewController.TabBarItem.SetTitleTextAttributes(normalTextAttributes, UIControlState.Normal);
}
}
}
}
}
This was a particularly interesting problem. I tried:
UITabBarItem.Appearance
Using the UITabBarItem.Appearance.SetTitleTextAttributes method to update the UITextAttribute to my font (size 9.0f) for UIControlState.Normal. This didn't appear to make any difference.
UINavigationBar.Appearance
I found out that setting UINavigationBar.Appearance.SetTitleTextAttributes would update both the UINavigationBar text appearance as well as the UITabBarItem text appearance.
Which was a problem because the tabbed page item font size was far too large.
Customizing the TabbedRenderer
Inspired by a sample I saw somewhere, I subclassed TabbedRenderer in the iOS project.
I tried overriding the settor for TabbedRenderer.SelectedViewController property and loop through the ViewControllers property to set their items. The icons would display with the standard font, but once the user changed the tab they would all update to the desired font. Almost there!
I then tried overriding AddChildViewController and updating that controller's TabBarItem after it was added which ended up having no effect. The TabBarItem for the added page was being updated at some point after the controller was added.
Eventually I found out that overriding ViewWillAppear and setting the appearance for all the tab bar items at that time seemed to do the job I desired.
I've included a sample in this gist.

Resources