Adjusting font size of Xamarin’s Picker

When you’re developing the front-end side of a Xamarin’s app, you’re demanded to put and arrange every UI component in the right place, with right size and right color.  When it comes to size, it’s kinda troublesome to adjust picker to precise font size. The font size of picker in Xamarin, including DatePicker and TimePicker, is simply fixed. So, in this post, we will discuss about a workaround method to approach this problem. With this method, you will be even able to bind the font size of the picker to certain variable in view model if you want to. Note: so far, it works in Android, I still haven’t tried it in iOS. I’ll update this post or make another post after try it in iOS.

Custom Picker Class

The first step is we will create the custom class of the picker. This custom class will inherit picker class and we’ll add one binding property, font size, to this class. The following code is the example of the custom class.


 public class CustomPicker : Picker
    {
        public static readonly BindableProperty FontSizeProperty =
            BindableProperty.Create(nameof(FontSize), typeof(Int32), typeof(CustomPicker),
                10, BindingMode.Default);
        public Int32 FontSize
        {
            get => (Int32)GetValue(FontSizeProperty);
            set => SetValue(FontSizeProperty, value);
        }
    }

Updating Picker UI

The second step, we move on to the Android project. This part is just about the UI, nothing to do with the functionality of the picker. You can ignore and jump to next step if you feel don’t need it. So, in this part, I made and axml file which define the the background of the picker. As you know, standard Xamarin is just a text with underline, not so eye catching. So, I make little bit modification on it. I put the file in Drawable folder inside Resources.


<?xml version="1.0" encoding="utf-8"?>
<selector xmlns:android="http://schemas.android.com/apk/res/android">
  <item android:state_enabled="false" >
    <shape android:shape="rectangle">
      <solid android:color="#EAEAEC"/>
      <corners android:radius="0dp" />
      <stroke android:width="1dp" android:color="#118E8C" />
    </shape>
  </item>

  <item>
    <shape android:shape="rectangle">
      <solid android:color="#80ffffff"/>
      <corners android:radius="1dp" />
      <stroke android:width="1dp" android:color="#118E8C" />
    </shape>
  </item>

</selector>

Custom Picker Renderer

The third step is the most important part. We gonna make custom renderer of the picker. Inheriting from PickerRenderer class, we will adjust the text size property of the picker base on font size property that we’ve created earlier. I use 0.01 as the multiplier just because it suit my need in my previous project, you can change it to meet your need if you feel need to.


 class CustomPickerRenderer : PickerRenderer
    {
        protected override void OnElementChanged(ElementChangedEventArgs<Picker> e)
        {
            base.OnElementChanged(e);
            this.Control.Background = Forms.Context.GetDrawable(Resource.Drawable.customPicker);
            if (e.OldElement != null || e.NewElement != null)
            {
                var customPicker = e.NewElement as CustomPicker;
                Control?.SetPadding(20, 0, 0, 0);
                Control.TextSize *= (customPicker.FontSize * 0.01f);
            }

        }
    }

Implementation

Now let’s try it out. The following codes if the sample of xaml file where I use the custom picker, followed by the view model.


<?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:control="clr-namespace:App1.CustomControls" x:Class="App1.Pages.PickerPage">
    <ContentPage.Content>
        <StackLayout VerticalOptions="Center" Padding="30,0,30,0">
            <Label Text="Choose Your Origin!" TextColor="#118E8C" FontSize="Large"/>
            <control:CustomPicker Title="Select Country" ItemsSource="{Binding CountryList}" FontSize="48"/>
            <control:CustomPicker Title="Select State" ItemsSource="{Binding StateList}" FontSize="42"/>
            <control:CustomPicker Title="Select City" ItemsSource="{Binding CityList}" FontSize="36"/>
        </StackLayout>
    </ContentPage.Content>
</ContentPage>

 public class PickerViewModel : INotifyPropertyChanged
    {

        private List<string> countryList;
        private List<string> stateList;
        private List<string> cityList;

        public List<string> CountryList
        {
            get => countryList;
            set => SetObservableProperty(ref countryList, value);
        }

        public List<string> StateList
        {
            get => stateList;
            set => SetObservableProperty(ref stateList, value);
        }

        public List<string> CityList
        {
            get => cityList;
            set => SetObservableProperty(ref cityList, value);
        }

        public event PropertyChangedEventHandler PropertyChanged;

        public PickerViewModel()
        {
            CountryList = new List<string>() { "USA", "China", "Russia" };
            StateList = new List<string>() { "California", "Sichuan", "Kirov" };
            CityList = new List<string>() { "Los Angeles", "Guangzhou", "St. Petersburg" };
        }

        protected void SetObservableProperty<T>(ref T field, T value,
            [CallerMemberName] string propertyName = "")
        {
            if (EqualityComparer<T>.Default.Equals(field, value)) return;
            field = value;
            OnPropertyChanged(propertyName);
        }

        protected virtual void OnPropertyChanged(string propertyName)
            => PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
    }
[XamlCompilation(XamlCompilationOptions.Compile)]
    public partial class PickerPage : ContentPage
    {
        public PickerPage()
        {
            InitializeComponent();
            BindingContext = new PickerViewModel();
        }
    }

