1
votes

The following example works fine (https://developer.xamarin.com/samples/xamarin-forms/UserInterface/BindablePicker/)

When i try to implement it in my code, the object referenced for selectedItem is not being set. The picker is loading and selecting data fine, just not updating the object.

Here is some of the code i'm using: XAML Page

 <Picker x:Name="testpicker" Title="Select a Service" ItemsSource="{Binding Services}, Mode=TwoWay}" ItemDisplayBinding="{Binding ServiceDescription}" SelectedItem="{Binding SelectedServiceName, Mode=TwoWay}" />

I have the object in the view model, but this is never called when the picker items are selected.:

string selectedServiceName;
    public string SelectedServiceName
    {
        get { return selectedServiceName; }
        set
        {
            if (selectedServiceName != value)
            {
                selectedServiceName = value;
                PickerOnPropertyChanged();
                PickerOnPropertyChanged("SelectedService");
            }
        }
    }

The binding is done from the controller when the view loads by the way....

  protected async override void OnAppearing()
    {

        base.OnAppearing();
        await viewModel.LoadPreferenceData();
        await viewModel.LoadServiceData();

        testpicker.SelectedIndex = 5;

    }

I've also updated the base class to reflect the tutorial, i've changed the names.

Can you see anything obvious why this is not working? I'm happy to supply more code if needed.

2
It looks like a type mismatch problem - SelectedService should be of same type as each item in Services list. Also, you don't need TwoWay binding mode on ItemsSourceSharada Gururaj
Thank you , it was indeed the type. I was using a custom type not a string for the binding of data.the_tr00per

2 Answers

2
votes

The error was due to binding the picker to a custom type for the source.

ItemsSource="{Binding Services}

Instead of using a string for the binding object, i changed the type from:

public String SelectedServiceName 

To this:

public Service SelectedServiceName 
0
votes

Create custom picker and implement in your code its working for me try below code :

public class CustomPicker : Picker
    {
        public CustomPicker()
        {
            SelectedIndexChanged += OnSelectedIndexChanged;
        }

        public static readonly BindableProperty SelectedItemProperty =
            BindableProperty.Create("SelectedItem", typeof(object), typeof(CustomPicker), null, BindingMode.TwoWay, null, OnSelectedItemChanged);

        public object SelectedItem
        {
            get { return GetValue(SelectedItemProperty); }
            set
            {
                SetValue(SelectedItemProperty, value);

                if (value != null &&  ItemsSource!=null && ItemsSource.Contains(value))
                    SelectedIndex = ItemsSource.IndexOf(value);
                else
                    SelectedIndex = -1;
            }
        }

        public static readonly BindableProperty ItemsSourceProperty =
            BindableProperty.Create("ItemsSource", typeof(IEnumerable), typeof(CustomPicker), null, BindingMode.TwoWay, null, OnItemsSourceChanged);

        public IList ItemsSource
        {
            get { return (IList)GetValue(ItemsSourceProperty); }
            set { SetValue(ItemsSourceProperty, value); }
        }

        public static readonly BindableProperty DisplayPropertyProperty =
            BindableProperty.Create("DisplayProperty", typeof(string), typeof(CustomPicker), null, BindingMode.TwoWay, null, OnDisplayPropertyChanged);


        public string DisplayProperty
        {
            get { return (string)GetValue(DisplayPropertyProperty); }
            set { SetValue(DisplayPropertyProperty, value); }
        }
        private static void OnSelectedItemChanged(BindableObject bindable, object oldValue, object newValue)
        {
            var picker = (CustomPicker)bindable;
            picker.SelectedItem = newValue;
            if (picker.ItemsSource != null && picker.SelectedItem != null)
            {
                var count = 0;
                foreach (var obj in picker.ItemsSource)
                {
                    if (obj == picker.SelectedItem)
                    {
                        picker.SelectedIndex = count;
                        break;
                    }
                    count++;
                }
            }
        }

        private static void OnDisplayPropertyChanged(BindableObject bindable, object oldValue, object newValue)
        {
            var picker = (CustomPicker)bindable;
            picker.DisplayProperty = (string)newValue;
            LoadItemsAndSetSelected(bindable);
        }

        private static void OnItemsSourceChanged(BindableObject bindable, object oldValue, object newValue)
        {
            var picker = (CustomPicker)bindable;
            picker.ItemsSource = (IList)newValue;

            var oc = newValue as INotifyCollectionChanged;

            if (oc != null)
            {
                oc.CollectionChanged += (a, b) =>
                {
                    LoadItemsAndSetSelected(bindable);
                };
            }

            LoadItemsAndSetSelected(bindable);
        }

        private static void LoadItemsAndSetSelected(BindableObject bindable)
        {
            var picker = (CustomPicker)bindable;

            if (picker.ItemsSource == null)
                return;

            var count = 0;

            foreach (var obj in picker.ItemsSource)
            {
                var value = string.Empty;
                if (picker.DisplayProperty != null)
                {
                    var prop = obj.GetType().GetRuntimeProperties().FirstOrDefault(p => string.Equals(p.Name, picker.DisplayProperty, StringComparison.OrdinalIgnoreCase));

                    if (prop != null)
                        value = prop.GetValue(obj).ToString();
                }
                else
                {
                    value = obj.ToString();
                }

                if (!picker.Items.Contains(value))
                {
                    picker.Items.Add(value);
                }

                if (picker.SelectedItem != null && picker.SelectedItem == obj)
                    picker.SelectedIndex = count;

                count++;
            }

            if (picker.ItemsSource.Count == picker.Items.Count - 1)
                picker.SelectedIndex++;
        }
        private void OnSelectedIndexChanged(object sender, EventArgs e)
        {
            if (SelectedIndex > -1)
            {
                SelectedItem = ItemsSource[SelectedIndex];
            }
        }
    }

Xaml Code

 <userControls:CustomPicker BackgroundColor="Transparent"  x:Name="testpicker" HorizontalOptions="FillAndExpand" ItemsSource="{Binding Services}" SelectedItem="{Binding SelectedServiceName}" DisplayProperty="{Binding ServiceDescription}" />

Don't forgot put in Xaml header

xmlns:userControls="clr-namespace:MyNameSpace"