Monday 14 May 2018

Xamarin.Forms MVVM: How to set Icon, TitleColor, BorderStyle, ItemsSource for Picker using CustomRenderer

Introduction

In Xamarin.Forms, Picker creation and assigning ItemsSource very simple But there is no direct way set right side down 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 source code is prepared by using Visual Studio. And it is better to install the latest Visual Studio updates from here.
  • This article is prepared on a MAC machine.
  • This sample project is Xamarin.Forms 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 in a Mac system with Visual Studio.
First, open Visual Studio for Mac. And click on New Project.
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.
Then, we have to give the app name; i.e., PickerDemo.

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.
After clicking on the create button it will create the PickerDemo Xamarin.Forms project like below.
And the project structure will be:
  • PickerDemo:  It is for Shared Code
  • PickerDemo.Droid:  It is for Android.
  • PickerDemo.iOS:  It is for iOS
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.   
  3. namespace PickerDemo.CustomControls  
  4. {  
  5.     public class CustomPicker : Picker  
  6.     {  
  7.         public static readonly BindableProperty ImageProperty =  
  8.             BindableProperty.Create(nameof(Icon), typeof(string), typeof(CustomPicker), string.Empty);  
  9.   
  10.         public string Icon  
  11.         {  
  12.             get { return (string)GetValue(ImageProperty); }  
  13.             set { SetValue(ImageProperty, value); }  
  14.         }  
  15.     }  
  16. }  

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"  
  3.     xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"  
  4.     xmlns:custom="clr-namespace:PickerDemo.CustomControls"  
  5.     x:Class="PickerDemo.Views.PickerDemoPage"  
  6.     BackgroundColor="White">  
  7.     <StackLayout VerticalOptions="FillAndExpand" Padding="20,50,20,20"  
  8.         HorizontalOptions="FillAndExpand">  
  9.         <StackLayout Spacing="70">  
  10.             <Label Text="Picker with right icon"   
  11.             FontSize="20" TextColor="#533F95"   
  12.             HorizontalOptions="CenterAndExpand"/>  
  13.             <Label Text="{Binding SelectedItemsValue}"   
  14.             FontSize="20"   
  15.             TextColor="#533F95"   
  16.             HorizontalOptions="CenterAndExpand"/>  
  17.         </StackLayout>  
  18.         <custom:CustomPicker  Icon="picker_right_arrow.png"   
  19.             Title="Select Country"   
  20.             SelectedItem="{Binding SelectedItem}"   
  21.             SelectedIndex="{Binding SelectedIndexChanged}"    
  22.             ItemsSource="{Binding Countries}"   
  23.             HeightRequest="50"   
  24.             TextColor="#533F95"   
  25.             HorizontalOptions="FillAndExpand"   
  26.             VerticalOptions="CenterAndExpand" >  
  27.         </custom:CustomPicker>  
  28.     </StackLayout>  
  29. </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.   
  4. namespace PickerDemo.Views  
  5. {  
  6.     public partial class PickerDemoPage : ContentPage  
  7.     {  
  8.         public PickerDemoPage()  
  9.         {  
  10.             InitializeComponent();  
  11.             BindingContext = new PickerDemoPageViewModel();  
  12.         }  
  13.     }  
  14. }  

In code behind make sure BindingContext with PickerDemoViewModel.


PickerDemoPageViewModel.cs


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


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 Android.Support.V4.Content;  
  4. using Android.Widget;  
  5. using PickerDemo.CustomControls;  
  6. using PickerDemo.Droid;  
  7. using Xamarin.Forms;  
  8. using Xamarin.Forms.Platform.Android;  
  9.   
  10. [assembly: ExportRenderer(typeof(CustomPicker), typeof(CustomPickerRenderer))]  
  11. namespace PickerDemo.Droid  
  12. {  
  13.     public class CustomPickerRenderer : PickerRenderer  
  14.     {  
  15.         CustomPicker element;  
  16.   
  17.         protected override void OnElementChanged(ElementChangedEventArgs<Picker> e)  
  18.         {  
  19.             base.OnElementChanged(e);  
  20.   
  21.             element = (CustomPicker)this.Element;  
  22.   
  23.             if (Control != null && this.Element != null && !string.IsNullOrEmpty(element.Icon))  
  24.             {  
  25.                 Control.Background = AddPickerStyles(element.Icon);  
  26.                 Control.SetHintTextColor(Android.Graphics.Color.Rgb(83, 63, 149));
  27.             }  
  28.         }  
  29.   
  30.         public LayerDrawable AddPickerStyles(string imagePath)  
  31.         {  
  32.             ShapeDrawable border = new ShapeDrawable();  
  33.             border.Paint.Color = Android.Graphics.Color.Gray;  
  34.             border.SetPadding(10, 10, 10, 10);  
  35.             border.Paint.SetStyle(Paint.Style.Stroke);  
  36.   
  37.             Drawable[] layers = { border, GetDrawable(imagePath) };  
  38.             LayerDrawable layerDrawable = new LayerDrawable(layers);  
  39.             layerDrawable.SetLayerInset(0, 0, 0, 0, 0);  
  40.   
  41.             return layerDrawable;  
  42.         }  
  43.   
  44.         private BitmapDrawable GetDrawable(string imagePath)  
  45.         {  
  46.             var drawable = Resources.GetDrawable(imagePath);  
  47.             var bitmap = ((BitmapDrawable)drawable).Bitmap;  
  48.   
  49.             var result = new BitmapDrawable(Resources, Bitmap.CreateScaledBitmap(bitmap, 70, 70, true));  
  50.             result.Gravity = Android.Views.GravityFlags.Right;  
  51.   
  52.             return result;  
  53.         }  
  54.     }  
  55. }  

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 taken from Resources what ever we 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.   
  7. [assembly: ExportRenderer(typeof(CustomPicker), typeof(CustomPickerRenderer))]  
  8. namespace PickerDemo.iOS  
  9. {  
  10.     public class CustomPickerRenderer : PickerRenderer  
  11.     {  
  12.         protected override void OnElementChanged(ElementChangedEventArgs<Picker> e)  
  13.         {  
  14.             base.OnElementChanged(e);  
  15.   
  16.             var element = (CustomPicker)Element;  
  17.   
  18.             if (Control != null && Element != null && !string.IsNullOrEmpty(element.Icon))  
  19.             {  
  20.                 var downarrow = UIImage.FromBundle(element.Icon);  
  21.                 Control.RightViewMode = UITextFieldViewMode.Always;  
  22.                 Control.RightView = new UIImageView(downarrow);  
  23.                 Control.TextColor = UIColor.FromRGB(83, 63, 149);  
  24.                 //Control.BackgroundColor = UIColor.Clear;  
  25.                 Control.AttributedPlaceholder = new Foundation.NSAttributedString(Control.AttributedPlaceholder.Value, foregroundColor: UIColor.FromRGB(83, 63, 149));  
  26.                 //Control.BorderStyle = UITextBorderStyle.RoundedRect;  
  27.                 //Layer.BorderWidth = 1.0f;  
  28.                 //Layer.CornerRadius = 4.0f;  
  29.                 //Layer.MasksToBounds = true;  
  30.                 //Layer.BorderColor = UIColor.FromRGB(83, 63, 149).CGColor;  
  31.             }  
  32.         }  
  33.     }  
  34. }  
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 Icon.
  2. TextColor
  3. Title Color i.e, AttributedPlaceholder.
  4. BorderColor.
Output:


Source Code:


No comments:

Post a Comment