Forum Xamarin.Forms
We are excited to announce that the Xamarin Forums are moving to the new Microsoft Q&A experience. Q&A is the home for technical questions and answers at across all products at Microsoft now including Xamarin!

We encourage you to head over to Microsoft Q&A for .NET for posting new questions and get involved today.

Can Bindable Picker show a different class property in the selection list and another as the value?

ColtColt Member

I have a situation where the user needs to select a single class from a long list of instances of that same class.

I am using a BindablePicker with its ItemsSource set to a List<> of those classes.

The classes themselves show a rather lengthy "Name" property in their ToString() method, so when the selection list expands to fill the screen, it looks great with long, human readable names showing on each line.

But the BindablePicker itself has to share its screen space with a lot of other input fields, so I cannot dedicate the entire width of the screen to displaying that long, human readable name when the selection list is not showing. Currently that long "Name" property is being cut off and looks very unprofessional.

I am hoping that there is a way that when the selection list is not visible, the control itself will display a different property, "Code" which is short enough to fit in the picker's unexpanded space

Do I need to go to a third party control (in which case, any recommendations?) or can I somehow trick the class's ToString() method into returning different values when shown in the expanded Selection list than in the unexpanded control view.

Answers

  • LandLuLandLu Member, Xamarin Team Xamurai

    Try to define a converter for your Picker to change the values.
    Here is my converter:

    public class NameConverter : IValueConverter
    {
        public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
        {
            User user = value as User;
            if (user.Name.Length > 20)
            {
                return user.Code;
            }
            return user.Name;
        }
    
        public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture)
        {
            throw new NotImplementedException();
        }
    }
    

    Replace it to code when the user is too long.
    And then consume this converter on Picker like:

    <ContentPage.Resources>
        <local:NameConverter x:Key="NameConverter"/>
    </ContentPage.Resources>
    
    <StackLayout>
        <Picker ItemsSource="{Binding Users}" ItemDisplayBinding="{Binding ., Converter={x:StaticResource NameConverter}}"/>
    </StackLayout>
    
  • ColtColt Member

    Thank you Land Lu,

    That gets me closer to what I was hoping for, it makes the Code field appear in the unexpanded control, but now the expanded list is showing Codes field values too, instead of human readable Name field values I had before.

    Is there any way to only apply the converter to just the expanded list or just the unexpanded selected value?

  • LandLuLandLu Member, Xamarin Team Xamurai

    We need to create custom renderer on each platform to achieve this.
    On Android:

    [assembly: ExportRenderer(typeof(Picker), typeof(MyPickerRenderer))]
    namespace Sample.Droid
    {
        public class MyPickerRenderer : Xamarin.Forms.Platform.Android.AppCompat.PickerRenderer
        {
            public MyPickerRenderer(Context context) : base(context)
            {
            }
    
            protected override void OnElementChanged(ElementChangedEventArgs<Picker> e)
            {
                base.OnElementChanged(e);
    
                Element.SelectedIndexChanged += Element_SelectedIndexChanged;
            }
    
            private void Element_SelectedIndexChanged(object sender, EventArgs e)
            {
                var model = Element.SelectedItem as User;
                if (model.Name.Length > 20)
                {
                    Control.Text = model.Code;
                }
            }
        }
    }
    

    iOS:

    [assembly: ExportRenderer(typeof(Picker), typeof(MyPickerRenderer))]
    namespace Sample.iOS
    {
        public class MyPickerRenderer : PickerRenderer
        {
            protected override void OnElementChanged(ElementChangedEventArgs<Picker> e)
            {
                base.OnElementChanged(e);
    
                if (e.NewElement != null)
                {
                    Element.SelectedIndexChanged += Element_SelectedIndexChanged;
                }
            }
    
            private void Element_SelectedIndexChanged(object sender, EventArgs e)
            {
                var model = Element.SelectedItem as User;
                if (model.Name.Length > 20)
                {
                    Control.Text = model.Code;
                }
            }
        }
    }
    

    Do not forget to remove the converter in Forms.

Sign In or Register to comment.