Xamarin.Forms MVVM - How To Set Icon, TitleColor, BorderStyle For Picker Using CustomRenderer

Introduction

In Xamarin.Forms, the creation of picker and assigning ItemsSource are very simple tasks but there is no direct way to set the right side down arrow Icon like iOS, Android native, TitleColor, BorderColor. So in this article, we will learn how to set those properties for Picker using CustomRenderer.

Requirements

  • This article's source code is prepared by using Visual Studio. It is better to install the latest Visual Studio updates from here.
  • This article is prepared on a MAC machine.
  • This sample project is in Xamarin.Forms of PCL project.
  • This sample app is targeted for Android, iOS. And tested for Android & iOS.

Description

The creation of a Xamarin.Forms project is very simple in Visual Studio for Mac. It will create three projects,

  1. Shared Code
  2. Xamarin.Android
  3. Xamarin.iOS

The Mac system with Visual Studio for Mac doesn't support Windows projects (UWP, Windows, Windows Phone).

The following steps will show you how to create Xamarin.Forms projects on a Mac system using Visual Studio.

First, open Visual Studio for Mac. and click on New Project.

Xamarin

After that, we need to select whether you're doing Xamarin.Forms or Xamarin.Android or Xamarin.iOS project. If we want to create Xamarin.Forms project, just follow the below screenshot.

Xamarin

Then, we have to give the app name; i.e., PickerDemo.

Xamarin

Note

In the above screen, under Shared Code, select Portable Class Library or use Shared Library.

Then, click on Next button and the following screenshot will be displayed. In that screen, we have to browse the file path where we want to save that application on our PC.

Xamarin

After clicking on the create button it will create the PickerDemo Xamarin.Forms project like below.

Xamarin

And the project structure will be,

  • PickerDemo
    It is for Shared Code

  • PickerDemo.Droid
    It is for Android.

  • PickerDemo.iOS
    It is for iOS

Xamarin

Now, follow the below steps.

Portable Class Library (PCL)

Step 1

In PCL, create a class name CustomPicker.cs inside the CustomControls folder which should inherit Picker like below.

CustomPicker.cs

  1. using Xamarin.Forms;  
  2. namespace PickerDemo.CustomControls {  
  3.     public class CustomPicker: Picker {  
  4.         public static readonly BindableProperty ImageProperty = BindableProperty.Create(nameof(Icon), typeof(string), typeof(CustomPicker), string.Empty);  
  5.         public string Icon {  
  6.             get {  
  7.                 return (string) GetValue(ImageProperty);  
  8.             }  
  9.             set {  
  10.                 SetValue(ImageProperty, value);  
  11.             }  
  12.         }  
  13.     }  
  14. }  

Added a Bindable property for Picker right side Icon in the above class.

Step 2

Create your own XAML page named PickerDemoPage.xaml inside the Views folder and make sure to refer to "CustomPicker" class in XAML by declaring a namespace for its location and using the namespace prefix on the control element. The following code example shows how the "CustomPicker" renderer class can be consumed by an XAML page,

PickerDemoPage.xaml

  1. <?xml version="1.0" encoding="UTF-8"?>  
  2. <ContentPage xmlns="http://xamarin.com/schemas/2014/forms" xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml" xmlns:custom="clr-namespace:PickerDemo.CustomControls" x:Class="PickerDemo.Views.PickerDemoPage" BackgroundColor="White">  
  3.     <StackLayout VerticalOptions="FillAndExpand" Padding="20,50,20,20" HorizontalOptions="FillAndExpand">  
  4.         <StackLayout Spacing="70">  
  5.             <Label Text="Picker with right icon" FontSize="20" TextColor="#533F95" HorizontalOptions="CenterAndExpand" />  
  6.             <Label Text="{Binding SelectedItemsValue}" FontSize="20" TextColor="#533F95" HorizontalOptions="CenterAndExpand" />   
  7.          </StackLayout>  
  8.         <custom:CustomPicker Icon="picker_right_arrow.png" Title="Select Country" SelectedItem="{Binding SelectedItem}" SelectedIndex="{Binding SelectedIndexChanged}" ItemsSource="{Binding Countries}" HeightRequest="50" TextColor="#533F95" HorizontalOptions="FillAndExpand" VerticalOptions="CenterAndExpand"</custom:CustomPicker>  
  9.     </StackLayout>  
  10. </ContentPage>  

