Xamarin Forms Entry: Use comma as decimal separator - xamarin

I want to use a comma as decimal separator in an Xamarin Forms entry with numeric keyboard. I set the Culture to CultureInfo.DefaultThreadCurrentCulture = new CultureInfo("de-DE"); and the , is shown on the keyboard, but the entry only accepts . as separator and this only works if I use the telephone keyboard. This seems to be an Andoid Bug. Is there a solution for Xamarin Forms?

There is.
In order to have it working properly, i would suggest you do the following:
In your shared code create a new class called "NumericInput", deriving from Entry:
public class NumericInput : Entry
{
public static BindableProperty AllowNegativeProperty = BindableProperty.Create("AllowNegative", typeof(bool), typeof(NumericInput), false, BindingMode.TwoWay);
public static BindableProperty AllowFractionProperty = BindableProperty.Create("AllowFraction", typeof(bool), typeof(NumericInput), false, BindingMode.TwoWay);
public NumericInput()
{
this.Keyboard = Keyboard.Numeric;
}
public bool AllowNegative
{
get { return (bool)GetValue(AllowNegativeProperty); }
set { SetValue(AllowNegativeProperty, value); }
}
public bool AllowFraction
{
get { return (bool)GetValue(AllowFractionProperty); }
set { SetValue(AllowFractionProperty, value); }
}
}
Then in your android project create a custom renderer for it:
[assembly: ExportRenderer(typeof(NumericInput), typeof(NumericInputRenderer))]
namespace MyApp.Droid.Renderer
{
public class NumericInputRenderer : EntryRenderer
{
public NumericInputRenderer(Context context) : base(context)
{
}
private EditText _native = null;
protected override void OnElementChanged(ElementChangedEventArgs<Entry> e)
{
base.OnElementChanged(e);
if (e.NewElement == null)
return;
_native = Control as EditText;
_native.InputType = Android.Text.InputTypes.ClassNumber;
if ((e.NewElement as NumericInput).AllowNegative == true)
_native.InputType |= InputTypes.NumberFlagSigned;
if ((e.NewElement as NumericInput).AllowFraction == true)
{
_native.InputType |= InputTypes.NumberFlagDecimal;
_native.KeyListener = DigitsKeyListener.GetInstance(string.Format("1234567890{0}", System.Globalization.CultureInfo.CurrentCulture.NumberFormat.NumberDecimalSeparator));
}
if (e.NewElement.FontFamily != null)
{
var font = Typeface.CreateFromAsset(Android.App.Application.Context.Assets, e.NewElement.FontFamily);
_native.Typeface = font;
}
}
protected override void OnElementPropertyChanged(object sender, PropertyChangedEventArgs e)
{
base.OnElementPropertyChanged(sender, e);
if (_native == null)
return;
if (e.PropertyName == NumericInput.AllowNegativeProperty.PropertyName)
{
if ((sender as NumericInput).AllowNegative == true)
{
// Add Signed flag
_native.InputType |= InputTypes.NumberFlagSigned;
}
else
{
// Remove Signed flag
_native.InputType &= ~InputTypes.NumberFlagSigned;
}
}
if (e.PropertyName == NumericInput.AllowFractionProperty.PropertyName)
{
if ((sender as NumericInput).AllowFraction == true)
{
// Add Decimal flag
_native.InputType |= InputTypes.NumberFlagDecimal;
_native.KeyListener = DigitsKeyListener.GetInstance(string.Format("1234567890{0}", System.Globalization.CultureInfo.CurrentCulture.NumberFormat.NumberDecimalSeparator));
}
else
{
// Remove Decimal flag
_native.InputType &= ~InputTypes.NumberFlagDecimal;
_native.KeyListener = DigitsKeyListener.GetInstance(string.Format("1234567890"));
}
}
}
}
}
this will create an entry element, which automatically uses the correct decimal separator depending on the current culture setting of the device.
since the class allows for some detailed settings, I should also add the iOS renderer:
[assembly: ExportRenderer(typeof(NumericInput), typeof(NumericInputRenderer))]
namespace MyApp.iOS.Renderer
{
public class NumericInputRenderer : EntryRenderer
{
private UITextField _native = null;
protected override void OnElementChanged(ElementChangedEventArgs<Entry> e)
{
base.OnElementChanged(e);
if (e.NewElement == null)
return;
_native = Control as UITextField;
_native.KeyboardType = UIKeyboardType.NumberPad;
if ((e.NewElement as NumericInput).AllowNegative == true && (e.NewElement as NumericInput).AllowFraction == true)
{
_native.KeyboardType = UIKeyboardType.NumbersAndPunctuation;
}
else if ((e.NewElement as NumericInput).AllowNegative == true)
{
_native.KeyboardType = UIKeyboardType.NumbersAndPunctuation;
}
else if ((e.NewElement as NumericInput).AllowFraction == true)
{
_native.KeyboardType = UIKeyboardType.DecimalPad;
}
else
{
_native.KeyboardType = UIKeyboardType.NumberPad;
}
if (e.NewElement.FontFamily != null)
{
e.NewElement.FontFamily = e.NewElement.FontFamily.Replace(".ttf", "");
}
}
protected override void OnElementPropertyChanged(object sender, PropertyChangedEventArgs e)
{
base.OnElementPropertyChanged(sender, e);
if (_native == null)
return;
}
}
}
However keep in mind that some vendors have implemented custom soft keyboards (i am looking at you, Samsung!), that don't even show a comma as a decimal seperator. In that case the only solution is to install another keyboard, such as SwiftKey or Gboard. However, if a comma is shown, you should be able to use it with the code above.

