the_tr00per
the_tr00per

Reputation: 439

Xamarin Forms - picker selectedItem not firing

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.

Upvotes: 1

Views: 4705

Answers (2)

the_tr00per
the_tr00per

Reputation: 439

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 

Upvotes: 2

Ziyad Godil
Ziyad Godil

Reputation: 2680

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"

Upvotes: 0

Related Questions