Note
The "custom" namespace prefix can be named anything. However, the clr-namespace and assembly values must match the details of the custom renderer class. Once the namespace is declared the prefix is used to reference the custom control.

PickerDemoPage.xaml.cs

  1. using PickerDemo.ViewModels;  
  2. using Xamarin.Forms;  
  3. namespace PickerDemo.Views {  
  4.     public partial class PickerDemoPage: ContentPage {  
  5.         public PickerDemoPage() {  
  6.             InitializeComponent();  
  7.             BindingContext = new PickerDemoPageViewModel();  
  8.         }  
  9.     }  
  10. }  

In code behind make sure BindingContext is with PickerDemoViewModel. 

PickerDemoPageViewModel.cs

  1. using System.Collections.Generic;  
  2. using System.ComponentModel;  
  3. using System.Runtime.CompilerServices;  
  4.  
  5. namespace PickerDemo.ViewModels {  
  6.     public class PickerDemoPageViewModel: INotifyPropertyChanged {  
  7.         string _selectedItem;  
  8.         public string SelectedItem {  
  9.             get => _selectedItem;  
  10.             set {  
  11.                 _selectedItem = value;  
  12.                 NotifyPropertyChanged("SelectedItemsValue");  
  13.             }  
  14.         }  
  15.         public string SelectedItemsValue => string.IsNullOrEmpty(SelectedItem) ? "" : "Selected Item: " + SelectedItem;  
  16.         List < string > countries = new List < string > {  
  17.             "India",  
  18.             "Afghanistan",  
  19.             "Albania",  
  20.             "Algeria",  
  21.             "Andorra",  
  22.             "Angola",  
  23.             "USA",  
  24.             "United Kingdom"  
  25.         };  
  26.         public List<string> Countries => countries;
  27.         #region INotifyPropertyChanged  
  28.         public event PropertyChangedEventHandler PropertyChanged;  
  29.         protected void NotifyPropertyChanged([CallerMemberName] string propertyName = "") {  
  30.             PropertyChanged?.Invoke(thisnew PropertyChangedEventArgs(propertyName));  
  31.         }  
  32.         #endregion  
  33.     }  
  34. }  

Xamarin.Android 

In an Android project, create a class that should inherit PickerRenderer and add the code like below.