When you run the project, you will have three different picker with three different size. You can try to bind the font size property with any integer variable you have within your view model.

 

Update : Set Placeholder Color of Xamarin’s Picker

Checkbox in Xamarin Forms

Xamarin Forms has many default UI components that can be used by developers. Unfortunately, one of most common components, checkbox is not one of them. So, in this post, I want to discuss about a workaround method that I used to create checkbox in Xamarin Forms. I’ve created a simple To-Do list form to show you how it works, but you will need two images, checked and unchecked box.

Creating Entity Class

First, we have to create an entity class to represent the to-do items. The following code shows you the example of simple entity class. I have one property called IsDone which will determine the status of the item on the list. I also have another property called ImageCheckBox, that I purposely put in different partial class just for tidiness sake, that will determine the image that will be shown base on the item status.


public partial class TodoItem
{
    public string Id { get; set; }
    public string ItemDescription { get; set; }
    public bool IsDone { get; set; }
}

public partial class TodoItem
{
    public string ImageCheckBox
    {
        get
        {
            if (IsDone)
                return "Checked.png";
            else
                return "Unchecked.png";
        }
    }
}

Custom Observable Collection

The next thing we need is a custom class of Observable Collection. Why we need this? Because we gonna put the checkbox in every item on list. It means we need to notify the UI if there’s a change in a single item of the list. But, it’s not how the traditional Observable Collection works. It will only notify the UI if there’s change to list, not to the item on list. If new item’s added to list or removed, to UI will be notified but if the change happen to the list item, in this case we want to change the image of the check box, the UI won’t be notified. So, this is the Custom Observable looks like.


public class CustomObservableCollection : ObservableCollection
{
    public CustomObservableCollection() { }
    public CustomObservableCollection(IEnumerable items) : this()
    {
        foreach (var item in items)
            this.Add(item);
    }
public void ReportItemChange(T item)
    {
        NotifyCollectionChangedEventArgs args =
            new NotifyCollectionChangedEventArgs(
                NotifyCollectionChangedAction.Replace,
                item,
                item,
                IndexOf(item));
        OnCollectionChanged(args);
    }
}

View Model Implementation

Next, we move on to the view model. The one thing that need to be noticed in this view model is the UpdateCheckBox method, which is called from UpdateCheckBoxCommand command, which will be bind to XAML file. That method contains mechanism to notify the list that one certain item is changed.


public class ToDoViewModel : INotifyPropertyChanged
{
    private CustomObservableCollection todoList;
    public CustomObservableCollection ToDoList
    {
        get => todoList;
        set => SetObservableProperty(ref todoList, value);
    }

    public ICommand UpdateCheckBoxCommand { get; set; }

    public ToDoViewModel()
    {
        ToDoList = new CustomObservableCollection()
        {
            new TodoItem(){ Id = "1", ItemDescription = "Task 1", IsDone = false},
            new TodoItem(){ Id = "2", ItemDescription = "Task 2", IsDone = false},
            new TodoItem(){ Id = "3", ItemDescription = "Task 3", IsDone = false},
            new TodoItem(){ Id = "4", ItemDescription = "Task 4", IsDone = false},
                new TodoItem(){ Id = "5", ItemDescription = "Task 5", IsDone = false},
        };

        UpdateCheckBoxCommand = new Command((Id) => UpdateCheckBox(Id));
    }

    private void UpdateCheckBox(string id)
    {
        TodoItem item = ToDoList.FirstOrDefault(x => x.Id == id);
        if (item.IsDone) item.IsDone = false;
        else item.IsDone = true;

        ToDoList.ReportItemChange(item);
    }

    public event PropertyChangedEventHandler PropertyChanged;

    protected void SetObservableProperty(ref T field,T value,
        [CallerMemberName] string propertyName = "")
    {
        if (EqualityComparer.Default.Equals(field, value)) return;
        field = value;
        OnPropertyChanged(propertyName);
    }

    protected virtual void OnPropertyChanged(string propertyName)
        => PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
}

Command Parameter

And then, we’re going to the XAML file, where we bind the data and the command. One this to notice in this file, we gonna passing a parameter to the command. The parameter is the Id of the item that we want to mark as done or undone. To pass the parameter to a command, first we need to give name to content page page. The name of the content name will be referenced by the command. Notice that I put same name in the x:Name property of Content Page and x:Reference of the Command. The path of the command is method name in view model, and command parameter is the method’s parameter.

checkbox

Final Touch

And now all has been set up, lastly we just need to bind the view model as the binding context to XAML.


[XamlCompilation(XamlCompilationOptions.Compile)]
    public partial class TodoPage : ContentPage
    {
        public TodoPage()
        {
            InitializeComponent();
            BindingContext = new ToDoViewModel();
        }
    }

Now if you run the project, you will find a list consisting of 5 task items with blank check box in the right side. If you want to mark one the them as done task, you can just press the check box and it will trigger the list to change the image and give you sense of using real check box.

UPDATE : Bindable Check Box and Radio Button