1

I'm trying to search how to create a custom picker on Xamarin but I have no idea how to do it.

Here is what I want to do

enter image description here

I don't even know if I need to install a nuget package. Please help and thanks.

behroozbc
  • 1,992
  • 2
  • 12
  • 25
robluc
  • 121
  • 12
  • see https://www.hiimray.co.uk/2019/10/14/xamarin-forms-quick-and-easy-custom-picker-with-a-more-traditional-look/297 – Mykyta Halchenko May 04 '22 at 09:28
  • @Skalpel02 it sais PickerRenderer is deprecated – robluc May 04 '22 at 09:33
  • Does this answer your question? [Custom Picker Xamarin Android](https://stackoverflow.com/questions/63519896/custom-picker-xamarin-android) – Mykyta Halchenko May 04 '22 at 09:36
  • @Skalpel02 The thing is I don't know how to use it on my view – robluc May 04 '22 at 09:39
  • See doc [Xamarin.Forms Custom Renderers](https://learn.microsoft.com/en-us/xamarin/xamarin-forms/app-fundamentals/custom-renderer/). The `Introduction` page has an example of a customized entry `public class MyEntry : Entry ...` and xaml ``. In your case, it would be similar to `public class MyPicker : Picker ...` with ``. – ToolmakerSteve May 04 '22 at 12:51

3 Answers3

0

As mentioned by @Skalpel02, you need to sub-class the Picker class and implement the corresponding Renderers in each platform. There, you have the ability to interact with native APIs of the platform.

Ali
  • 121
  • 7
  • I'm following the link he provided but it is giving error on `ElementChangedEventArgs` on my `OnElementChanged` Android renderer class method – robluc May 04 '22 at 09:36
  • @robluc - Add to your question the code you now have, and the exact error message (copy/paste its text). IMPORTANT: The android custom renderer class must be in your `.Android` project (not in the project that has the xaml files). – ToolmakerSteve May 04 '22 at 12:49
  • @robluc - Maybe [this xamgirl picker example](https://xamgirl.com/picker-with-right-side-icon-in-xamarin-forms/) will help you understand how to work with picker custom renderers. – ToolmakerSteve May 04 '22 at 13:27
  • @ToolmakerSteve I'm following that link. I'm hoping that will work, thanks! – robluc May 04 '22 at 15:12
  • @ToolmakerSteve I have followed that code but it gives me error on the image. Mine it's an svg. Why is that happening? It's giving null reference – robluc May 04 '22 at 15:24
  • Using an svg requires [extra code](https://stackoverflow.com/questions/48158751/how-to-use-svg-images-correctly-in-xamarin-forms-android). I recommend first getting it to work with a png. – ToolmakerSteve May 06 '22 at 00:53
0

This could be implemented by custom renderer.

First,a custom Picker control can be created by subclassing the Picker control, as shown in the following code:

    public class BorderlessPicker : Picker
    {
        public BorderlessPicker() : base()
        {
        }
    }

Second:Create the Custom Renderer on each Platform,Override the OnElementChanged method and write logic to customize the control,then Add an ExportRenderer attribute to the custom renderer class to specify that it will be used.

In Android:

[assembly: ExportRenderer(typeof(BorderlessPicker), typeof(BordlessPickerRenderer))]
 namespace AppPicker01.Droid
{
public class BordlessPickerRenderer : PickerRenderer
{
    public BordlessPickerRenderer(Context context) : base(context)
    {
    }
    protected override void OnElementChanged(ElementChangedEventArgs<Picker> e)
    {
        base.OnElementChanged(e);

        if (e.OldElement == null)
        {
            Control.Background = null;
        }
    }
}

}

In iOS:

[assembly: ExportRenderer(typeof(BorderlessPicker), typeof(BorderlessPickerRenderer))]
namespace AppPicker01.iOS
   {
         public class BorderlessPickerRenderer : PickerRenderer
        {
            protected override void OnElementChanged(ElementChangedEventArgs<Picker> e)
        {
            base.OnElementChanged(e);

            if (Control == null)
            {
                return;
            }

            Control.Layer.BorderWidth = 0;
            Control.BorderStyle = UITextBorderStyle.None;
        }
    }
}

Last but not least, consume the custom picker control in Xaml:

  <apppicker01:BorderlessPicker Title="Select a color" ItemsSource="{Binding ColorNames}" SelectedItem="{Binding SelectedColorName, Mode=TwoWay}" />

Screenshot:

enter image description here

MS official docs link: https://learn.microsoft.com/en-us/xamarin/xamarin-forms/app-fundamentals/custom-renderer/

Alexandar May - MSFT
  • 6,536
  • 1
  • 8
  • 15
0

You can easily create your own control that doesn't need a renderer and works on iOS, Android, and UWP. Here my solution. You have to create a View "PickerCustom" for the control

<?xml version="1.0" encoding="UTF-8"?>
<ContentView xmlns="http://xamarin.com/schemas/2014/forms" 
             xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
             x:Class="___YOURCLASS"
             xmlns:xmleditor="clr-namespace:XmlEditor" HorizontalOptions="FillAndExpand" BackgroundColor="#ddd">
    <StackLayout x:Name="stack" Orientation="Horizontal" HorizontalOptions="FillAndExpand" Margin="1" BackgroundColor="#fff" Padding="5">
        <Label Text="{Binding TextValue}" Margin="0" HorizontalOptions="FillAndExpand" VerticalOptions="FillAndExpand"/>
        <ImageButton BackgroundColor="#ffffff" Source="dropdown.png" x:Name="img" WidthRequest="20"></ImageButton>
        <Entry WidthRequest="0"></Entry>
    </StackLayout>
</ContentView>

with this code behind

public partial class PickerCustom : ContentView
    {
        public PickerCustom()
        {
            InitializeComponent();
            Items = new ObservableCollection<CustomItem>();
            SelectedIndex = -1;
            BindingContext = this;

            TapGestureRecognizer tap0 = new TapGestureRecognizer();
            tap0.Tapped +=  (sender, e) =>
            {
                img.Focus();

                PickerCustomList pcl = new PickerCustomList();
                pcl.Items = this.Items;
                App.Current.MainPage.Navigation.PushModalAsync(pcl);
                MessagingCenter.Subscribe<PickerCustomList>(this, "finish", (sender1) =>
                {
                    MessagingCenter.Unsubscribe<PickerCustomList>(this, "finish");
                    img.Focus();
                    if(((PickerCustomList)sender1).SelectedIndex != -1)
                    {
                        SelectedIndex = ((PickerCustomList)sender1).SelectedIndex;
                    }
                });
            };
            GestureManager.AddGesture(stack, tap0);
        }

        string _textvalue = "";
        public string TextValue
        {
            get
            {
                return _textvalue;
            }
            set
            {
                _textvalue = value;
                OnPropertyChanged();
            }
        }

        public ObservableCollection<CustomItem> Items { get; set; }

        int _selectedIndex = 0;
        public int SelectedIndex
        {
            get
            {
                return _selectedIndex;
            }
            set
            {
                _selectedIndex = value;
                if(_selectedIndex>= Items.Count)
                {
                    _selectedIndex = -1;
                } else if (_selectedIndex != -1)
                {
                    TextValue = Items[SelectedIndex].Name;
                }
                else
                {
                    TextValue = "";
                }
                OnPropertyChanged();
            }
        }  
    }

    public class CustomItem
    {
        public CustomItem(string _name)
        {
            name = _name;
        }

        private string name;

        public string Name
        {
            get { return name; }
            set { name = value; }
        }
    }

And a View "PickerCustomList" for the choice

    <?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="___YOURCLASS" BackgroundColor="#66aaaaaa"
             x:Name="ContentPage1" Padding="30,100,30,100" >
    <ListView x:Name="ContactsList" ItemsSource="{Binding Items}" IsVisible="True"
                VerticalOptions="Start" HorizontalOptions="Center"
                BackgroundColor="Transparent" HasUnevenRows="True">
        <ListView.Header  HorizontalOptions="FillAndExpand">
            <StackLayout Orientation="Horizontal" HorizontalOptions="FillAndExpand" BackgroundColor="#f0f0f0" >
                <ImageButton Source="close.png" WidthRequest="20"  Clicked="Button_Clicked" Margin="10,5,10,5" BackgroundColor="Transparent"></ImageButton>
            </StackLayout>
        </ListView.Header>
        <ListView.ItemTemplate>
            <DataTemplate>
                <ViewCell Tapped="ViewCell_Tapped" >
                    <StackLayout BackgroundColor="#ffffff">
                        <Label Text="{Binding Name}" Padding="10"></Label>
                        <ContentView HeightRequest="1" BackgroundColor="#666"></ContentView>
                    </StackLayout>
                </ViewCell>
            </DataTemplate>
        </ListView.ItemTemplate>
    </ListView>
</ContentPage>

with this code behind

public partial class PickerCustomList : ContentPage
    {
        public int SelectedIndex = -1;
        ObservableCollection<CustomItem> myItems= new ObservableCollection<CustomItem>();
        public ObservableCollection<CustomItem> Items
        {
            get { return myItems; }
            set { 
                myItems = value;
                OnPropertyChanged();
            }
        }

        public PickerCustomList()
        {
            InitializeComponent();
            BindingContext = this;
        }

        private void Button_Clicked(object sender, EventArgs e)
        {
            SelectedIndex = -1;
            App.Current.MainPage.Navigation.PopModalAsync();
            MessagingCenter.Send<PickerCustomList>(this, "finish");
        }

        private void ViewCell_Tapped(object sender, EventArgs e)
        {
            SelectedIndex = Items.IndexOf(((CustomItem)((ViewCell)sender).BindingContext));
            App.Current.MainPage.Navigation.PopModalAsync();
            MessagingCenter.Send<PickerCustomList>(this, "finish");
        }
    }

Picker1 Picker2

Renzo Ciot
  • 3,746
  • 2
  • 25
  • 29