Xamarin Forms - ContentPage with SearchBar in the Navigation bar - c#

https://www.linkedin.com/pulse/xamarin-forms-contentpage-searchbar-navigation-bar-vipin-mathews/
I tried to implement the above code but not succeed, Search Icon not coming in page,
After that I tried this Adding a Search Bar in the toolbar of a navigationpage in Prism
, and its appears but once I changes the orientation or I logout or clicked on other page and come back again at this page its gone
I downloaded the code from git but not able to run that also.
<?xml version="1.0" encoding="utf-8" ?>
<menu xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto">
<item android:id="#+id/action_search"
android:title="Search"
android:icon="#android:drawable/ic_menu_search"
app:showAsAction="always|collapseActionView"
app:actionViewClass="android.support.v7.widget.SearchView"/>
</menu>
But ic_menu_search file not there in drawable folder, Is that an issue?

Just use the TitleView something like:
<NavigationPage.TitleView>
<StackLayout HorizontalOptions="FillAndExpand" Orientation="Horizontal">
<Label
HorizontalOptions="FillAndExpand"
Text="{StaticResource PageTitle}"
TextColor="White"
VerticalOptions="Center" />
<SearchBar
HorizontalOptions="End"
Placeholder="Search"
PlaceholderColor="{StaticResource GrayPlaceHolderColor}"
TextColor="White"
VerticalOptions="Center" />
</StackLayout>
</NavigationPage.TitleView>