CustomPickerRenderer.cs

  1. using Android.Graphics;  
  2. using Android.Graphics.Drawables;    
  3. using PickerDemo.CustomControls;  
  4. using PickerDemo.Droid;  
  5. using Xamarin.Forms;  
  6. using Xamarin.Forms.Platform.Android;  
  7. [assembly: ExportRenderer(typeof(CustomPicker), typeof(CustomPickerRenderer))]  
  8. namespace PickerDemo.Droid {  
  9.     public class CustomPickerRenderer: PickerRenderer {  
  10.             CustomPicker element;  
  11.             protected override void OnElementChanged(ElementChangedEventArgs<Picker> e) {  
  12.                 base.OnElementChanged(e);  
  13.                 element = (CustomPicker)this.Element;  
  14.                 if (Control != null && this.Element != null && !string.IsNullOrEmpty(element.Icon)) {  
  15.                     Control.Background = AddPickerStyles(element.Icon);  
  16.                     Control.SetHintTextColor(Android.Graphics.Color.#533f95);  
  17. }   
  18. }   
  19. public LayerDrawable AddPickerStyles(string imagePath)   
  20. {   
  21. ShapeDrawable border = new ShapeDrawable(); 
  22. border.Paint.Color = Android.Graphics.Color.Gray; 
  23. border.SetPadding(10, 10, 10, 10); 
  24. border.Paint.SetStyle(Paint.Style.Stroke); 
  25. Drawable[] layers = {  
  26.                             border,  
  27.                             GetDrawable(imagePath)  
  28.                         }; 
  29. LayerDrawable layerDrawable = new LayerDrawable(layers); 
  30. SetLayerInset(0, 0, 0, 0, 0);  
  31.                         return layerDrawable;  
  32.                     }  
  33.                     private BitmapDrawable GetDrawable(string imagePath) {  
  34.                         var drawable = Resources.GetDrawable(imagePath);  
  35.                         var bitmap = ((BitmapDrawable)drawable).Bitmap;  
  36.                         var result = new BitmapDrawable(Resources, Bitmap.CreateScaledBitmap(bitmap, 70, 70, true));  
  37.                        result.Gravity = Android.Views.GravityFlags.Right;  
  38.                         return result;  
  39.                     }  
  40.                 }  
  41.             }  

The call to the base class's OnElementChanged method instantiates an Android Picker, with a reference to the control being assigned to the renderer's property. Here we are loading the drawable dynamically with the help of LayerDrawable, LayerDrawable to combine a drawable that will be our border with the drawable image of our right side icon. And the assigned below properties and events are for Picker.

Properties

  1. Icon - it should be taken from Resources whatever we are passing from PCL.
  2. Title Color i.e, SetHintTextColor
  3. BorderColor
  4. SetPadding

Xamarin.iOS

In iOS project, create a class that should inherit PickerRenderer and add the code like below.

CustomPickerRenderer.cs

  1. using PickerDemo.CustomControls;  
  2. using PickerDemo.iOS;  
  3. using UIKit;  
  4. using Xamarin.Forms;  
  5. using Xamarin.Forms.Platform.iOS;  
  6. [assembly: ExportRenderer(typeof(CustomPicker), typeof(CustomPickerRenderer))]  
  7. namespace PickerDemo.iOS {  
  8.     public class CustomPickerRenderer: PickerRenderer {  
  9.         protected override void OnElementChanged(ElementChangedEventArgs<Picker> e) {  
  10.             base.OnElementChanged(e);  
  11.             var element = (CustomPicker) Element;  
  12.             if (Control != null && Element != null && !string.IsNullOrEmpty(element.Icon)) {  
  13.                 var downarrow = UIImage.FromBundle(element.Icon);  
  14.                 RightViewMode = UITextFieldViewMode.Always;  
  15.                 RightView = new UIImageView(downarrow);  
  16.                 TextColor = UIColor.From#533f95;   
  17. //Control.BackgroundColor = UIColor.Clear;  
  18.                 AttributedPlaceholder = new Foundation.NSAttributedString(Control.AttributedPlaceholder.Value, foregroundColor: UIColor.FromRGB(83,63,149);   
  19. //Control.BorderStyle = UITextBorderStyle.RoundedRect;  
  20.                     //Layer.BorderWidth = 1.0f;   
  21.                     //Layer.CornerRadius = 4.0f;   
  22.                     //Layer.MasksToBounds = true;   
  23.                     //Layer.BorderColor = UIColor.FromRGB(83,63,149.CGColor;   
  24.                 }  
  25.             }  
  26.         }  
  27.     }  

The call to the base class's method OnElementChanged instantiates an iOS control UIPickerView, with a reference to the control being assigned to the renderer's property Control. The native picker has a property called RightView in which you can set a view. In our case we are create an ImageView and set it to the RightView. And the assigned below properties and Events are for Picker.

Properties

  1. RightView i.e, for Picker right side Icon.
  2. TextColor
  3. AttributedPlaceholder i.e, for Title Color.
  4. BorderColor.
Output:

Xamarin 

Please download the sample from here.