Related

Icon not highlighted when using BottomBarPage

I’m using the BottomBarPage package. I’m changing the Tab on the fly programmatically, however the icon at the bottom doesn’t get highlighted for Android when there’s a tab change. It works well on iOS on the hand.
I tried James Montemagno’s code, I couldn’t get it to work with the BottomBarPage package.
This is the link to his code
https://montemagno.com/dynamically-changing-xamarin-forms-tab-icons-when-select/
How do I get the Icon highlighted when there is a Tab Page happening programmatically for BottomBarPage?
Below is my code.
public AndroidMainPage(int indexPage)
{
InitializeComponent();
NavigationPage.SetHasNavigationBar(this, false);
CurrentPageChanged += MainPage_CurrentPageChanged;
CurrentPage = Children[indexPage];
CurrentPage.Appearing += (s, a) => Children[indexPage].Icon = new FileImageSource() { File = "quizzes_selected.png" };
CurrentPage.Disappearing += (s, a) => Children[indexPage].Icon = new FileImageSource() { File = "quizzes.png" };
_currentPage = CurrentPage;
}
private void MainPage_CurrentPageChanged(object sender, EventArgs e)
{
IIconChange currentBinding;
if (_currentPage != null)
{
currentBinding = ((NavigationPage)_currentPage).CurrentPage.BindingContext as IIconChange;
if (currentBinding != null)
currentBinding.IsSelected = false;
}
_currentPage = CurrentPage;
currentBinding = ((NavigationPage)_currentPage).CurrentPage.BindingContext as IIconChange;
if (currentBinding != null)
currentBinding.IsSelected = true;
UpdateIcons?.Invoke(this, EventArgs.Empty);
}
Android BottomBarPage Renderer:
[assembly: ExportRenderer(typeof(AndroidMainPage), typeof(MyTabbedPageRenderer))]
namespace TabPageDemo.Android.Renderers
{
public class MyTabbedPageRenderer : BottomBarPageRenderer
{
bool setup;
TabLayout layout;
public MyTabbedPageRenderer()
{
}
protected override void OnElementChanged(ElementChangedEventArgs<BottomBarPage> e)
{
if (e != null) base.OnElementChanged(e);
if (Element != null)
{
((AndroidMainPage)Element).UpdateIcons += Handle_UpdateIcons;
}
}
protected override void OnElementPropertyChanged(object sender, System.ComponentModel.PropertyChangedEventArgs e)
{
base.OnElementPropertyChanged(sender, e);
if (layout == null && e.PropertyName == "Renderer")
{
layout = (TabLayout)ViewGroup.GetChildAt(1);
}
}
void Handle_UpdateIcons(object sender, EventArgs e)
{
TabLayout tabs = layout;
if (tabs == null)
return;
for (var i = 0; i < Element.Children.Count; i++)
{
var child = Element.Children[i].BindingContext as IIconChange;
if (child == null) continue;
var icon = child.CurrentIcon;
if (string.IsNullOrEmpty(icon))
continue;
TabLayout.Tab tab = tabs.GetTabAt(i);
SetCurrentTabIcon(tab, icon);
}
}
void SetCurrentTabIcon(TabLayout.Tab tab, string icon)
{
tab.SetIcon(IdFromTitle(icon, ResourceManager.DrawableClass));
}
int IdFromTitle(string title, Type type)
{
string name = Path.GetFileNameWithoutExtension(title);
int id = GetId(type, name);
return id;
}
int GetId(Type type, string memberName)
{
object value = type.GetFields().FirstOrDefault(p => p.Name == memberName)?.GetValue(type)
?? type.GetProperties().FirstOrDefault(p => p.Name == memberName)?.GetValue(type);
if (value is int)
return (int)value;
return 0;
}
}
}
Using the Renderer, the tab variable is always null from this TabLayout.Tab tab = tabs.GetTabAt(i);
Any suggestion?