We can create a custom renderer in both the Xamarin.iOS and Xamarin.Android to accomplish it.
Here's a sample application for reference:
https://github.com/brminnick/GitTrends
And here's a blog post that shows how to add a search bar to a Xamarin.Forms app for both Xamarin.iOS & Xamarin.Android: https://www.codetraveler.io/2019/08/10/adding-a-search-bar-to-xamarin-forms-navigationpage/
App.cs
Use a Xamarin.Forms Platform-Specific to use LargeTitles on the Xamarin.iOS app.
using Xamarin.Forms.PlatformConfiguration;
using Xamarin.Forms.PlatformConfiguration.iOSSpecific;
public class App : Xamarin.Forms.Application
{
public App()
{
var navigationPage = new Xamarin.Forms.NavigationPage(new MyContentPage());
navigationPage.On<iOS>().SetPrefersLargeTitles(true);
MainPage = navigationPage;
}
}
ISearchPage Interface
Create an Interface that can be used across the Xamarin.Forms, Xamarin.Android and Xamarin.iOS projects.
public interface ISearchPage
{
void OnSearchBarTextChanged(in string text);
event EventHandler<string> SearchBarTextChanged;
}
Xamarin.Forms Page
public class MyContentPage : ContentPage, ISearchPage
{
public MyContentPage()
{
SearchBarTextChanged += HandleSearchBarTextChanged
}
public event EventHandler<string> SearchBarTextChanged;
public void OnSearchBarTextChanged(in string text) => SearchBarTextChanged?.Invoke(this, text);
void HandleSearchBarTextChanged(object sender, string searchBarText)
{
//Logic to handle updated search bar text
}
}
iOS Custom Renderer
using System.Collections;
using System.Collections.Generic;
using System.Linq;
using UIKit;
using MyNamespace;
using MyNamespace.iOS;
using Xamarin.Forms;
using Xamarin.Forms.Platform.iOS;
[assembly: ExportRenderer(typeof(MyContentPage), typeof(SearchPageRenderer))]
namespace MyNamespace.iOS
{
public class SearchPageRenderer : PageRenderer, IUISearchResultsUpdating
{
bool _isFirstAppearing = true;
public override void WillMoveToParentViewController(UIViewController parent)
{
base.WillMoveToParentViewController(parent);
var searchController = new UISearchController(searchResultsController: null)
{
SearchResultsUpdater = this,
DimsBackgroundDuringPresentation = false,
HidesNavigationBarDuringPresentation = false,
HidesBottomBarWhenPushed = true
};
searchController.SearchBar.Placeholder = string.Empty;
parent.NavigationItem.SearchController = searchController;
DefinesPresentationContext = true;
}
public override void ViewDidAppear(bool animated)
{
base.ViewDidAppear(animated);
//Work-around to ensure the SearchController appears when the page first appears https://stackoverflow.com/a/46313164/5953643
if (_isFirstAppearing)
{
ParentViewController.NavigationItem.SearchController.Active = true;
ParentViewController.NavigationItem.SearchController.Active = false;
_isFirstAppearing = false;
}
}
public void UpdateSearchResultsForSearchController(UISearchController searchController)
{
if (Element is ISearchPage searchPage)
searchPage.OnSearchBarTextChanged(searchController.SearchBar.Text);
}
}
}
Xamarin.Android Menu XML
In the Xamarin.Android project, in the Resources folder, create a new folder called menu (if one doesn't already exist).
Note: the folder, menu, has a lowercase 'm'
In the Resources > menu folder, create a new file called MainMenu.xml.
Open Resources > menu > MainMenu.xml
In MainMenu.xml add the following code:
<?xml version="1.0" encoding="utf-8" ?>
<menu xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto">
<item android:id="#+id/ActionSearch"
android:title="Filter"
android:icon="#android:drawable/ic_menu_search"
app:showAsAction="always|collapseActionView"
app:actionViewClass="android.support.v7.widget.SearchView"/>
</menu>
Xamarin.Android CustomRenderer
Uses the Plugin.CurrentActivity NuGet Package.
using Android.Content;
using Android.Runtime;
using Android.Support.V7.Widget;
using Android.Text;
using Android.Views.InputMethods;
using Plugin.CurrentActivity;
using MyNamespace;
using MyNamespace.Droid;
using Xamarin.Forms;
using Xamarin.Forms.Platform.Android;
[assembly: ExportRenderer(typeof(MyContentPage), typeof(SearchPageRenderer))]
namespace MyNamespace.Droid
{
public class SearchPageRenderer : PageRenderer
{
public SearchPageRenderer(Context context) : base(context)
{
}
protected override void OnAttachedToWindow()
{
base.OnAttachedToWindow();
if (Application.Current.MainPage is NavigationPage navigationPage)
navigationPage.Popped += HandleNavigationPagePopped;
if (Element is ISearchPage && Element is Page page && page.Parent is NavigationPage navigationPage && navigationPage.CurrentPage is ISearchPage)
AddSearchToToolbar(page);
}
protected override void Dispose(bool disposing)
{
if (GetToolbar() is Toolbar toolBar)
toolBar.Menu?.RemoveItem(Resource.Menu.MainMenu);
base.Dispose(disposing);
}
void AddSearchToToolbar(in Page page)
{
if (GetToolbar() is Toolbar toolBar
&& toolBar.Menu?.FindItem(Resource.Id.ActionSearch)?.ActionView?.JavaCast<SearchView>().GetType() != typeof(SearchView))
{
toolBar.Title = page.Title;
toolBar.InflateMenu(Resource.Menu.MainMenu);
if (toolBar.Menu?.FindItem(Resource.Id.ActionSearch)?.ActionView?.JavaCast<SearchView>() is SearchView searchView)
{
searchView.QueryTextChange += HandleQueryTextChange;
searchView.ImeOptions = (int)ImeAction.Search;
searchView.InputType = (int)InputTypes.TextVariationFilter;
searchView.MaxWidth = int.MaxValue; //Set to full width - http://stackoverflow.com/questions/31456102/searchview-doesnt-expand-full-width
}
}
}
void HandleQueryTextChange(object sender, SearchView.QueryTextChangeEventArgs e)
{
if (Element is ISearchPage searchPage)
searchPage.OnSearchBarTextChanged(e.NewText);
}
void HandleNavigationPagePopped(object sender, NavigationEventArgs e)
{
if (sender is NavigationPage navigationPage
&& navigationPage.CurrentPage is ISearchPage)
{
AddSearchToToolbar(navigationPage.CurrentPage);
}
}
Toolbar GetToolbar() => CrossCurrentActivity.Current.Activity.FindViewById<Toolbar>(Resource.Id.toolbar);
}
}
Sample App
Here's a sample app for reference:
https://github.com/brminnick/GitTrends
And a blog post that shows how to add a search bar for both Xamarin.iOS and Xamarin.Android: https://www.codetraveler.io/2019/08/10/adding-a-search-bar-to-xamarin-forms-navigationpage/

Related

AdMob not displaying the banner ad on my app why?

So I tried implementing AdMob to my project but my ad is not showing up and I have no idea why.
I was following a guide online which I can't seem to find at this point but when they did it, it worked just fine, and I followed all the steps, I am starting to consider that it might be Xamarin but I am not sure.
I have a AdMobRenderer.cs that looks like this
using Google.MobileAds;
using UIKit;
using Xamarin.Forms;
using Xamarin.Forms.Platform.iOS;
namespace AdMobTestProject.iOS
{
public class AdMobRenderer : ViewRenderer
{
//hiding the key for this question
private const string adMobId = "ca-app-pub-xxxxxx/xxxxxxx";
private BannerView adView;
private bool viewOnScreen;
protected override void OnElementChanged(ElementChangedEventArgs<View> e)
{
base.OnElementChanged(e);
if (e.NewElement == null)
return;
if (e.OldElement == null)
{
adView = new BannerView(AdSizeCons.SmartBannerPortrait)
{
AdUnitID = adMobId,
RootViewController = UIApplication.SharedApplication.Windows[0].RootViewController
};
adView.AdReceived += (sender, args) =>
{
if (!viewOnScreen)
{
this.AddSubview(adView);
}
viewOnScreen = true;
};
Request request = Request.GetDefaultRequest();
adView.LoadRequest(request);
base.SetNativeControl(adView);
}
}
public AdMobRenderer()
{
}
}
}
And then the XAML aswell
<?xml version="1.0" encoding="utf-8" ?>
<ContentPage xmlns="http://xamarin.com/schemas/2014/forms"
xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
xmlns:local="clr-namespace:AdMobTestProject"
x:Class="AdMobTestProject.MainPage">
<StackLayout>
<!-- Place new controls here -->
<Label Text="Welcome to Xamarin.Forms!"
HorizontalOptions="Center"
VerticalOptions="CenterAndExpand" />
<local:AdMobView WidthRequest="320" HeightRequest="50"></local:AdMobView>
</StackLayout>
</ContentPage>
And ofcourse the MainPage.xaml.cs
namespace AdMobTestProject
{
public partial class MainPage : ContentPage
{
public MainPage()
{
InitializeComponent();
}
}
public class AdMobView : ContentView
{
public AdMobView()
{
}
}
}
Am I missing something? Why is it not displaying anything when I deploy the app to my device? iPhone 7.
I am using Xamarin.Forms
Check the space available for the adview should not be less than 360 pixel wide so check the main layout of the page if there margins remove them.
as a clue check the build output window in visual studio it will show if the ads are loaded or not and show a reason for why ads are not visible if they are loaded.
in your sample remove the WidthRequest.
this is a late reply for future searches.
Try change adMobId to a test ad id.
https://developers.google.com/admob/android/test-ads
(ca-app-pub-3940256099942544/6300978111)
I had the same issue, and if I navigated to another page and went back my ads showed up.

How can I change the colors of a stepper for iOS and Android?

My code uses a stepper which looks like this:
How can I change the color Blue to Red for the iOS and Android versions of the stepper by setting the new color in XAML?
This can be done using Effects.
Code
I've created a sample app here: https://github.com/brminnick/CustomStepper
Consuming the Effects in XAML
<?xml version="1.0" encoding="UTF-8"?>
<ContentPage xmlns="http://xamarin.com/schemas/2014/forms"
xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
x:Class="CustomStepper.StepperPage"
xmlns:local="clr-namespace:CustomStepper">
<ContentPage.Content>
<Stepper
HorizontalOptions="Center"
VerticalOptions="Center"
local:StepperColorEffect.Color="Red"/>
</ContentPage.Content>
</ContentPage>
Stepper Color Effect
using System.Linq;
using Xamarin.Forms;
namespace CustomStepper
{
public static class StepperColorEffect
{
public static readonly BindableProperty ColorProperty =
BindableProperty.CreateAttached(nameof(Color),
typeof(Color),
typeof(Stepper),
Color.Gray,
propertyChanged: OnStepperColorChanged);
public static Color GetColor(BindableObject view) => (Color)view.GetValue(ColorProperty);
public static void SetColor(BindableObject view, Color value) => view.SetValue(ColorProperty, value);
static void OnStepperColorChanged(BindableObject bindable, object oldValue, object newValue) => UpdateEffect(bindable);
static void UpdateEffect(BindableObject bindable)
{
var stepper = (Stepper)bindable:
RemoveEffect(stepper);
stepper.Effects.Add(new StepperColorRoutingEffect());
}
static void RemoveEffect(Stepper entry)
{
var effectToRemoveList = entry.Effects.OfType<StepperColorRoutingEffect>();
foreach (var entryReturnTypeEffect in effectToRemoveList)
entry.Effects.Remove(entryReturnTypeEffect);
}
}
class StepperColorRoutingEffect : RoutingEffect
{
public StepperColorRoutingEffect() : base("CustomStepper.StepperColorEffect")
{
}
}
}
iOS PlatformEffect
using UIKit;
using Xamarin.Forms;
using Xamarin.Forms.Platform.iOS;
using CustomStepper.iOS;
[assembly: ResolutionGroupName("CustomStepper")]
[assembly: ExportEffect(typeof(StepperColorEffect), nameof(StepperColorEffect))]
namespace CustomStepper.iOS
{
public class StepperColorEffect : PlatformEffect
{
protected override void OnAttached()
{
if (Element is Stepper element && Control is UIStepper control)
{
control.TintColor = CustomStepper.StepperColorEffect.GetColor(element).ToUIColor();
control.SetIncrementImage(Control.GetIncrementImage(UIControlState.Normal), UIControlState.Normal);
control.SetDecrementImage(Control.GetDecrementImage(UIControlState.Normal), UIControlState.Normal);
}
}
protected override void OnDetached()
{
if (Element is Stepper element && Control is UIStepper control)
{
control.TintColor = UIColor.Blue;
control.SetIncrementImage(Control.GetIncrementImage(UIControlState.Normal), UIControlState.Normal);
control.SetDecrementImage(Control.GetDecrementImage(UIControlState.Normal), UIControlState.Normal);
}
}
}
}
Android PlatformEffect
using Android.Widget;
using Android.Graphics;
using Xamarin.Forms;
using Xamarin.Forms.Platform.Android;
using CustomStepper.Droid;
[assembly: ResolutionGroupName("CustomStepper")]
[assembly: ExportEffect(typeof(StepperColorEffect), nameof(StepperColorEffect))]
namespace CustomStepper.Droid
{
public class StepperColorEffect : PlatformEffect
{
protected override void OnAttached()
{
if (Element is Stepper element && Control is LinearLayout control)
{
control.GetChildAt(0).Background.SetColorFilter(CustomStepper.StepperColorEffect.GetColor(element).ToAndroid(), PorterDuff.Mode.Multiply);
control.GetChildAt(1).Background.SetColorFilter(CustomStepper.StepperColorEffect.GetColor(element).ToAndroid(), PorterDuff.Mode.Multiply);
}
}
protected override void OnDetached()
{
if (Element is Stepper element && Control is LinearLayout control)
{
control.GetChildAt(0).Background.SetColorFilter(Xamarin.Forms.Color.Gray.ToAndroid(), PorterDuff.Mode.Multiply);
control.GetChildAt(1).Background.SetColorFilter(Xamarin.Forms.Color.Gray.ToAndroid(), PorterDuff.Mode.Multiply);
}
}
}
}
Screenshots
Android
iOS
How can I change the colors of a stepper for iOS and Android?
In your Xamarin.Android project, you could create a custom vector drawable and use it as a background to achieve the same appearance as the picture you have post above.
Place the button_selector.xml and button_border.xml file in your Android Resources\drawable folder:
button_selector.xml
<?xml version="1.0" encoding="utf-8"?>
<selector xmlns:android="http://schemas.android.com/apk/res/android">
<item android:drawable="#color/colorAccent" android:state_pressed="true"/>
<item android:drawable="#color/colorPrimaryDark" android:state_focused="true"/>
<item android:drawable="#drawable/button_border"/>
</selector>
button_border.xml:
<?xml version="1.0" encoding="utf-8"?>
<shape xmlns:android="http://schemas.android.com/apk/res/android"
android:shape="rectangle" >
<solid android:color="#00FFFFFF" />
<corners android:radius="5dp" />
<stroke
android:width="2dp"
android:color="#FFFFFF" />
</shape>
In your ExtStepperRenderer:
protected override void OnElementChanged(ElementChangedEventArgs<Stepper> e)
{
base.OnElementChanged(e);
MyStepper s = Element as MyStepper;
if (Control != null)
{
var button = Control.GetChildAt(0) as Android.Widget.Button;
button.SetTextColor(s.MyColor.ToAndroid());
button.SetBackground(ResourcesCompat.GetDrawable(Resources, Resource.Drawable.button_selector, null));
button.Background.SetColorFilter(s.MyColor.ToAndroid(), PorterDuff.Mode.Multiply);
var button2 = Control.GetChildAt(1) as Android.Widget.Button;
button2.SetTextColor(s.MyColor.ToAndroid());
button2.SetBackground(ResourcesCompat.GetDrawable(Resources, Resource.Drawable.button_selector, null));
button2.Background.SetColorFilter(s.MyColor.ToAndroid(), PorterDuff.Mode.Multiply);
}
}
Effect on Android device:
This is my solution that also refer from #Alan2's answer.
Xamarin.Forms
public class StepperExtend : Stepper
{
public static readonly BindableProperty ColorProperty =
BindableProperty.Create(
nameof(Color),
typeof(Color),
typeof(StepperExtend),
Color.Default);
public Color Color
{
get { return (Color)GetValue(ColorProperty); }
set { SetValue(ColorProperty, value); }
}
}
Xamarin.Android
[assembly:ExportRenderer(typeof(StepperExtend), typeof(StepperExtendRenderer))]
namespace HydroUkPoc.Frontend.Forms.Droid.Renderer
{
public class StepperExtendRenderer : StepperRenderer
{
StepperExtend FormElement
{
get { return Element as StepperExtend; }
}
protected override void OnElementChanged(ElementChangedEventArgs<Stepper> e)
{
base.OnElementChanged(e);
if (Control != null)
{
UpdateColor();
}
}
protected override void OnElementPropertyChanged(object sender, PropertyChangedEventArgs e)
{
if (e.PropertyName == StepperExtend.ColorProperty.PropertyName)
{
UpdateColor();
}
else
{
base.OnElementPropertyChanged(sender, e);
}
}
private void UpdateColor()
{
Control.GetChildAt(0).Background.SetColorFilter(FormElement.Color.ToAndroid(), PorterDuff.Mode.Multiply);
Control.GetChildAt(1).Background.SetColorFilter(FormElement.Color.ToAndroid(), PorterDuff.Mode.Multiply);
}
}
}
Xamarin.iOS
[assembly:ExportRenderer(typeof(StepperExtend), typeof(StepperExtendRenderer))]
namespace HydroUkPoc.Frontend.Forms.iOS.Renderer
{
public class StepperExtendRenderer : StepperRenderer
{
StepperExtend FormElement
{
get { return Element as StepperExtend; }
}
protected override void OnElementChanged(ElementChangedEventArgs<Stepper> e)
{
base.OnElementChanged(e);
if (Control != null)
{
UpdateColor();
}
}
protected override void OnElementPropertyChanged(object sender, PropertyChangedEventArgs e)
{
if (e.PropertyName == StepperExtend.ColorProperty.PropertyName)
{
UpdateColor();
}
else
{
base.OnElementPropertyChanged(sender, e);
}
}
private void UpdateColor()
{
Control.TintColor = FormElement.Color.ToUIColor();
}
}
}
You will need to create a custom renderer. Looking at the source code, the native control for the Stepper on iOS is UIStepper, and on Android it is actually a horizontal LinearLayout with two buttons. So for Android, the custom renderer should update the background color of the buttons, and on iOS, they appear to be icons, so try changing the TintColor of the UIStepper.
I came up with a simple solution.
Common File:
using Xamarin.Forms;
namespace Japanese
{
public class ExtStepper : Stepper
{
public static readonly BindableProperty ColorProperty =
BindableProperty.Create(nameof(Color),
typeof(Color), typeof(ExtStepper),
Color.Default);
public Color StepperColor
{
get { return (Color)GetValue(ColorProperty); }
set { SetValue(ColorProperty, value); }
}
}
}
iOS
using Xamarin.Forms;
using Japanese;
using Japanese.iOS;
using Xamarin.Forms.Platform.iOS;
[assembly: ExportRenderer(typeof(ExtStepper), typeof(ExtStepperRenderer))]
namespace Japanese.iOS
{
public class ExtStepperRenderer : StepperRenderer
{
protected override void OnElementChanged(ElementChangedEventArgs<Stepper> e)
{
base.OnElementChanged(e);
ExtStepper s = Element as ExtStepper;
if (Control != null)
Control.TintColor = s.StepperColor.ToUIColor();
}
}
}
Andoid
using Xamarin.Forms;
using Xamarin.Forms.Platform.Android;
using Japanese;
using Japanese.Droid;
using Android.Content;
using Android.Graphics;
[assembly: ExportRenderer(typeof(ExtStepper), typeof(ExtStepperRenderer))]
namespace Japanese.Droid
{
public class ExtStepperRenderer : StepperRenderer
{
public ExtStepperRenderer(Context context) : base(context)
{
}
protected override void OnElementChanged(ElementChangedEventArgs<Stepper> e)
{
base.OnElementChanged(e);
ExtStepper s = Element as ExtStepper;
if (Control != null)
{
Control.GetChildAt(0).Background.SetColorFilter(s.StepperColor.ToAndroid(), PorterDuff.Mode.Multiply);
Control.GetChildAt(1).Background.SetColorFilter(s.StepperColor.ToAndroid(), PorterDuff.Mode.Multiply);
}
}
}
}
XAML
<local:ExtStepper StepperColor="Red" x:Name="rptStepper

XAMARIN - not found in xmlns clr-namespace

i'm new with Xamarin, i tried to open the simulator iOS but i found this error:
Position 9:14. Type infra:IstanceLocator not found in xmlns
clr-namespace:Convert.Infrastructure;assembly=Convert
MY PROJECT
https://snag.gy/98ygVu.jpg
App.xaml
<?xml version="1.0" encoding="utf-8"?>
<Application xmlns="http://xamarin.com/schemas/2014/forms"
xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
xmlns:infra="clr-namespace:Convert.Infrastructure;assembly=Convert"
x:Class="Convert.App">
<Application.Resources>
<!-- Application resource dictionary -->
<ResourceDictionary>
<infra:IstanceLocator x:Key="Locator"></infra:IstanceLocator>
</ResourceDictionary>
</Application.Resources>
</Application>
App.xaml.cs
using Xamarin.Forms;
using Convert.Pages;
namespace Convert
{
public partial class App : Application
{
public App()
{
InitializeComponent();
MainPage = new NavigationPage(new MainPage());
}
protected override void OnStart()
{
// Handle when your app starts
}
protected override void OnSleep()
{
// Handle when your app sleeps
}
protected override void OnResume()
{
// Handle when your app resumes
}
}
}
IstanceLocator.cs
using System;
using Convert.ViewModels;
namespace Convert.Infrastructure
{
public class InstanceLocator
{
public MainViewModel Main { get; set; }
public InstanceLocator()
{
}
}
}
you have a typo
<infra:IstanceLocator x:Key="Locator"></infra:IstanceLocator>
You're missing the "n" in InstanceLocator

How can I detect the clicking of a tab button in Xamarin.Forms?

Here is the code that I have. I would like to know how I can detect when a user clicks a tab that is already selected as I want to toggle the icon for the aPage between play.png and pause.png plus I also want to call a method on APage.
public partial class MainPage : TabbedPage
{
public MainPage()
{
InitializeComponent();
var aPage = new NavigationPage(new APage())
{
Title = "Play",
Icon = "play.png"
};
var bPage = new NavigationPage(new BPage())
{
Title = "Settings",
Icon = "b.png"
};
Children.Add(aPage);
Children.Add(bPage);
}
}
Note that if possible I would like to find a solution that does not involve custom renderers for both iOS and Android. I'm wondering can I redefine the TabbedPage and put the logic in that class?
I know you want to avoid using custom renderers, but this is only possible by using a Custom Renderer.
Code
Xamarin.Android Custom Renderer
using Android.Content;
using Android.Support.Design.Widget;
using Xamarin.Forms;
using Xamarin.Forms.Platform.Android;
using Xamarin.Forms.Platform.Android.AppCompat;
[assembly: ExportRenderer(typeof(MainPage), typeof(MainPageRenderer))]
namespace YourNameSpace
{
public class MainPageRenderer : TabbedPageRenderer, TabLayout.IOnTabSelectedListener
{
MainPage _page;
public MainPageRenderer(Context context) : base(context) { }
protected override void OnElementChanged(ElementChangedEventArgs<TabbedPage> e)
{
base.OnElementChanged(e);
if (e.NewElement != null)
_page = e.NewElement as MainPage;
else
_page = e.OldElement as MainPage;
}
void TabLayout.IOnTabSelectedListener.OnTabReselected(TabLayout.Tab tab)
{
System.Diagnostics.Debug.WriteLine("Tab Reselected");
//Handle Tab Reselected
}
}
}
Xamarin.iOS Custom Renderer
using System;
using System.Diagnostics;
using UIKit;
using Xamarin.Forms;
using Xamarin.Forms.Platform.iOS;
[assembly: ExportRenderer(typeof(MainPage), typeof(MainPageRenderer))]
namespace YourNameSpace
{
public class MainPageRenderer : TabbedRenderer
{
MainPage _page;
protected override void OnElementChanged(VisualElementChangedEventArgs e)
{
base.OnElementChanged(e);
if (e.NewElement != null)
_page = e.NewElement as MainPage;
else
_page = e.OldElement as MainPage;
try
{
if (ViewController is UITabBarController tabBarController)
tabBarController.ViewControllerSelected += OnTabbarControllerItemSelected;
}
catch (Exception exception)
{
Debug.WriteLine(exception);
}
}
void OnTabbarControllerItemSelected(object sender, UITabBarSelectionEventArgs eventArgs)
{
if (_page?.CurrentPage?.Navigation != null && _page.CurrentPage.Navigation.NavigationStack.Count > 0)
{
Debug.WriteLine("Tab Tapped");
//Handle Tab Tapped
}
}
}
}
Code credit: #Kyle https://stackoverflow.com/a/42909203/5953643
If you want to get selected tab then you need to use ItemSource and SelectedItem property like ListView.
You can do this easily in iOS, but in Android you need a custom renderer. Just check this blog
http://motzcod.es/post/162985782667/dynamically-changing-xamarin-forms-tab-icons-when-select
You can't. TabbedPage interited from MultiPage that you can check the source from here. All select, deselect, update, template and logic is implemented here. You suppose to watch CurrentPage property but it has value check if already selected, so you cannot use.
this.PropertyChanging += async (object sender, PropertyChangingEventArgs e) =>
{
if (e.PropertyName == "CurrentPage")
{
if (this.CurrentPage == null)
return;
}
};

C# Xaml - Use Custom Class - Works programatically but not in Xaml

I'm pretty new with Xaml and i'm facing an issue . I want to use FontAwesome Icons in my app and after following a tutorial , i can use the icons programmatically (Code Below) .
Content = new StackLayout
{
Children = {
new FontIcon(FontIcon.Icon.Globe) {TextColor=Color.Red }
},
VerticalOptions = LayoutOptions.CenterAndExpand,
HorizontalOptions = LayoutOptions.CenterAndExpand,
};
However , when i try to implement this in Xaml - it crashes my app.
Code for Shared class extending label :
using Xamarin.Forms;
namespace myApp.Fonts
{
public class FontIcon : Label
{
public const string Typeface = "FontAwesome";
public FontIcon(string faIcon = null)
{
FontFamily = Typeface;
Text = faIcon;
}
public static class Icon
{
public static readonly string Gear = "";
public static readonly string Globe = "\uf000";
}
}
}
Xaml code ...Note that i'm already using the xmlns:local for another class
<ContentPage xmlns="http://xamarin.com/schemas/2014/forms"
xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
x:Class="myApp.TestPage"
xmlns:ctt="clr-namespace:myApp.Fonts">
<ctt:FontIcon FontIcon ="\uf000" VerticalOptions="Center" HorizontalOptions="Center" />
I'm guessing the issue is with this line :
<ctt:FontIcon FontIcon ="\uf000" VerticalOptions="Center" HorizontalOptions="Center" />
I'm not sure how to access that class via xaml or if its even possible to use xlmns:ctt
EDIT-------------------------------------------------------------------------
I used debug and this is the actual error :
System.MissingMethodException: Default constructor not found for type myApp.Fonts.FontIcon
Edit 2 :
I did this :
public FAIcon()
{
}
And in xaml :
<custom:FAIcon FontFamily = "Typeface" Text = "\uf000" VerticalOptions="Center" HorizontalOptions="Center" />
The app doesn't crash now but it displays the plain text instead of the icon
This is my android renderer :
[assembly: ExportRenderer(typeof(FontIcon), typeof(FARenderer))]
namespace myApp.Droid.Renderers
{
public class FARenderer : LabelRenderer
{
protected override void OnElementChanged(ElementChangedEventArgs<Label> e)
{
base.OnElementChanged(e);
if (e.OldElement == null)
{
Control.Typeface = Typeface.CreateFromAsset(Forms.Context.Assets, FontIcon.Typeface + ".ttf");
}
}
}
}
if you always want to use FontAwesome, set it in your constructor:
public const string Typeface = "FontAwesome";
public FAIcon()
{
FontFamily = TypeFace;
}
don't do this in your XAML, it just sets the FontFamily to "TypeFace" which is not what you want
<custom:FAIcon FontFamily = "Typeface" ...

Categories

Resources