How to set corner radius for button in xamarin uwp by programatically?

I am having an grid holding in Button. I want to set corner radius for this button by programmatically in xamarin.uwp. If it possible means i want to set corner radius for Bottom left alone. Please suggest your ideas for this query.
For your requirement, you could custom BottomLeft BindableProperty. then use it in your custom button render like the following.
Custom Forms buttom
public class MyButton : Button
{
public static readonly BindableProperty BottomLeftProperty = BindableProperty.Create(
propertyName: "BottomLeft",
returnType: typeof(int),
declaringType: typeof(MyButton),
defaultValue: default(int));
public int BottomLeft
{
get { return (int)GetValue(BottomLeftProperty); }
set { SetValue(BottomLeftProperty, value); }
}
}
CustomButtonRenderer.cs
public class CustomButtonRenderer : ButtonRenderer
{
Windows.UI.Xaml.Controls.ContentPresenter _contentPresenter;
MyButton _myElement;
protected override void OnElementChanged(ElementChangedEventArgs<Button> e)
{
base.OnElementChanged(e);
if (Control == null)
{
SetNativeControl(new FormsButton());
}
if (e.NewElement != null)
{
Control.Loaded += Control_Loaded;
_myElement = Element as MyButton;
}
}
protected override void OnElementPropertyChanged(object sender, PropertyChangedEventArgs e)
{
base.OnElementPropertyChanged(sender, e);
if (e.PropertyName == MyButton.BottomLeftProperty.PropertyName)
{
UpdateBottomLeftBorderRadius(_myElement.BottomLeft);
}
}
private void Control_Loaded(object sender, Windows.UI.Xaml.RoutedEventArgs e)
{
_contentPresenter = MyFindChildByName(Control, "ContentPresenter") as Windows.UI.Xaml.Controls.ContentPresenter;
if (_myElement.IsSet(MyButton.BottomLeftProperty) && _myElement.BottomLeft != (int)MyButton.BottomLeftProperty.DefaultValue)
{
UpdateBottomLeftBorderRadius(_myElement.BottomLeft);
}
}
private void UpdateBottomLeftBorderRadius(int bottomLeft)
{
if (_contentPresenter != null)
{
_contentPresenter.CornerRadius = new CornerRadius(0, 0, 0, bottomLeft);
}
}
public static DependencyObject MyFindChildByName(DependencyObject parant, string ControlName)
{
int count = VisualTreeHelper.GetChildrenCount(parant);
for (int i = 0; i < count; i++)
{
var MyChild = VisualTreeHelper.GetChild(parant, i);
if (MyChild is FrameworkElement && ((FrameworkElement)MyChild).Name == ControlName)
return MyChild;
var FindResult = MyFindChildByName(MyChild, ControlName);
if (FindResult != null)
return FindResult;
}
return null;
}
}
Usage
<local:MyButton x:Name="Hello" Text="hello"
WidthRequest="100"
HeightRequest="100"
Margin="0,100,0,0"
BottomLeft="15"
VerticalOptions="Center"
HorizontalOptions="Center"
Clicked="MyButton_Clicked"/>
For edit the bottom left property programatically.
HelloBtn.BottomLeft = 30;

Custom Renderer for Picker in Xamarin.Forms

I want to customize my picker. I created a custom renderer for my picker but I dont know how the customization settings. How can I change the font style and size of the item? and How can I remove the two lines?
public class CustomPickerRenderer : PickerRenderer
{
public CustomPickerRenderer(Context context) : base(context)
{
AutoPackage = false;
}
protected override void OnElementChanged(ElementChangedEventArgs<Picker> e)
{
base.OnElementChanged(e);
if (e.OldElement == null)
{
Control.Background = null;
var layoutParams = new MarginLayoutParams(Control.LayoutParameters);
layoutParams.SetMargins(0, 0, 0, 0);
Control.LayoutParameters = layoutParams;
Control.SetPadding(0, 0, 0, 0);
SetPadding(0, 0, 0, 0);
}
}
}
If you want to set the fontSize of the text , you first need to customize a subclass extends from NumberPicker and overwrite the method AddView.
public class TextColorNumberPicker: NumberPicker
{
public TextColorNumberPicker(Context context) : base(context)
{
}
public override void AddView(View child, int index, ViewGroup.LayoutParams #params)
{
base.AddView(child, index, #params);
UpdateView(child);
}
public void UpdateView(View view)
{
if ( view is EditText ) {
//set the font of text
((EditText)view).TextSize = 8;
}
}
}
If you want to remove the lines,you should rewrite the NumberPicker
in Android Custom Renderer
public class MyAndroidPicker:PickerRenderer
{
IElementController ElementController => Element as IElementController;
public MyAndroidPicker()
{
}
private AlertDialog _dialog;
protected override void OnElementChanged(ElementChangedEventArgs<Picker> e)
{
base.OnElementChanged(e);
if (e.NewElement == null || e.OldElement != null)
return;
Control.Click += Control_Click;
}
protected override void Dispose(bool disposing)
{
Control.Click -= Control_Click;
base.Dispose(disposing);
}
private void SetPickerDividerColor(TextColorNumberPicker picker)
{
Field[] fields = picker.Class.GetDeclaredFields();
foreach (Field pf in fields)
{
if(pf.Name.Equals("mSelectionDivider"))
{
pf.Accessible = true;
// set the color as transparent
pf.Set(picker, new ColorDrawable(this.Resources.GetColor(Android.Resource.Color.Transparent)));
}
}
}
private void Control_Click(object sender, EventArgs e)
{
Picker model = Element;
var picker = new TextColorNumberPicker(Context);
if (model.Items != null && model.Items.Any())
{
picker.MaxValue = model.Items.Count - 1;
picker.MinValue = 0;
picker.SetBackgroundColor(Android.Graphics.Color.Yellow);
picker.SetDisplayedValues(model.Items.ToArray());
//call the method after you setting DisplayedValues
SetPickerDividerColor(picker);
picker.WrapSelectorWheel = false;
picker.Value = model.SelectedIndex;
}
var layout = new LinearLayout(Context) { Orientation = Orientation.Vertical };
layout.AddView(picker);
ElementController.SetValueFromRenderer(VisualElement.IsFocusedProperty, true);
var builder = new AlertDialog.Builder(Context);
builder.SetView(layout);
builder.SetTitle(model.Title ?? "");
builder.SetNegativeButton("Cancel ", (s, a) =>
{
ElementController.SetValueFromRenderer(VisualElement.IsFocusedProperty, false);
// It is possible for the Content of the Page to be changed when Focus is changed.
// In this case, we'll lose our Control.
Control?.ClearFocus();
_dialog = null;
});
builder.SetPositiveButton("Ok ", (s, a) =>
{
ElementController.SetValueFromRenderer(Picker.SelectedIndexProperty, picker.Value);
// It is possible for the Content of the Page to be changed on SelectedIndexChanged.
// In this case, the Element & Control will no longer exist.
if (Element != null)
{
if (model.Items.Count > 0 && Element.SelectedIndex >= 0)
Control.Text = model.Items[Element.SelectedIndex];
ElementController.SetValueFromRenderer(VisualElement.IsFocusedProperty, false);
// It is also possible for the Content of the Page to be changed when Focus is changed.
// In this case, we'll lose our Control.
Control?.ClearFocus();
}
_dialog = null;
});
_dialog = builder.Create();
_dialog.DismissEvent += (ssender, args) =>
{
ElementController?.SetValueFromRenderer(VisualElement.IsFocusedProperty, false);
};
_dialog.Show();
}
}
I also used this CustomRenderer which was posted before only instead of overriding it you can change the properties like this.
private void Control_Click(object sender, EventArgs e)
{
Picker model = Element;
var picker = new MyNumberPicker(Context);
if (model.Items != null && model.Items.Any())
{
// set style here
picker.MaxValue = model.Items.Count - 1;
picker.MinValue = 0;
picker.SetBackgroundColor(Android.Graphics.Color.Transparent);
picker.SetDisplayedValues(model.Items.ToArray());
//call the method after you setting DisplayedValues
SetPickerDividerColor(picker);
picker.WrapSelectorWheel = false;
picker.Value = model.SelectedIndex;
// change Text Size and Divider
picker.TextSize = 30;
picker.SelectionDividerHeight = 1;
}

How to implement TabLayout.IOnTabSelectedListener.OnTabUnselected with TabbedPage.ToolbarPlacement="Bottom" - Xamarin Forms?

I just recently used android:TabbedPage.ToolbarPlacement="Bottom". I used to have the following code:
void TabLayout.IOnTabSelectedListener.OnTabUnselected(TabLayout.Tab tab)
{
var playPage = Element.CurrentPage as NavigationPage;
if (!(playPage.RootPage is PhrasesFrame))
return;
var tabLayout = (TabLayout)ViewGroup.GetChildAt(1);
var playTab = tabLayout.GetTabAt(4);
tab.SetText("Play");
tab.SetIcon(Resource.Drawable.ionicons_2_0_1_play_outline_25);
App.pauseCard = true;
}
Anyone knows how can I implement this with ToolbarPlacement="Bottom" ? I have implemented both BottomNavigationView.IOnNavigationItemSelectedListener, BottomNavigationView.IOnNavigationItemReselectedListener but can't find any reference for UnselectedTab if there is any.
Edit:
Previous custom renderer using the default tab position and implementing TabLayout:
namespace Japanese.Droid
{
public class MyTabbedPageRenderer: TabbedPageRenderer, TabLayout.IOnTabSelectedListener
{
ViewPager viewPager;
TabLayout tabLayout;
bool setup;
public MyTabbedPageRenderer(Context context): base(context){ }
protected override void OnElementPropertyChanged(object sender, PropertyChangedEventArgs e)
{
// More codes here
}
void TabLayout.IOnTabSelectedListener.OnTabReselected(TabLayout.Tab tab)
{
UpdateTab(tab);
}
void TabLayout.IOnTabSelectedListener.OnTabSelected(TabLayout.Tab tab)
{
UpdateTab(tab);
}
void TabLayout.IOnTabSelectedListener.OnTabUnselected(TabLayout.Tab tab)
{
var playPage = Element.CurrentPage as NavigationPage;
if (!(playPage.RootPage is PhrasesFrame))
return;
var tabLayout = (TabLayout)ViewGroup.GetChildAt(1);
var playTab = tabLayout.GetTabAt(4);
tab.SetText("Play");
tab.SetIcon(Resource.Drawable.ionicons_2_0_1_play_outline_25);
App.pauseCard = true;
}
void UpdateTab(TabLayout.Tab tab)
{
// To have the logic only on he tab on position 1
if (tab == null || tab.Position != 4)
{
return;
}
if (tab.Text == "Play")
{
tab.SetText("Pause");
tab.SetIcon(Resource.Drawable.ionicons_2_0_1_pause_outline_22);
App.pauseCard = false;
}
else
{
tab.SetText("Play");
tab.SetIcon(Resource.Drawable.ionicons_2_0_1_play_outline_25);
App.pauseCard = true;
}
}
}
}
Current custom renderer using the ToolbarPlacement="Bottom":
namespace Japanese.Droid
{
public class BottomTabPageRenderer : TabbedPageRenderer, BottomNavigationView.IOnNavigationItemSelectedListener, BottomNavigationView.IOnNavigationItemReselectedListener
{
public BottomTabPageRenderer(Context context) : base(context) { }
protected override void OnElementChanged(ElementChangedEventArgs<TabbedPage> e)
{
base.OnElementChanged(e);
// More codes here
}
bool BottomNavigationView.IOnNavigationItemSelectedListener.OnNavigationItemSelected(IMenuItem item)
{
base.OnNavigationItemSelected(item);
UpdateTab(item)
}
void BottomNavigationView.IOnNavigationItemReselectedListener.OnNavigationItemReselected(IMenuItem item)
{
UpdateTab(item);
}
void UpdateTab(IMenuItem item)
{
var playTabId = 4;
var title = item.TitleFormatted.ToString();
if (item == null || item.ItemId != playTabId)
{
return;
}
if (item.ItemId == playTabId)
{
if (title == "Play")
{
item.SetTitle("Pause");
item.SetIcon(Resource.Drawable.ionicons_2_0_1_pause_outline_22);
App.pauseCard = false;
}
else
{
item.SetTitle("Play");
item.SetIcon(Resource.Drawable.ionicons_2_0_1_play_outline_25);
App.pauseCard = true;
}
}
}
}
}
So now my problem is I don't have any idea how will I implement the TabLayout.IOnTabSelectedListener.OnTabUnselected in the new custom renderer.
There is no official stuff for OnTabReselected event for TabbedPage's bottom navigation or
BottomNavigationView because It doesn't use TabLayout.Tab for a start. Many overridden methods of TabbedPageRenderer not being called like SetTabIcon. If you are using IOnTabSelectedListener interface(As your first part of code) you have three methods to use.
void OnTabReselected(Tab tab);
void OnTabSelected(Tab tab);
void OnTabUnselected(Tab tab);
But when it comes to BottomNavigationView interface you have only two methods
void OnNavigationItemReselected
bool OnNavigationItemSelected
So we don't have built in OnTabUnselected method. Here you need to write custom code to make unseleted event.
I have tried this code without using custom renderer using 4 tabs pages & the xaml of tabbed written in MailPage.xaml file. First declare List<string> in App.xaml.cs file to store Title of all tabs
public static List<string> Titles {get;set;}
Add tabs pages title in above list from MainPage.xaml.cs file's OnAppearing method
protected override void OnAppearing()
{
for (int i = 0; i < this.Children.Count; i++)
{
App.Titles.Add(this.Children[i].Title);
}
}
Now go to your MyTabbedPage class in which is available in shared project.
public class MyTabbedPage : Xamarin.Forms.TabbedPage
{
string selectedTab = string.Empty;
string unSelectedTab = string.Empty;
bool isValid;
public MyTabbedPage()
{
On<Xamarin.Forms.PlatformConfiguration.Android>().SetToolbarPlacement(ToolbarPlacement.Bottom);
this.CurrentPageChanged += delegate
{
unSelectedTab = selectedTab;
selectedTab = CurrentPage.Title;
if (App.Titles != null)
isValid = true;
else
App.Titles = new List<string>();
if (isValid)
{
MoveTitles(selectedTab);
//Pass 0 index for tab selected & 1 for tab unselected
var unSelecteTabTitle = App.Titles[1];
//TabEvents(1); here you know which tab unseleted call any method
}
};
}
//This method is for to moving selected title on top of App.Titles list & unseleted tab title automatic shifts at index 1
void MoveTitles(string selected)
{
var holdTitles = App.Titles;
if (holdTitles.Count > 0)
{
int indexSel = holdTitles.FindIndex(x => x.StartsWith(selected));
holdTitles.RemoveAt(indexSel);
holdTitles.Insert(0, selected);
}
App.Titles = holdTitles;
}
}
Or you can make swith case like this
void TabEvents(int index)
{
switch (index)
{
case 0:
//Tab selected
break;
case 1:
//Tab unselected
break;
}
}
Few things I should mention that MainPage.xaml.cs file inheriting MyTabbedPage
public partial class MainPage : MyTabbedPage
Structure of MainPage.xaml file
<?xml version="1.0" encoding="utf-8" ?>
<local:MyTabbedPage
<TabbedPage.Children>
<NavigationPage Title="Browse">
</NavigationPage>
</TabbedPage.Children>
</local:MyTabbedPage>
Answer seems long but hope it help you.
As per G.Hakim's suggestion, I was able to do what I wanted to do by capturing the tab item I wanted to work on and do the necessary actions in BottomNavigationView.IOnNavigationItemSelectedListener.OnNavigationItemSelected.
namespace Japanese.Droid
{
public class BottomTabPageRenderer : TabbedPageRenderer, BottomNavigationView.IOnNavigationItemSelectedListener, BottomNavigationView.IOnNavigationItemReselectedListener
{
// same as above
bool BottomNavigationView.IOnNavigationItemSelectedListener.OnNavigationItemSelected(IMenuItem item)
{
base.OnNavigationItemSelected(item);
if(item.ItemId == 4 && item.TitleFormatted.ToString() == "Play")
{
item.SetTitle("Pause");
item.SetIcon(Resource.Drawable.ionicons_2_0_1_pause_outline_22);
App.pauseCard = false;
playTab = item;
}
if(item.ItemId !=4 && playTab.TitleFormatted.ToString() == "Pause")
{
playTab.SetTitle("Play");
playTab.SetIcon(Resource.Drawable.ionicons_2_0_1_play_outline_25);
App.pauseCard = true;
}
return true;
}
// same as above
}
}

Is there a way I can pass a style request to a Xamarin Label Custom renderer?

I came up with this code so far. My problem is I would like to pass a style to the renderer and I am not sure how to pass a styleId.
public class LabelBodyCustomRenderer : LabelRenderer
{
public LabelBodyCustomRenderer()
{
}
protected override void OnElementChanged(ElementChangedEventArgs<Label> e)
{
base.OnElementChanged(e);
if (Control != null)
Control.Font = UIFont.GetPreferredFontForTextStyle(UIFontTextStyle.Body);
}
protected override void OnElementPropertyChanged(object sender, System.ComponentModel.PropertyChangedEventArgs e)
{
base.OnElementPropertyChanged(sender, e);
if (e.PropertyName == Label.TextColorProperty.PropertyName
|| e.PropertyName == Label.FontProperty.PropertyName
|| e.PropertyName == Label.TextProperty.PropertyName
|| e.PropertyName == Label.FormattedTextProperty.PropertyName)
{
switch (e.StyleId)
{
case "Body":
Control.Font = UIFont.GetPreferredFontForTextStyle(UIFontTextStyle.Body);
break;
case "Callout":
Control.Font = UIFont.GetPreferredFontForTextStyle(UIFontTextStyle.Callout);
break;
case "Caption1":
Control.Font = UIFont.GetPreferredFontForTextStyle(UIFontTextStyle.Caption1);
break;
case "Caption2":
Control.Font = UIFont.GetPreferredFontForTextStyle(UIFontTextStyle.Caption2);
break;
case "Footnote":
I saw another example where it was done like this and where the code used item.StyleId. But this is quite different from the label renderer so I am interested to see if something similar can be done for the labelRenderer above:
public class TextCellCustomRenderer : TextCellRenderer
{
CellTableViewCell cell;
public override UITableViewCell GetCell(Cell item, UITableViewCell reusableCell, UITableView tv)
{
var textCell = (TextCell)item;
var fullName = item.GetType().FullName;
cell = tv.DequeueReusableCell(fullName) as CellTableViewCell;
if (cell == null)
{
cell = new CellTableViewCell(UITableViewCellStyle.Value1, fullName);
}
else
{
cell.Cell.PropertyChanged -= cell.HandlePropertyChanged;
//cell.Cell.PropertyChanged -= Current_PropertyChanged;
}
Create a class derived from Label - MyLabel. Add binding property MyStyleId to MyLabel. Update your renderer to render MyLabel and not all Labels.
MyLabel class
public class MyLabel : Label
{
public static readonly BindableProperty MyStyleIdProperty =
BindableProperty.Create("MyStyleId", typeof(string), typeof(MyLabel), "Body");
public string MyStyleId
{
get { return (string)GetValue(MyStyleIdProperty); }
}
}
XAML
<local:MyLabel MyStyleId="Header" Text="Custom Label"></local:MyLabel>
Don't forget to define your "local"
xmlns:local="clr-namespace:ButtonRendererDemo;assembly=ButtonRendererDemo"
Renderer
[assembly: ExportRenderer(typeof(MyLabel), typeof(MyLabelRenderer))]
namespace ButtonRendererDemo.iOS
{
public class MyLabelRenderer : LabelRenderer
{
//protected override void OnElementChanged(ElementChangedEventArgs<Label> e)
//{
// base.OnElementChanged(e);
// if (e.NewElement != null)
// {
// var label = e.NewElement as MyLabel;
// if (label != null) //sanity check
// {
// var styleId = label.MyStyleId;
// }
// }
//}
protected override void OnElementPropertyChanged(object sender, PropertyChangedEventArgs e)
{
base.OnElementPropertyChanged(sender, e);
if (e.PropertyName == "Renderer")
{
var label = sender as MyLabel;
if (label != null) //sanity check
{
switch(label.MyStyleId)
{
case "Body":
break;
case "Header":
break;
}
}
}
}
}
}

Resources