locked
Xamarin.Forms Bindable Picker RRS feed

  • Question

  • User54786 posted

    If anyone wonders how to make a Bindable Picker, here's the code:

    `

    public class BindablePicker : Picker
    {
        #region Fields
    
        //Bindable property for the items source
        public static readonly BindableProperty ItemsSourceProperty =
            BindableProperty.Create<BindablePicker, IEnumerable>(p => p.ItemsSource, null, propertyChanged: OnItemsSourcePropertyChanged);
    
        //Bindable property for the selected item
        public static readonly BindableProperty SelectedItemProperty =
            BindableProperty.Create<BindablePicker, object>(p => p.SelectedItem, null, BindingMode.TwoWay, propertyChanged: OnSelectedItemPropertyChanged);
    
        #endregion
    
        #region Properties
    
        /// <summary>
        /// Gets or sets the items source.
        /// </summary>
        /// <value>
        /// The items source.
        /// </value>
        public IEnumerable ItemsSource
        {
            get { return (IEnumerable)GetValue(ItemsSourceProperty); }
            set { SetValue(ItemsSourceProperty, value); }
        }
    
        /// <summary>
        /// Gets or sets the selected item.
        /// </summary>
        /// <value>
        /// The selected item.
        /// </value>
        public object SelectedItem
        {
            get { return GetValue(SelectedItemProperty); }
            set { SetValue(SelectedItemProperty, value); }
        }
    
        #endregion
    
        #region Methods
    
        /// <summary>
        /// Called when [items source property changed].
        /// </summary>
        /// <param name="bindable">The bindable.</param>
        /// <param name="value">The value.</param>
        /// <param name="newValue">The new value.</param>
        private static void OnItemsSourcePropertyChanged(BindableObject bindable, IEnumerable value, IEnumerable newValue)
        {
            var picker = (BindablePicker)bindable;
            var notifyCollection = newValue as INotifyCollectionChanged;
            if (notifyCollection != null)
            {
                notifyCollection.CollectionChanged += (sender, args) =>
                {
                    if (args.NewItems != null)
                    {
                        foreach (var newItem in args.NewItems)
                        {
                            picker.Items.Add((newItem ?? "").ToString());
                        }
                    }
                    if (args.OldItems != null)
                    {
                        foreach (var oldItem in args.OldItems)
                        {
                            picker.Items.Remove((oldItem ?? "").ToString());
                        }
                    }
                };
            }
    
            if (newValue == null) 
                return;
    
            picker.Items.Clear();
    
            foreach (var item in newValue)
                picker.Items.Add((item ?? "").ToString());
        }
    
        /// <summary>
        /// Called when [selected item property changed].
        /// </summary>
        /// <param name="bindable">The bindable.</param>
        /// <param name="value">The value.</param>
        /// <param name="newValue">The new value.</param>
        private static void OnSelectedItemPropertyChanged(BindableObject bindable, object value, object newValue)
        {
            var picker = (BindablePicker)bindable;
            if (picker.ItemsSource != null)
                picker.SelectedIndex = picker.ItemsSource.IndexOf(picker.SelectedItem);
        }
    
        #endregion
    }
    

    `

    Usage is pretty straight forward..:

    <local:BindablePicker ItemsSource="{Binding Cats}" SelectedItem="{Binding SelectedCat}"/>

    It would've been nice that the Xamarin.Forms.Picker already had this functionality, maybe we'll see it in a next update...

    Any questions, comments, or contributions are welcome :)

    Tuesday, January 13, 2015 1:18 PM

All replies

  • User482 posted

    That'll be really handy , thanks for posting

    Tuesday, January 13, 2015 4:14 PM
  • User70005 posted

    Thanks so much!

    Tuesday, January 13, 2015 4:26 PM
  • User482 posted
    picker.SelectedIndex = picker.ItemsSource.IndexOf
    

    Where does IndexOf come from, I don't see it in the namespaces I have included.

    Tuesday, January 13, 2015 4:39 PM
  • User54786 posted

    @IanVink? IndexOf comes from an extension I made for IEnumerable:

    `

    public static class EnumerableExtensions
    {
        /// <summary>
        /// Returns the index of the specified object in the collection.
        /// </summary>
        /// <param name="self">The self.</param>
        /// <param name="obj">The object.</param>
        /// <returns>If found returns index otherwise -1</returns>
        public static int IndexOf(this IEnumerable self, object obj)
        {
            int index = -1;
    
            var enumerator = self.GetEnumerator();
            enumerator.Reset();
            int i = 0;
            while (enumerator.MoveNext())
            {
                if (enumerator.Current == obj)
                {
                    index = i;
                    break;
                }
    
                i++;
            }
    
            return index;
        }
    }
    

    `

    Tuesday, January 13, 2015 4:47 PM
  • User73114 posted

    Really driving me crazy that there isn't a qualitative suite of controls ("views") for Xamarin Forms.

    Great work

    Saturday, February 7, 2015 11:20 AM
  • User88565 posted

    Just tweaked the event handler so the if you clear your bound list all the items are removed.

     if (notifyCollection != null)
          {
            notifyCollection.CollectionChanged += (sender, args) =>
            {
              if (args.Action == NotifyCollectionChangedAction.Reset)
              {
                picker.Items.Clear();
    
                return;
              }
    
              if (args.NewItems != null)
              {
                foreach (var newItem in args.NewItems)
                {
                  picker.Items.Add((newItem ?? "").ToString());
                }
              }
              if (args.OldItems != null)
              {
                foreach (var oldItem in args.OldItems)
                {
                  picker.Items.Remove((oldItem ?? "").ToString());
                }
              }
            };
          }
    
    Wednesday, February 11, 2015 10:10 PM
  • User55329 posted

    Another bindable picker for XF is here: https://github.com/soltechinc/soltechxf

    Thursday, February 12, 2015 5:45 PM
  • User105734 posted

    Hi there, I´m a newbie with Xamarin. Is possible to have a picker that shows a friendly name but binded to an ID? For example in my case I have a Product model and I would like to select the category from the picker, I want to show a friendly name like the CategoryName but save the CategoryId. Is there any way to do that? Thanks in advance

    Friday, February 27, 2015 8:10 PM
  • User119 posted

    In Extension method IndexOf returned -1 for everything (did not work for strings in my case)

    if (enumerator.Current == obj)
    {
            index = i;
            break;
    }
    

    I fixed it with:

    if (enumerator.Current.Equals(obj))
    {
        index = i;
        break;
    }
    

    XAML snippet:

        <controls:BindablePicker 
            x:Name="Picker1" 
            ItemsSource="{Binding Source={ x:Static local:App.Data }}"
            SelectedItem="C"
            HeightRequest="200"
            />
    
    Tuesday, March 17, 2015 10:52 PM
  • User76916 posted

    @xamadeve003 - In your product model, override the ToString method to return the name you want displayed. Its the only way I have so far found to do it.

    Wednesday, March 18, 2015 12:06 AM
  • User6753 posted

    I did the following bindable picker, I did a DisplayMember property so that you could set the display member of the object you are using in the picker, the selected item remains as the actual object in the list. It will also handle a list of strings and simply display the string if the display member is left empty.

    Could have probably easily expanded it so the DisplayMember was a bindable property but I haven't done so yet

    public class BindablePicker : Picker
    {
        public BindablePicker()
        {
            this.SelectedIndexChanged += OnSelectedIndexChanged;
        }
    
        public static BindableProperty ItemsSourceProperty =
            BindableProperty.Create<BindablePicker, IList>(o => o.ItemsSource, default(IList), propertyChanged: OnItemsSourceChanged);
    
        public static BindableProperty SelectedItemProperty =
            BindableProperty.Create<BindablePicker, object>(o => o.SelectedItem, default(object));
    
    
        public string DisplayMember { get; set; }
    
        public IList ItemsSource
        {
            get { return (IList)GetValue(ItemsSourceProperty); }
            set { SetValue(ItemsSourceProperty, value); }
        }
    
        public object SelectedItem
        {
            get { return (object)GetValue(SelectedItemProperty); }
            set { SetValue(SelectedItemProperty, value); }
        }
    
        private static void OnItemsSourceChanged(BindableObject bindable, IList oldvalue, IList newvalue)
        {
            var picker = bindable as BindablePicker;
    
            if (picker != null)
            {
                picker.Items.Clear();
                if (newvalue == null) return;
                //now it works like "subscribe once" but you can improve
                foreach (var item in newvalue)
                {
                    if (string.IsNullOrEmpty(picker.DisplayMember))
                    {
                        picker.Items.Add(item.ToString());
                    }
                    else
                    {
                        var type = item.GetType();
    
                        var prop = type.GetProperty(picker.DisplayMember);
    
    
                        //var value = 
                        picker.Items.Add(prop.GetValue(item).ToString());
                    }
                }
            }
        }
    
        private void OnSelectedIndexChanged(object sender, EventArgs eventArgs)
        {
            if (SelectedIndex < 0 || SelectedIndex > Items.Count - 1)
            {
                SelectedItem = null;
            }
            else
            {
                SelectedItem = ItemsSource[SelectedIndex];
            }
        }
    }
    
    Wednesday, March 18, 2015 1:24 AM
  • User6753 posted

    Also, I haven't used an observablecollection so I cannot see when a member of the list changes in this particular control, so you need to update the list to a new list to get an update

    Wednesday, March 18, 2015 1:28 AM
  • User113114 posted

    ViewModel

    public string SelectedPickerItem { get { return _SelectedPickerItem; } set { if (value == _SelectedPickerItem) return; _SelectedPickerItem = value; OnPropertyChanged("SelectedPickerItem"); } } private string _SelectedPickerItem;

    XAML

    SelectedItem="{Binding SelectedPickerItem}"

    When i do a debug.writeline on SelectedPickerItemi get nothing.. What do i do wrong?

    Wednesday, April 1, 2015 1:51 PM
  • User118705 posted

    @MarkRadcliffe I'm using your implementation of the bindable picker, and everything is working great except for trying to set the SelectedItem in some cases.

    In summary, I have a BindablePicker which the user makes a selection on, goes to a new page, and then navigates back to the original page (same ViewModel backing it). As I step through the code, I can see that the picker has the SelectedItem property correctly set to what the user had picked before, but there is no visual indication. In other words, where I would expect to see the string from DisplayMember show up, it does not after returning to the page. However, the SelectedItem property is still set, and I can grab it and use it, I just can't seem to get it to show up on the UI.

    Is there a way to force the view to redraw itself or something? I am manually constructing the view from code, not XAML, and set the binding context in code as well. Any thoughts?

    Tuesday, May 26, 2015 7:15 PM
  • User6753 posted

    Hi Joshua,

    I haven't had a need for that so far, so didn't even think about that, shouldn't be too hard to add that as functionality though.

    Give this a try, keep in mind I haven't tested this, but it should be about right,

        public object SelectedItem
        {
            get { return (object)GetValue(SelectedItemProperty); }
            set { 
                SetValue(SelectedItemProperty, value);
                UpdateSelected();
            }
        }
    
        private void UpdateSelected()
        {
            if (ItemsSource != null)
            {
                if (ItemsSource.Contains(SelectedItem))
                {
                    SelectedIndex = ItemsSource.IndexOf(SelectedItem);
                }
                else
                {
                    SelectedIndex = -1;
                }
            }
        }
    

    Also add a "picker.UpdateSelected();" after the foreach loop in the OnItemsSourceChanged so that if the binding to the itemsSource fires after the binding of the selected item it should still work.

    Basically my implementation had nothing done to handle a preselection.

    Let me know how you go,

    Cheers Mark

    Tuesday, May 26, 2015 8:16 PM
  • User118705 posted

    Awesome Mark, that worked great, thank you! I ended up calling UpdateSelected() in OnItemsSourceChanged but also inside of OnBindingContextChanged instead of inside of the setter.

    Thanks again.

    Wednesday, May 27, 2015 12:14 PM
  • User6753 posted

    Glad I could help,

    Having it in the OnBindingContextChanged should work well also, if you are filtering on the name of the property. I am not using visual studio 2015 so dont have the nameof() type syntax for that type of thing but suppose prop name wouldn't need to change :).

    Cheers

    Wednesday, May 27, 2015 7:25 PM
  • User133800 posted

    I can´t make this work, I have my ViewModel: private ObservableCollection<Grupo> groupsList; public const string GroupListPropertyName = "GroupPicker"; public ObservableCollection<Grupo> GroupList { get { return groupsList = Task.Run(async () => await _usuarioServicios.Groups()).Result; } set { groupsList = value; } }

    And in myBindable Picker I have: BindablePicker grupoPicker = new BindablePicker { Title = "Franquicias", VerticalOptions = LayoutOptions.CenterAndExpand, BackgroundColor = Color.FromHex(editColor), }; grupoPicker.SetBinding(BindablePicker.ItemsSourceProperty, new Binding("GroupPicker", BindingMode.TwoWay)); however my picker is empty and I can Edit it ?? Help please, I am trying to fill the picker from a service using a ObservableCollection Thanks in advanced!

    Thursday, June 11, 2015 2:33 PM
  • User125322 posted

    I have a content page with two bindable picker inside a Table Section. I add the item source of both picker reading from database. In Android i have no problem, but in Windows Phone when my page appearing the picker looks like if it visualize all the items in one row. All the items are overlying. After i make a selection in one of the picker, both picker become ok........

    Can someone help me?

    Thanks

    Friday, July 24, 2015 3:18 PM
  • User132080 posted

    I am running into a issue where the raise propertychanged event is not firing in my viewmodel for the selecteditem in the picker.

    Aka the user is changing the selecteditem by using the picker, and for some reason there is no event being fired for that property being changed in the view model. Even though it is being changed just fine in the view model.

    Do you guys have any ideas why this would be broken?

    Friday, September 18, 2015 3:35 PM
  • User132080 posted

    So as it trued out. The property that I was bound to was not updating when the user was selecting a new object in the picker. I ended up have to subscribe to the IndexChanged event to make sure that it updates the item correctly.

    /// <summary> /// Builds a new bindable picker. /// </summary> public BindablePicker() { SelectedIndexChanged += OnSelectedIndexChanged; }

        private void OnSelectedIndexChanged(object sender, EventArgs eventArgs)
        {
            if (ItemsSource != null) {
                if (SelectedIndex != -1)
                {
                    SelectedItem = ItemsSource[SelectedIndex];
                }
            }
        }
    
    Wednesday, September 23, 2015 6:12 PM
  • User148014 posted

    I have my BindablePicker bound to a simple object that has public properties of Id and Description. The SelectedItem passes back the Description field but I need the selected Id and the only way I can think of doing it is:

    foreach (ConfigItemCategory item in PickerCategory.ItemsSource) { selection = item.Id; break; } }

    Where PickerCategory is my BindablePicker and ConfigItemCategory my simple object; I can't think of creating a generic way without the method knowing the type of object its bound to or am I missing a trick?

    Thanks

    Sunday, September 27, 2015 7:53 PM
  • User182596 posted

    @MarkRadcliffe thanks for your bindable picker code. It works great. I was attempting to modify your code so that I can specify a DisplayMember and an IDMember. And then have a property called SelectedID to get the selected object's ID.. but I am stuck. If you have any ideas on how best to make this work, I would really appreciate it. Thanks!

    Thursday, February 11, 2016 1:55 AM
  • User6753 posted

    Hi Mike,

    When you say ID member you are talking about having a member that is inside a list item? And on selection changed that bindable property would be updated to be that value?

    Cheers

    Thursday, February 11, 2016 2:31 AM
  • User80334 posted

    @MarkRadcliffe i am trying to get your version of the bindable picker working however i getting the following error

    ReflectionExtensions.GetProperty(Type, string) is inaccessible due to its protection level

    on the following line of code

    var prop = type.GetProperty(picker.DisplayMember);

    Others here seem to have this working so im wondering where i am going wrong with this.

    Any help appreciated.

    Jas

    Thursday, February 11, 2016 12:08 PM
  • User182596 posted

    @MarkRadcliffe said: Hi Mike,

    When you say ID member you are talking about having a member that is inside a list item? And on selection changed that bindable property would be updated to be that value?

    Cheers

    Basically, I would like to have it behave similar to how a ASP.NET web dropdownlist behaves... it has a DataTextField and DataValueField. So when I bind a list of customers, I can specify the customer name to be displayed (DataTextField), but then I would like to also have the ability to retrieve the selected customer ID (DataValueField).

    Thursday, February 11, 2016 2:47 PM
  • User182596 posted

    @jaspalsinghkhokhar said: @MarkRadcliffe i am trying to get your version of the bindable picker working however i getting the following error

    ReflectionExtensions.GetProperty(Type, string) is inaccessible due to its protection level

    on the following line of code

    var prop = type.GetProperty(picker.DisplayMember);

    Others here seem to have this working so im wondering where i am going wrong with this.

    Any help appreciated.

    Jas

    If you are using the code in a PCL library like me, you can use LINQ. Just add "using System.Linq;" and change this:

                        var type = item.GetType();
                        var prop = type.GetProperty(picker.DisplayMember);
                        picker.Items.Add(prop.GetValue(item).ToString());
    

    To this:

                            var prop = item.GetType().GetRuntimeProperties().FirstOrDefault(p => string.Equals(p.Name, picker.DisplayMember, StringComparison.OrdinalIgnoreCase));
                            if (prop != null)
                            {
                                picker.Items.Add(prop.GetValue(item).ToString());
                            }
    
    Thursday, February 11, 2016 2:49 PM
  • User182596 posted

    @MikePeters said:

    @MarkRadcliffe said: Hi Mike,

    When you say ID member you are talking about having a member that is inside a list item? And on selection changed that bindable property would be updated to be that value?

    Cheers

    Basically, I would like to have it behave similar to how a ASP.NET web dropdownlist behaves... it has a DataTextField and DataValueField. So when I bind a list of customers, I can specify the customer name to be displayed (DataTextField), but then I would like to also have the ability to retrieve the selected customer ID (DataValueField).

    Nevermind my question. I think I figured it out. I was retrieving the selected item incorrectly.

    I was doing this: picker.Items[picker.SelectedIndex] But picker.Items was a list of strings, not objects....

    This seems to work fine: picker.ItemsSource[picker.SelectedIndex]

    EDIT: actually I am still testing.. My itemssource is an ienumerable which I can't apply indexing to, so still trying other ways to get the selected object (instead of just the selected display member). Any ideas would be appreciated. Thanks!

    Thursday, February 11, 2016 4:07 PM
  • User6753 posted

    Hi Mike, with an IEnumerable, you could use enumerable.ToList()[index],

    I am not really sure why you aren't using the selectedItem which is already the item you are looking for.

    "public object SelectedItem" is the actual item that is selected (as in the object not the display string). you can bind a property to this selectedItem in your viewmodel which will update to the correct object when it changes. Does this provide the solution you need?

    Thursday, February 11, 2016 7:19 PM
  • User182596 posted

    @MarkRadcliffe said: Hi Mike, with an IEnumerable, you could use enumerable.ToList()[index],

    I am not really sure why you aren't using the selectedItem which is already the item you are looking for.

    "public object SelectedItem" is the actual item that is selected (as in the object not the display string). you can bind a property to this selectedItem in your viewmodel which will update to the correct object when it changes. Does this provide the solution you need?

    That's how I thought it would work, but it is not working for me that way. Maybe it is because I am using it in a PCL project? Here is my watch list in visual studio. SelectedItem is a STRING even though I am binding a Customer List.

    +       picker.SelectedItem "CUSTOMER NAME" string
    +       picker.ItemsSource  Count = 200 System.Collections.Generic.List<Customer>
    +       picker.Items    Count = 200 Xamarin.Forms.ObservableList<string>
    
    Thursday, February 11, 2016 7:50 PM
  • User6753 posted

    If you used it how it was originally made, the bound value needed to implement IList, the selected Item would be updated in the "private void OnSelectedIndexChanged" method.

    Put a breakpoint in this method and make sure it updates to be the OBJECT rather than just the string.

    Make sure you haven't changed this to be SelectedItem = picker.Items[SelectedIndex] instead as this would set it to the string. It has to be the ItemsSource.

    Cheers

    Thursday, February 11, 2016 8:03 PM
  • User182596 posted

    OK, my ItemsSource was an IEnumerable. Once I did "ToList()" then I got my object OK. Thanks so much!!!

    The code I posted above is what you will need if you are using it in a PCL library.

    Thanks!!!!

    Thursday, February 11, 2016 8:03 PM
  • User6753 posted

    All good, glad to see you got it working :smile:

    Thursday, February 11, 2016 8:28 PM
  • User182596 posted

    Sorry to be a nuisance, but has anyone gotten this control to work with XAML inside a ListView?

    I have an integer array (QuantityArray) defined in my ViewModel, and it binds correctly. However, the SelectedItem is not working. If I put a label bound to Quantity, I see the data so I know the data is binding correctly. Could it be that it is not translating the type integer correctly?

    When I use my same code outside a ListView and set the SelectedItem in code, everything works fine. :(

    <control:BindablePicker ItemsSource="{Binding Source={x:Reference page}, Path=BindingContext.QuantityArray}" SelectedItem="{Binding Quantity}" />

    Friday, February 12, 2016 3:13 AM
  • User80334 posted

    @MikePeters Thanks mike, updated my code and got the picker to display the correct items which is great.

    I do now have another problem that needs resolving so i'm hoping someone can help.

    XAML

        <controls:BindablePicker ItemsSource="{Binding SubElementList}"  SelectedItem="{Binding SubElementName}" 
                             DisplayMember="SubElementName"  />
    

    ViewModel

            private List<SurveyDesign_StockCondition> _subElementList;
            public List<SurveyDesign_StockCondition> SubElementList
            {
                get { return _subElementList; }
                set { SetProperty(ref _subElementList, value); }
            }
            private string _subElementName;
            public string SubElementName
            {
                get { return _subElementName; }
                set { SetProperty(ref _subElementName, value); }
            }
    

    Although the picker is displaying the correct items from the SubElementList the SubElementNameproperty never gets populated with the SelectedItem.

    I am using a PCL.

    As usual any help greatly appreciated.

    Thanks,

    Jas

    Friday, February 12, 2016 3:58 PM
  • User169828 posted

    This thread is really good but the code is now super fragmented. Could someone maybe put a github/gist together of a complete sample?

    Friday, February 12, 2016 4:09 PM
  • User182596 posted

    @jaspalsinghkhokhar said: @MikePeters Thanks mike, updated my code and got the picker to display the correct items which is great.

    I do now have another problem that needs resolving so i'm hoping someone can help.

    XAML

        <controls:BindablePicker ItemsSource="{Binding SubElementList}"  SelectedItem="{Binding SubElementName}" 
                           DisplayMember="SubElementName"  />
    

    ViewModel

            private List<SurveyDesign_StockCondition> _subElementList;
            public List<SurveyDesign_StockCondition> SubElementList
            {
              get { return _subElementList; }
              set { SetProperty(ref _subElementList, value); }
            }
            private string _subElementName;
            public string SubElementName
            {
                get { return _subElementName; }
                set { SetProperty(ref _subElementName, value); }
            }
    

    Although the picker is displaying the correct items from the SubElementList the SubElementNameproperty never gets populated with the SelectedItem.

    I am using a PCL.

    As usual any help greatly appreciated.

    Thanks,

    Jas

    Your problem sounds similar to mine. Binding "SelectedItem" in XAML doesn't seem to work. Setting it in code, seems to work fine. If you figure anything out, let me know. Thanks

    Friday, February 12, 2016 6:54 PM
  • User182596 posted

    OK guys, I took pieces of different pickers (Xlabs extended picker, @MarkRadcliffe, and others) and I think this is a good solution for a PCL project. It seems to work for me, even in XAML and in a ListView... Let me know if there is anything that can be improved on.

    using System;
    using System.Collections;
    using Xamarin.Forms;
    using System.Linq;
    using System.Reflection;
    
        public class ExtendedPicker : Picker
        {
            public ExtendedPicker()
            {
                base.SelectedIndexChanged += OnSelectedIndexChanged;
            }
    
            public static readonly BindableProperty SelectedItemProperty = BindableProperty.Create("SelectedItem", typeof(object), typeof(ExtendedPicker), null, BindingMode.OneWay, null, new BindableProperty.BindingPropertyChangedDelegate(ExtendedPicker.OnSelectedItemChanged), null, null, null);
            public static readonly BindableProperty ItemsSourceProperty = BindableProperty.Create("ItemsSource", typeof(IEnumerable), typeof(ExtendedPicker), null, BindingMode.OneWay, null, new BindableProperty.BindingPropertyChangedDelegate(ExtendedPicker.OnItemsSourceChanged), null, null, null);
            public static readonly BindableProperty DisplayPropertyProperty = BindableProperty.Create("DisplayProperty", typeof(string), typeof(ExtendedPicker), null, BindingMode.OneWay, null, new BindableProperty.BindingPropertyChangedDelegate(ExtendedPicker.OnDisplayPropertyChanged), null, null, null);
    
            public IList ItemsSource
            {
                get { return (IList)base.GetValue(ExtendedPicker.ItemsSourceProperty); }
                set { base.SetValue(ExtendedPicker.ItemsSourceProperty, value); }
            }
    
            public object SelectedItem
            {
                get { return base.GetValue(ExtendedPicker.SelectedItemProperty); }
                set
                {
                    base.SetValue(ExtendedPicker.SelectedItemProperty, value);
                    if (ItemsSource.Contains(SelectedItem))
                    {
                        SelectedIndex = ItemsSource.IndexOf(SelectedItem);
                    }
                    else
                    {
                        SelectedIndex = -1;
                    }
                }
            }
    
            public string DisplayProperty
            {
                get { return (string)base.GetValue(ExtendedPicker.DisplayPropertyProperty); }
                set { base.SetValue(ExtendedPicker.DisplayPropertyProperty, value); }
            }
    
            private void OnSelectedIndexChanged(object sender, EventArgs e)
            {
                this.SelectedItem = ItemsSource[SelectedIndex];
            }
    
    
            private static void OnSelectedItemChanged(BindableObject bindable, object oldValue, object newValue)
            {
                ExtendedPicker picker = (ExtendedPicker)bindable;
                picker.SelectedItem = newValue;
                if (picker.ItemsSource != null && picker.SelectedItem != null)
                {
                    int count = 0;
                    foreach (object obj in picker.ItemsSource)
                    {
                        if (obj == picker.SelectedItem)
                        {
                            picker.SelectedIndex = count;
                            break;
                        }
                        count++;
                    }
                }
            }
    
            private static void OnDisplayPropertyChanged(BindableObject bindable, object oldValue, object newValue)
            {
                ExtendedPicker picker = (ExtendedPicker)bindable;
                picker.DisplayProperty = (string)newValue;
                loadItemsAndSetSelected(bindable);
    
            }
            private static void OnItemsSourceChanged(BindableObject bindable, object oldValue, object newValue)
            {
                ExtendedPicker picker = (ExtendedPicker)bindable;
                picker.ItemsSource = (IList)newValue;
                loadItemsAndSetSelected(bindable);
            }
    
            static void loadItemsAndSetSelected(BindableObject bindable)
            {
                ExtendedPicker picker = (ExtendedPicker)bindable;
                if (picker.ItemsSource as IEnumerable != null)
                {
                    int count = 0;
                    foreach (object obj in (IEnumerable)picker.ItemsSource)
                    {
                        string 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();
                        }
                        picker.Items.Add(value);
                        if (picker.SelectedItem != null)
                        {
                            if (picker.SelectedItem == obj)
                            {
                                picker.SelectedIndex = count;
                            }
                        }
                        count++;
                    }
                }
            }
        }
    
    Friday, February 12, 2016 7:38 PM
  • User169828 posted

    @MikePeters Hey Mike, thanks a lot for the sample. That is great, I really like that it supports binding a list of objects. The one piece that it is missing that I have struggled to properly implement is changes to the itemssource after binding. So adding/removing (and perhaps rearranging) an OC will affect the items in the picker in real time.

    With the example you posted, your binding list must be populated and then bound. Any changes to the list after this don't affect the picker view.

    Here is my terrible attempt:

    Modify OnItemsSourceChanged

            private static void OnItemsSourceChanged(BindableObject bindable, object oldValue, object newValue)
            {
                var picker = (ExtendedPicker) bindable;
                picker.ItemsSource = (IList) newValue;
    
                var oc = newValue as INotifyCollectionChanged;
    
                if (oc != null && !PickerLookup.ContainsKey(oc))
                {
                    oc.CollectionChanged += Oc_CollectionChanged;
                    PickerLookup.Add(oc, new WeakReference(picker));
                }
    
                LoadItemsAndSetSelected(bindable);
            }
    

    Add:

    private static readonly Dictionary<INotifyCollectionChanged, WeakReference> PickerLookup = new Dictionary<INotifyCollectionChanged, WeakReference>();
    
            private static void Oc_CollectionChanged(object sender, NotifyCollectionChangedEventArgs e)
            {
                var oc = (INotifyCollectionChanged)sender;
    
                if (e.Action == NotifyCollectionChangedAction.Add)
                {
                    var picker = (ExtendedPicker)PickerLookup[oc].Target;
    
                    foreach (var newItem in e.NewItems)
                    {
                        var value = string.Empty;
                        if (picker.DisplayProperty != null)
                        {
                            var prop = newItem.GetType().GetRuntimeProperties().FirstOrDefault(p => string.Equals(p.Name, picker.DisplayProperty, StringComparison.OrdinalIgnoreCase));
    
                            if (prop != null)
                                value = prop.GetValue(newItem).ToString();
                        }
                        else
                        {
                            value = newItem.ToString();
                        }
    
                        picker.Items.Add(value);
                    }
                }
                else if (e.Action == NotifyCollectionChangedAction.Remove)
                {
                    var picker = (ExtendedPicker)PickerLookup[oc].Target;
    
                    foreach (var newItem in e.OldItems)
                    {
                        var value = string.Empty;
                        if (picker.DisplayProperty != null)
                        {
                            var prop = newItem.GetType().GetRuntimeProperties().FirstOrDefault(p => string.Equals(p.Name, picker.DisplayProperty, StringComparison.OrdinalIgnoreCase));
    
                            if (prop != null)
                                value = prop.GetValue(newItem).ToString();
                        }
                        else
                        {
                            value = newItem.ToString();
                        }
    
                        picker.Items.Remove(value);
                    }
                }
            }
    
    Friday, February 12, 2016 7:51 PM
  • User6753 posted

    @JulienRosen Why not just recall loadItemsAndSetSelected on collection changed or something? Your solution seems rather complicated for something quite simple.

    Sunday, February 14, 2016 12:09 AM
  • User169828 posted

    @MarkRadcliffe maybe I am missing something simple, but OnCollectionChanged doesn't give you a reference to the picker, nor would I want to completely empty and repopulate the picker with every change. I love you how call it quite simple lol I struggled with this.

    Sunday, February 14, 2016 12:54 AM
  • User6753 posted

    @JulienRosen - It is simple because the number of elements that go into a picker are not huge, so just refreshing the who items list and reselecting the originally selected item would be a valid approach. The reason you cannot get a reference to the picker is because you made it a static function rather than just a lambda method. If you made it a lambda method you'd be able to reference your picker item above.

    ie oc.CollectionChanged += Oc_CollectionChanged would be changed to something like this:

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

    This stops you from having to maintain a global static dictionary etc and just resets the pickers items. The reason I am saying that it is simple is that the cost to doing this would be tiny, so no need to overcomplicate things.

    Also keep in mind that you'd HAVE to have an observable collection in order for this to work. If you bound a list to it rather than a collection the collection changed events wouldn't occur.

    Also be careful with the OnItemsSourceChanged handler as if you were setting the item source to be the same value and raising a on prop changed event. you'd end up with a double up of oc.CollectionChanged events. To stop this you should check if the old value is not the same as the new value before adding the CollectionChanged event handler.

    Sunday, February 14, 2016 7:24 PM
  • User169828 posted

    Awesome!! Thank you.

    Sunday, February 14, 2016 7:46 PM
  • User182596 posted

    Just curious. I notice that when I tap a picker and the options are displayed, I am still able to perform actions on the content page. Is this the default behavior? Do I need to explicitly disable the page when the picker options are shown and re-enable it when the user is done picking?

    Monday, February 15, 2016 12:57 AM
  • User2148 posted

    @MikePeters it's different from android to iOS. If I remember, your situation should be for iOS. In android , if you tap outside the picker, the picker should close

    Monday, February 15, 2016 6:49 AM
  • User68968 posted

    @JulienRosen Could you post your full bindable picker class, please?

    Wednesday, February 24, 2016 8:46 PM
  • User169828 posted

    @DylanW here is the full picker class i am using. the one bug i have found is that the SelectedItem changing does not trigger INPC, while the SelectedIndex changing does.

    public class ExtendedPicker : Picker
        {
            public ExtendedPicker()
            {
                SelectedIndexChanged += OnSelectedIndexChanged;
            }
    
            public static readonly BindableProperty SelectedItemProperty = BindableProperty.Create("SelectedItem", typeof (object), typeof (ExtendedPicker), null, BindingMode.OneWay, null, OnSelectedItemChanged);
    
            public static readonly BindableProperty ItemsSourceProperty = BindableProperty.Create("ItemsSource", typeof (IEnumerable), typeof (ExtendedPicker), null, BindingMode.OneWay, null, OnItemsSourceChanged);
    
            public static readonly BindableProperty DisplayPropertyProperty = BindableProperty.Create("DisplayProperty", typeof (string), typeof (ExtendedPicker), null, BindingMode.OneWay, null, OnDisplayPropertyChanged);
    
            public IList ItemsSource
            {
                get { return (IList) GetValue(ItemsSourceProperty); }
                set { SetValue(ItemsSourceProperty, value); }
            }
    
            public object SelectedItem
            {
                get { return GetValue(SelectedItemProperty); }
                set
                {
                    SetValue(SelectedItemProperty, value);
    
                    if (ItemsSource.Contains(SelectedItem))
                        SelectedIndex = ItemsSource.IndexOf(SelectedItem);
                    else
                        SelectedIndex = -1;
                }
            }
    
            public string DisplayProperty
            {
                get { return (string) GetValue(DisplayPropertyProperty); }
                set { SetValue(DisplayPropertyProperty, value); }
            }
    
            private void OnSelectedIndexChanged(object sender, EventArgs e)
            {
                SelectedItem = ItemsSource[SelectedIndex];
            }
    
            private static void OnSelectedItemChanged(BindableObject bindable, object oldValue, object newValue)
            {
                var picker = (ExtendedPicker) 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 = (ExtendedPicker) bindable;
                picker.DisplayProperty = (string) newValue;
                LoadItemsAndSetSelected(bindable);
            }
    
            private static void OnItemsSourceChanged(BindableObject bindable, object oldValue, object newValue)
            {
                var picker = (ExtendedPicker) 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 = (ExtendedPicker) 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++;
            }
        }
    
    Wednesday, February 24, 2016 9:08 PM
  • User192776 posted

    Changing this allows for the ViewModel to handle the PropertyChanged event when the user changes the selected item.

    Slightly Changed Extended Picker: public static readonly BindableProperty SelectedItemProperty = BindableProperty.Create("SelectedItem", typeof(object), typeof(ExtendedPicker), null, BindingMode.TwoWay, null, OnSelectedItemChanged);

    Noted Change: "TwoWay"

    View: <controls:ExtendedPicker ItemsSource="{Binding MyItems}" SelectedItem="{Binding UserSelectedItem}" DisplayProperty="Name" />

    ViewModel: public MyItem UserSelectedItem { get { return currentItem; } set { SetProperty(ref currentItem, value); } } public IEnumerable<MyItem> MyItems { get { return collectionOfMyItems; } }

    Model: public class MyItem { string Name { get; set; } ... }

    Wednesday, March 23, 2016 9:03 PM
  • User203004 posted

    Hi I used a bindable datepicker like specified below

    But I am getting an error Namespace prefix "Local" is not defined.

    Tuesday, March 29, 2016 11:54 AM
  • User45069 posted

    The same picker but with

    1. ToStringFunction instead of DisplayProperty to not to use reflection and have some more flexible abilities for configuration
    2. “empty” (-) value.

    Hope it may help somebody

    using System;
    using Xamarin.Forms;
    using System.Collections;
    using System.Collections.Specialized;
    using System.Reflection;
    using System.Linq;
    
    namespace knock
    {
        public class ExtendedPicker : Picker
        {
            public ExtendedPicker()
            {
                SelectedIndexChanged += OnSelectedIndexChanged;
            }
    
            public static readonly BindableProperty SelectedItemProperty = BindableProperty.Create("SelectedItem", typeof (object), typeof (ExtendedPicker), null, BindingMode.TwoWay, null, OnSelectedItemChanged);
    
            public static readonly BindableProperty ItemsSourceProperty = BindableProperty.Create("ItemsSource", typeof (IEnumerable), typeof (ExtendedPicker), null, BindingMode.OneWay, null, OnItemsSourceChanged);
    
            public static readonly BindableProperty ToStringFunctionProperty = BindableProperty.Create(
                propertyName: nameof(ToStringFunction), 
                returnType: typeof(Func<object, string>), 
                declaringType: typeof(ExtendedPicker), 
                defaultValue: default(Func<object, string>),
                propertyChanged: OnToStringFunctionPropertyChanged
            );
    
            public Func<object, string> ToStringFunction
            {
                get { return (Func<object, string>)GetValue(ToStringFunctionProperty); }
                set { SetValue(ToStringFunctionProperty, value); }
            }
    
            public static readonly BindableProperty DisplayPropertyProperty = BindableProperty.Create("DisplayProperty", typeof (string), typeof (ExtendedPicker), null, BindingMode.OneWay, null, OnToStringFunctionPropertyChanged);
    
            public IList ItemsSource
            {
                get { return (IList) GetValue(ItemsSourceProperty); }
                set { SetValue(ItemsSourceProperty, value); }
            }
    
            public object SelectedItem
            {
                get { return GetValue(SelectedItemProperty); }
                set
                {
                    if (SelectedItem == value)
                        return;
                    SetValue(SelectedItemProperty, value);
                    if (ItemsSource.Contains(SelectedItem))
                        SelectedIndex = ItemsSource.IndexOf(SelectedItem) + 1;
                    else
                        SelectedIndex = 0;
                }
            }
    
            private void OnSelectedIndexChanged(object sender, EventArgs e)
            {
                SelectedItem = SelectedIndex == 0 
                    ? null 
                    : ItemsSource[SelectedIndex - 1];
            }
    
            private static void OnSelectedItemChanged(BindableObject bindable, object oldValue, object newValue)
            {
                var picker = (ExtendedPicker) bindable;
                picker.SelectedItem = newValue;
                if (picker.ItemsSource != null && picker.SelectedItem != null)
                {
                    var count = 1;
                    foreach (var obj in picker.ItemsSource)
                    {
                        if (obj == picker.SelectedItem)
                        {
                            picker.SelectedIndex = count;
                            break;
                        }
                        count++;
                    }
                }
            }
    
            private static void OnToStringFunctionPropertyChanged(BindableObject bindable, object oldValue, object newValue)
            {
                LoadItemsAndSetSelected(bindable);
            }
    
            private static void OnItemsSourceChanged(BindableObject bindable, object oldValue, object newValue)
            {
                var picker = (ExtendedPicker) 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 = (ExtendedPicker) bindable;
    
                if (picker.ItemsSource == null)
                    return;
    
                var count = 1;
                picker.Items.Add("-");
    
                foreach (var obj in picker.ItemsSource)
                {
                    var value = picker.ToStringFunction != null 
                        ? picker.ToStringFunction.Invoke(obj)
                        : 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++;
            }
        }
    }
    
    Wednesday, March 30, 2016 6:57 PM
  • User203004 posted

    Hi all

    I have used a bindable picker whose xaml file is shown below.

    <!--<Picker x:Name="POCodePicker"   SelectedIndex="{Binding PONumberList.PurchaseOrderId}" BackgroundColor="#ffffff" HorizontalOptions="FillAndExpand" VerticalOptions="Center" IsVisible="True" />-->
    <controls:BindablePicker ItemsSource="{Binding PONumberList}"  SelectedItem="{Binding SelectedPONumber}"
                             DisplayMember="{Binding PONumberList.PONumber}"/>
    

    and my pagemodel is like this

    using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.Threading.Tasks; using FreshMvvm; using ProcurementApp.BizServices.Receiving; using ProcurementApp.Model; using System.Collections.ObjectModel;

    namespace Procurement_App { public class DemoPickerPageModel:FreshBasePageModel { private readonly IReceivingService _receiveService;

        public PONumberList SelectedPONumber { get; set; }
        public ObservableCollection<PONumberList> PONumberList { get; set; }
        public DemoPickerPageModel(IReceivingService receiveService)
        {
            _receiveService = receiveService;
        }
    
        public override void Init(object initData)
        {
            var result = _receiveService.GetPONumberList();
            if (result != null)
            {
                PONumberList = result.PONumberList;
            }
        }
    
    
    }
    

    }

    But i couldnot populate picker in my application.Since i am new in xamarin i dont know deep about this.Whether all the terms mentioned in xaml is correct?.Could anyone please help me to sort this.

    Friday, April 1, 2016 9:47 AM
  • User201969 posted

    Hi Suja,

    XLabs provides one control Extended Picker. You can use this.

    namespace: xmlns:xLabsControl="clr-namespace:XLabs.Forms.Controls;assembly=XLabs.Forms"

     <xLabsControl:ExtendedPicker ItemsSource="{Binding PONumberList}" DisplayProperty="PONumber" SelectedItem="{Binding SelectedPONumber}"></xLabsControl:ExtendedPicker> 
    
    Friday, April 1, 2016 1:17 PM
  • User502 posted

    Like others I found that the ExtendedPicker code posted here was not handling ItemsSource changes after the fact. In fact, I saw the same thing on the 2 extended versions of the picker class in the XLabs project. So I pasted the above version into the project and debugged. I found that it was just a UI thread consistency failing silently during LoadItemsAndSetSelected while calling AddItem. So I fixed that up and am pasting it below. (Side note: I went with the reflection-based approach to getting property values as I think relative to all the reflection invocation that goes on in a binding based system already this probably is not a big factor. However I did change the loop so that it only acquired the product info once instead of during each iteration.)

            private static void LoadItemsAndSetSelected(BindableObject bindable)
            {
                var picker = (ExtendedPicker) bindable;
    
                if (picker.ItemsSource == null)
                    return;
    
                var count = 0;
                PropertyInfo prop = null;
    
                Device.BeginInvokeOnMainThread(() =>
                {
                    foreach (var obj in picker.ItemsSource)
                    {
                        var value = string.Empty;
                        if (picker.DisplayProperty != null)
                        {
                            if (prop == null)
                                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++;
                });
            }
    
    Monday, April 4, 2016 5:15 AM
  • User203004 posted

    Hi all,

    As mentioned by aneesha i have used extendedpicker control and it works well in android but it is showing error in IOS.Whether I need to include any reference?

    Wednesday, April 6, 2016 1:29 PM
  • User502 posted

    @SujaB - I see a couple things in your code snippets. First I think the DisplayMember is relative to the ItemsSource so you don't need to provide the full path for it. So instead of:

    DisplayMember="{Binding PONumberList.PONumber}"
    

    do

    DisplayMember="{Binding PONumber}
    

    The second thing is the SelectedPONumber property needs to provide PropertyChanged notification when it is set. This is done by having the model/viewmodel implement INotifyPropertyChanged in one way or the other. If you are not familiar with that then you will want to take a few minutes to research it. There are many examples around for implementing that interface in different ways and the way you implement your property is based on the approach you take, but ultimately the "set" will fire the PropertyChanged notification. Note that your code as-is related to this should not cause an error, it just wouldn't keep the binding between UI and property updated properly. a Other than that I'm not sure...I'm using it in iOS fine. :-)

    Wednesday, April 6, 2016 5:24 PM
  • User203004 posted

    @DennisWelu -I have changed my code to < xLabsControl:ExtendedPicker Grid.Row="10" Grid.Column="0" ItemsSource="{Binding LineItemDetails.UOMList}" DisplayProperty="Value" SelectedItem="{Binding LineItemDetails.SelectedPONumber}">>

    But it is still showing error in IOS and is working fine in android.I deleted the display property also but no progression.

    Thursday, April 7, 2016 5:53 AM
  • User502 posted

    @SujaB As mentioned I had troubles with the xLabs version - ended up using the code on this thread but with fixes. Perhaps seeing the error you are receiving might help us diagnose...

    Friday, April 8, 2016 1:17 AM
  • User203004 posted

    Hi Dennis, Got the solution .I just declared an object of extended picker in code behind and it i working in ios also. :)

    Friday, April 8, 2016 5:01 AM
  • User502 posted

    Suja - good to hear. That gives me a thought about why it wasn't working in Xaml for you. Perhaps double check the namespace for the xlabs controls in xaml, it should be:

    xmlns:controls="clr-namespace:XLabs.Forms.Controls;assembly=XLabs.Forms"
    

    And if you have a Shared core project (not PCL) make sure the XLabs package has been referenced from both the iOS and Android projects.

    In the case of when you were trying to use the code in this forum thread for the bindable picker I assume you were adding that as a class inside the project. There is a "gotcha" if your core project is a Shared project and not PCL. When you bring in the local namespace to the Xaml it references the assembly. But in the Xamarin Forms template for a Shared project the default assembly names for iOS and Android are different...

    I usually go into the iOS and Android project settings and set the output assembly to not include the ".iOS" and ".Android" suffix that is there by default. That way the namespace in xaml can just be your project name and it will match regardless of which platform it is built into.

    Friday, April 8, 2016 5:12 AM
  • User97779 posted

    This new nuget does exactly that: https://github.com/XAM-Consulting/FreshEssentialsSample

    <fe:BindablePicker ItemsSource="{Binding MyCars}" SelectedItem="{Binding SelectedCar}" DisplayProperty="MakeAndModel" Title="Select..." />

    Monday, April 25, 2016 2:27 AM
  • User203004 posted

    @DennisWelu Iam facing an issue in pickers. Ihave 2 pickers.On changing any value in first picker the second picker elements should change.My issue is on selectIndexchanged () of first picker iam clearing secondpicker list by picker2.items.clear().But if i select a value in second picker and then change first picker index on executing clear() it is showing exception"System.ArgumentOutOfRangeException: Index was out of range. Must be non-negative and less than the size of the collection"

    Monday, May 9, 2016 9:11 AM
  • User2148 posted

    You should post a testable project so people could help you

    Monday, May 9, 2016 9:42 AM
  • User203004 posted

    @AlessandroCaliaro Thanks :)

    page xaml //This is the first picker.On changing any value from this picker second picker list should change < xLabsControl:ExtendedPicker HorizontalOptions="FillAndExpand" x:Name="ProjectPicker" ItemsSource="{Binding LineItemDetails.ProjectList}" DisplayProperty="Value" SelectedItem="{Binding SelectedProject,Mode=TwoWay}">

    < xLabsControl:ExtendedPicker HorizontalOptions="FillAndExpand" x:Name="TaskListPicker" IsVisible="False" ItemsSource="{Binding LineItemDetails.TaskList,Mode=TwoWay}" DisplayProperty="Value" SelectedItem="{Binding LineItemDetails.SelectedTask,Mode=TwoWay}">

    Code behind xaml.cs

      private async void  SetTaskList(object sender, EventArgs e)
        {
            var s = sender as ExtendedPicker;
            var item = s.SelectedItem as NameValue ;
           if(item.Id!=0 && item.Id!= 9176)
            {
                var model = new AddLineItemPageModel();
                await model.SetTaskNumber(item.Id);
                if (Application.Current.Properties.ContainsKey("TaskList"))
                {
                    TaskListPicker.ItemsSource = null;
                   TaskListPicker.Items.Clear();       //app is crashed sowing system out of range exception.   
                   TaskListPicker.ItemsSource = Application.Current.Properties["TaskList"] as ObservableCollection<NameValue>;                
                   TaskButton.Text = "";
    
    
                }
            }
        }
    

    I set selectedindex to -1.Eventhough iam getting the same error.Can you please help to sort out this? :(

    Monday, May 9, 2016 11:27 AM
  • User2148 posted

    @SujaB please attach a project, not a piece of code. If you post a project we can help you rapidly

    Monday, May 9, 2016 12:02 PM
  • User502 posted

    Just a hunch...what happens if you don't do this line:

    TaskListPicker.ItemsSource = null;

    Monday, May 9, 2016 1:18 PM
  • User203004 posted

    @DennisWelu I have changed that also.In itemsource it is showing correct number but on displaying it is getting appended to the previous picker list.The picker list view is not getting cleared.For that i used items.clear().But if already a value is selected and i try to clear the picker it is showing out of range exception.

    Tuesday, May 10, 2016 4:26 AM
  • User203004 posted

    @AlessandroCaliaro Sorry i couldnot attach the entire project. :(.It is showing uploaded file type is not allowed

    Tuesday, May 10, 2016 4:37 AM
  • User2148 posted

    Not the entire project. A little project with a page and two pickers

    Tuesday, May 10, 2016 5:37 AM
  • User502 posted

    Feels like a bug in the xlabs:ExtendedPicker. I did not use the xlabs extended picker / used one derived from this thread. If if is a bug and you had the source code in the project it probably would be easy to locate the problem. You could try copying the xlabs:ExtendedPicker source into your project in a different namespace and debugging it.

    Tuesday, May 10, 2016 12:02 PM
  • User203004 posted

    @AlessandroCaliaro Hereby iam including the whole code for related to the picker.Please refer it

    *ADD LINE ITEM.XAML*

    < StackLayout Orientation="Vertical" Spacing="0" VerticalOptions="FillAndExpand"> < xLabsControl:ExtendedPicker HorizontalOptions="FillAndExpand" IsVisible="False" x:Name="ProjectPicker" ItemsSource="{Binding LineItemDetails.ProjectList}" DisplayProperty="Value" SelectedItem="{Binding SelectedProject,Mode=TwoWay}">< /xLabsControl:ExtendedPicker>

    < xLabsControl:ExtendedPicker HorizontalOptions="FillAndExpand" x:Name="TaskListPicker" IsVisible="False" ItemsSource="{Binding LineItemDetails.TaskList,Mode=TwoWay}" DisplayProperty="Value" SelectedItem="{Binding LineItemDetails.SelectedTask,Mode=TwoWay}">< /xLabsControl:ExtendedPicker>

    *ADD LINE ITEM .XAML.CS*

    namespace Procurement_App { public partial class AddLineItemPage : ContentPage { public AddLineItemPage() { ExtendedPicker e = new ExtendedPicker(); DatePicker s = new DatePicker(); InitializeComponent();

            ProjectPicker.SelectedIndexChanged += SetTaskList;
        }
       private async void  SetTaskList(object sender, EventArgs e)
        {
            var s = sender as ExtendedPicker;
            var item = s.SelectedItem as NameValue ;
           if(item.Id!=0 && item.Id!= 9176)
            {
                var model = new AddLineItemPageModel();
                await model.SetTaskNumber(item.Id);
                if (Application.Current.Properties.ContainsKey("TaskList"))
                {
                    TaskListPicker.ItemsSource = null;
                    TaskListPicker.ItemsSource = Application.Current.Properties["TaskList"] as ObservableCollection<NameValue>;
                    TaskButton.Text = "";
                }
            }
        }     
    }
    

    }

    Thursday, May 12, 2016 4:35 AM
  • User203004 posted

    @DennisWelu I think it is not only for extended picker.This issue is there in all pickers and list view also.My analysis is item source is getting changed but ui is not getting refreshed instead of that the new item source is getting appended to the old one.To clear the view we need to use items.clear().

    In my case issue is since i already selected a value from second picker.while changing index of first picker the second picker list should change.Since i have already selected a value in second picker the selected index value is there and when i try to clear that out of range exception occurs.

    Thursday, May 12, 2016 4:41 AM
  • User203004 posted

    @GeraldVersluis @DennisWelu @ThomasBurkhart @AlessandroCaliaro @FredyWenger Hi all

    If any one has encountered the same issue and solved it please share it with me.Our app is getting crashed because of this reason.

    Thursday, May 12, 2016 5:10 AM
  • User179286 posted

    I would recommend you try the BindablePicker from @MichaelRidland 's FreshEssentials library

    Thursday, May 12, 2016 5:59 AM
  • User203004 posted

    @ThomasBurkhart How can i access the details regarding this picker?.Is this the bindablepicker control provided by xlabs.While using that also i could experience the issue.

    Thursday, May 12, 2016 12:31 PM
  • User179286 posted

    http://lmgtfy.com/?q=FreshEssentials+Xamarin

    Thursday, May 12, 2016 1:57 PM
  • User2148 posted

    sorry @SujaB but I am not able to compile your code xlabscontrol is an undeclared prefix

    Thursday, May 12, 2016 3:05 PM
  • User203004 posted

    @AlessandroCaliaro here < ContentPage xmlns="http://xamarin.com/schemas/2014/forms" xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml" xmlns:sys="clr-namespace:System;assembly=mscorlib" xmlns:controls="clr-namespace:XLabs.Forms.Controls;assembly=XLabs.Forms" xmlns:xLabsControl="clr-namespace:XLabs.Forms.Controls;assembly=XLabs.Forms"
    x:Class="Procurement_App.AddLineItemPage" BackgroundColor="#E7E7E7">

    < ContentPage.Content> < StackLayout Orientation="Vertical" Spacing="0" VerticalOptions="FillAndExpand"> < xLabsControl:ExtendedPicker HorizontalOptions="FillAndExpand" IsVisible="False" x:Name="ProjectPicker" ItemsSource="{Binding LineItemDetails.ProjectList}" DisplayProperty="Value" SelectedItem="{Binding SelectedProject,Mode=TwoWay}">< /xLabsControl:ExtendedPicker>

    < xLabsControl:ExtendedPicker HorizontalOptions="FillAndExpand" x:Name="TaskListPicker" IsVisible="False" ItemsSource="{Binding LineItemDetails.TaskList,Mode=TwoWay}" DisplayProperty="Value" SelectedItem="{Binding LineItemDetails.SelectedTask,Mode=TwoWay}">< /xLabsControl:ExtendedPicker>

    we need to include reference for that.Please use this code :)

    Thursday, May 12, 2016 3:59 PM
  • User2148 posted

    this is the working code

    <ContentPage xmlns="http://xamarin.com/schemas/2014/forms"
    xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
    xmlns:sys="clr-namespace:System;assembly=mscorlib"
    xmlns:controls="clr-namespace:XLabs.Forms.Controls;assembly=XLabs.Forms"
    xmlns:xLabsControl="clr-namespace:XLabs.Forms.Controls;assembly=XLabs.Forms"
    x:Class="Procurement_App.AddLineItemPage" BackgroundColor="#E7E7E7">
    
    <ContentPage.Content>
    <StackLayout Orientation="Vertical" Spacing="0" VerticalOptions="FillAndExpand">
    <xLabsControl:ExtendedPicker HorizontalOptions="FillAndExpand" IsVisible="False" x:Name="ProjectPicker" 
    ItemsSource="{Binding LineItemDetails.ProjectList}" DisplayProperty="Value" SelectedItem="{Binding SelectedProject,Mode=TwoWay}"></xLabsControl:ExtendedPicker>
    
    <xLabsControl:ExtendedPicker HorizontalOptions="FillAndExpand" x:Name="TaskListPicker" IsVisible="False" 
    ItemsSource="{Binding LineItemDetails.TaskList,Mode=TwoWay}" DisplayProperty="Value" SelectedItem="{Binding LineItemDetails.SelectedTask,Mode=TwoWay}"></xLabsControl:ExtendedPicker>
    </StackLayout >
    </ContentPage.Content>
    </ContentPage>
    

    I have other errors

    https://www.dropbox.com/s/992jfh3smyagbie/Screenshot%202016-05-12%2018.09.00.png?dl=0

    Thursday, May 12, 2016 4:09 PM
  • User179286 posted

    I really would recomend using Michaels BindablePicker

    Thursday, May 12, 2016 4:11 PM
  • User225572 posted

    @ThomasBurkhart Thank you FreshEssentials is really amazing. I've having an issue though where I have to populate the ItemSource after the page is loaded (async calls to Azure) and the ItemsSource isn't getting populated.

    public class SelectProjectViewModel : INotifyPropertyChanged
    {
        public List<Project> Projects { get; set; }
    
        private Project _selectedProject;
        public Project SelectedProject
        {
            get { return _selectedProject; }
            set
            {
                _selectedProject = value;
            }
        }
    
        public SelectProjectViewModel()
        {
            Projects = new List<Project>();
        }
    
        public async Task PopulateItems()
        {
            var projects = await App.Client.GetTable<Project>().ToListAsync();
            foreach (var proj in projects)
            {
                Projects.Add(proj);
            }
    
            OnPropertyChanged(nameof(Projects));
        }
    
        public event PropertyChangedEventHandler PropertyChanged;
    
        [NotifyPropertyChangedInvocator]
        protected virtual void OnPropertyChanged([CallerMemberName] string propertyName = null)
        {
            PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
        }
    }
    

    And then in my View code behind I do:

    protected override async void OnAppearing()
        {
            base.OnAppearing();
    
            await ((SelectProjectViewModel) this.BindingContext).PopulateItems();
        }
    

    View code:

    <?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="CoverageTool.Field.Views.SelectProjectView"
             xmlns:viewModels="clr-namespace:CoverageTool.Field.ViewModels;assembly=CoverageTool.Field"
             xmlns:fe="clr-namespace:FreshEssentials;assembly=FreshEssentials">
      <ContentPage.BindingContext>
        <viewModels:SelectProjectViewModel/>
      </ContentPage.BindingContext>
      <AbsoluteLayout>
        <StackLayout Spacing="12"
                     Padding="12">
          <fe:BindablePicker ItemsSource="{Binding Projects}" DisplayProperty="Name" Title="Select a Project..." SelectedItem="{Binding SelectedProject}"/>
        </StackLayout>
        <Button AbsoluteLayout.LayoutBounds="1,1,.5,.5"
                AbsoluteLayout.LayoutFlags="PositionProportional"
                Clicked="OnContinueButtonClicked"
                Text="Start test"/>
      </AbsoluteLayout>
    </ContentPage>
    

    But when the page loads the BindablePicker has an empty list. I feel like I'm making a simple mistake but I'm just not seeing it. Thanks for the help.

    Monday, May 16, 2016 2:56 AM
  • User225572 posted

    @EbsanUddin said: @ThomasBurkhart Thank you FreshEssentials is really amazing. I've having an issue though where I have to populate the ItemSource after the page is loaded (async calls to Azure) and the ItemsSource isn't getting populated.

    public class SelectProjectViewModel : INotifyPropertyChanged
    {
    

    .....

    I figured it out I just had to set the binding mode to two-way...I knew it was something small.

    <fe:BindablePicker ItemsSource="{Binding Projects, Mode=TwoWay}" DisplayProperty="Name" Title="Select a Project..." SelectedItem="{Binding SelectedProject}"/>
    
    Monday, May 16, 2016 3:11 AM
  • User179131 posted

    Thanks for that @SalemKorayem. Just wasted a bunch of time trying to figure out why when changing the list for the XLabs ExtendedPicker it wasn't refreshing the list but just adding to it. I guess that is an issue in the XLabs version. Just tried out the Fresh Essentials picker and issue resolved.

    Tuesday, May 24, 2016 7:54 PM
  • User129345 posted

    I never got the bound picker code I was using to work properly on Android (it crashed if you held an item outside of the selection).

    https://forums.xamarin.com/discussion/52329/xamarin-forms-picker-class-causes-app-crash-on-android

    The FreshEssentials code works flawlessly.

    Saturday, June 25, 2016 2:27 PM
  • User114637 posted

    before clearing the item of picker its good to get the selected index and set it back as items.clear() sets the selected index to -1 and which in turn will set your selected item to null.

    Monday, July 4, 2016 7:42 AM
  • User76049 posted

    I used this one successfully in my app that just went live, works fine binding a list of objects, selecteditem etc in iOS and Android.

    https://forums.xamarin.com/discussion/63565/xamarin-forms-picker-data-binding-using-mvvm#latest

    Thursday, July 7, 2016 4:32 PM
  • User212451 posted

    Hi I think this is the best solution I found trying to fill the bindable picker from a azure call. I'm getting two compiler error:

    • I get the following error on the data annotation of the OnPropertyChanged method. The type or namespace name `NotifyPropertyChangedInvocator' could not be found. Are you missing an assembly reference? (CS0246)

    • I get a System.NullReferenceException: Object reference not set to an instance of an object error on the async call to get the data, when I comment out the compilation error.

    I cant figure out what I'm doing wrong. I followed step by step this guide.

    Please any help.

    Friday, August 5, 2016 11:27 PM
  • User238348 posted

    @EbsanUddin I implemented the picker the exact way you did but my list is still empty. I am also using an async call to populate my binded list. Is there anything else you did? Here is my code.

    ` /**View Model */ public class RspListDetailViewModel : ViewModelBase, INotifyPropertyChanged { IService azureService;

        public List<AC_Publication> Publications { get; set; }
        //public AC_Publication SelectedPub { get; set; }
    
        private AC_Publication _selectedPub;
        public AC_Publication SelectedPub
        {
            get { return _selectedPub; }
            set
            {
                _selectedPub = value;
            }
        }
    
        public RspListDetailViewModel(AC_RspListItem RSPItem)
        {
            azureService = ServiceLocator.Instance.Resolve<IService>();
    
            Refresh(RSPItem);
    
            Publications = new List<AC_Publication>();
            //Publications = new List<AC_Publication>
            //{
            //  new AC_Publication
            //  {
            //      PubID = 3,
            //      PubDesc = "Mustang"
            //  },
            //  new AC_Publication
            //  {
            //      PubID = 3,
            //      PubDesc = "Pulsar"
            //  }
            //};
    
        }
    
        public async Task ExecuteRefreshCommand(AC_RspListItem RSPItem)
        {
    
            if (IsBusy)
                return;
    
            IsBusy = true;
    
            try
            {
                var scsDrawVar = await azureService.GetSCSDraw(RSPItem.RspID);
                SCSDraw.Clear();
    
                //Get Distinct Publications
                var distinctPublications = scsDrawVar.Select(p => new { p.PubID, p.PubDesc }).Distinct().ToList();
    
    
    
                foreach (var item in distinctPublications)
                {
                    AC_Publication publication = new AC_Publication();
                    publication.PubID = item.PubID;
                    publication.PubDesc = item.PubDesc;
    
                    Publications.Add(publication);
                }
    
                OnPropertyChanged(nameof(Publications));
    
                foreach (var Draw in scsDrawVar)
                {
                    SCSDraw.Add(Draw);
                }
            }
            catch (Exception ex)
            {
                Acr.UserDialogs.UserDialogs.Instance.ShowError(ex.Message);
            }
            finally
            {
                IsBusy = false;
            }
        }
    
    
        void Refresh(AC_RspListItem RSPItem)
        {
            ExecuteRefreshCommand(RSPItem);
            //MessagingCenter.Subscribe<ToDoDetailViewModel>(this, "ItemsChanged", (sender) =>
            //{
            //  ExecuteRefreshCommand();
            //});
    
        }
    
        public event PropertyChangedEventHandler PropertyChanged;
    
        [NotifyPropertyChangedInvocator]
        protected virtual void OnPropertyChanged([CallerMemberName] string propertyName = null)
        {
            PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
        }
    

    }`

    ` /* View */

    <ContentPage.Content>
        <fe:BindablePicker ItemsSource="{Binding Publications, Mode=TwoWay}" SelectedItem="{Binding SelectedPub}" DisplayProperty="PubDesc" Title="Select..." />
    

    ...`

    ` /* View codebehind */ using System; using System.Collections.Generic; using Collector.Models; using Xamarin.Forms;

    namespace Collector.Pages { public partial class RSPDetail : ContentPage {

        public RSPDetail (AC_RspListItem RSPItem)
        {
            InitializeComponent ();
    
            AC_RspListItem RSP = new AC_RspListItem ();
            RSP = RSPItem;
            RSPItemParam = RSPItem;
    
            RSPName.Text = RSP.RSPName;
    
            viewModel = new RspListDetailViewModel(RSPItem);
    
    
            BindingContext = viewModel;
    
    
        }
    }
    

    }`

    Monday, August 8, 2016 1:05 PM
  • User60362 posted

    I created a nuget package which could be found here. It contains ItemsSourceProperty and handles INotifyCollectionChanged events properly as well as SelectedItem. It also contains DisplayMemberPath which could be used to display a certain property. If a more complex string representation is needed DisplayFunc (Func) exits which givs full control if you are not able to modify to objects ToString-method.

    The readme for more information!

    Feel free to report any issues or submit a pull request if there is something missing/bugs.

    Thursday, August 18, 2016 11:24 AM
  • User221709 posted

    Hi Joakim,

    thanks for the great BindablePicker. Can you please give me an example for an event watching when an item is selected? I've tried to get the SelectedItem in the OnPropertyChanged method of my VM but it never gets there.

    Thursday, August 18, 2016 12:48 PM
  • User254264 posted

    my repro is: https://github.com/kaushaldevluk/BindablePickerTestDemo in that i can not get selected value and how to implement onSelectedIndexChange Event ??

    Tuesday, September 6, 2016 6:42 AM
  • User254264 posted

    https://forums.xamarin.com/discussion/76137/bindable-picker-issue/p1?new=1

    my repro is: https://github.com/kaushaldevluk/BindablePickerTestDemo in that i can not get selected value and how to implement onSelectedIndexChange Event ??

    Tuesday, September 6, 2016 6:50 AM
  • User260067 posted

    I have added a new implementation to use a model instead of an string. You can add format to your string and name of the properties that you want to use.

    I uploaded a repository with the example in XAML and code behind:

    https://github.com/Antonio24991GM/BindablePicker

    Code:

    var picker = new UserControls.BindablePicker(); picker.SetBinding(UserControls.BindablePicker.FormatProperty, nameof(samplePickerVM.Format)); picker.SetBinding(UserControls.BindablePicker.ElementsProperty, nameof(samplePickerVM.Elements)); picker.SetBinding(UserControls.BindablePicker.ItemsSourceProperty, nameof(samplePickerVM.Users)); picker.SetBinding(UserControls.BindablePicker.SelectedItemProperty, nameof(samplePickerVM.UserSelected));

    Xaml:

    <controls:BindablePicker Format="{Binding Format}" Elements="{Binding Elements}" ItemsSource="{Binding Users}" SelectedItem="{Binding UserSelected}"/>

    Sunday, September 25, 2016 6:27 PM
  • User240190 posted

    Hi, this is my bindable picker. I would like to trigger a function in the ViewModel after click on Done, and then bind the selected value should bind in that ViewModer property.

    xml <mycontrol:BindablePicker Title="Choose the Course" DisplayMemberPath="Name" SelectedValuePath="CourseId" ItemsSource="{Binding ListOfCourses}" SelectedValue="{Binding Path=ListOfCourses}" IsVisible="{Binding ShowControl}" SelectedItem="{Binding DoneClickCommand}" />

    bindable class:

    ```CSharp namespace Sample.Utils {

    using System;
    using System.Collections;
    using System.Collections.Specialized;
    using System.Reflection;
    using Xamarin.Forms;
    
    public class BindablePicker : Picker {
    
        Boolean _disableNestedCalls;
    
        public static readonly BindableProperty ItemsSourceProperty =
            BindableProperty.Create("ItemsSource", typeof(IEnumerable), typeof(BindablePicker),
                null, propertyChanged: OnItemsSourceChanged);
    
        public static readonly BindableProperty SelectedItemProperty =
            BindableProperty.Create("SelectedItem", typeof(Object), typeof(BindablePicker),
                null, BindingMode.TwoWay, propertyChanged: OnSelectedItemChanged);
    
        public static readonly BindableProperty SelectedValueProperty =
            BindableProperty.Create("SelectedValue", typeof(Object), typeof(BindablePicker),
                null, BindingMode.TwoWay, propertyChanged: OnSelectedValueChanged);
    
        public String DisplayMemberPath { get; set; }
    
        public IEnumerable ItemsSource {
            get { return (IEnumerable)GetValue(ItemsSourceProperty); }
            set { SetValue(ItemsSourceProperty, value); }
        }
    
        public Object SelectedItem {
            get { return GetValue(SelectedItemProperty); }
            set {
                if (this.SelectedItem != value) {
                    SetValue(SelectedItemProperty, value);
                    InternalSelectedItemChanged();
                }
            }
        }
    
        public Object SelectedValue {
            get { return GetValue(SelectedValueProperty); }
            set {
                SetValue(SelectedValueProperty, value);
                InternalSelectedValueChanged();
            }
        }
    
        public String SelectedValuePath { get; set; }
    
        public BindablePicker()
        {
            this.SelectedIndexChanged += OnSelectedIndexChanged;
        }
    
        public event EventHandler<SelectedItemChangedEventArgs> ItemSelected;
    
        void InstanceOnItemsSourceChanged(Object oldValue, Object newValue)
        {
            _disableNestedCalls = true;
            this.Items.Clear();
    
            var oldCollectionINotifyCollectionChanged = oldValue as INotifyCollectionChanged;
            if (oldCollectionINotifyCollectionChanged != null) {
                oldCollectionINotifyCollectionChanged.CollectionChanged -= ItemsSource_CollectionChanged;
            }
    
            var newCollectionINotifyCollectionChanged = newValue as INotifyCollectionChanged;
            if (newCollectionINotifyCollectionChanged != null) {
                newCollectionINotifyCollectionChanged.CollectionChanged += ItemsSource_CollectionChanged;
            }
    
            if (!Equals(newValue, null)) {
                var hasDisplayMemberPath = !String.IsNullOrWhiteSpace(this.DisplayMemberPath);
    
                foreach (var item in (IEnumerable)newValue) {
                    if (hasDisplayMemberPath) {
                        var type = item.GetType();
                        var prop = type.GetRuntimeProperty(this.DisplayMemberPath);
                        this.Items.Add(prop.GetValue(item).ToString());
                    } else {
                        this.Items.Add(item.ToString());
                    }
                }
    
                this.SelectedIndex = -1;
                this._disableNestedCalls = false;
    
                if (this.SelectedItem != null) {
                    this.InternalSelectedItemChanged();
                } else if (hasDisplayMemberPath && this.SelectedValue != null) {
                    this.InternalSelectedValueChanged();
                }
            } else {
                _disableNestedCalls = true;
                this.SelectedIndex = -1;
                this.SelectedItem = null;
                this.SelectedValue = null;
                _disableNestedCalls = false;
            }
        }
    
        void InternalSelectedItemChanged()
        {
            if (_disableNestedCalls) {
                return;
            }
    
            var selectedIndex = -1;
            Object selectedValue = null;
            if (this.ItemsSource != null) {
                var index = 0;
                var hasSelectedValuePath = !String.IsNullOrWhiteSpace(this.SelectedValuePath);
                foreach (var item in this.ItemsSource) {
                    if (item != null && item.Equals(this.SelectedItem)) {
                        selectedIndex = index;
                        if (hasSelectedValuePath) {
                            var type = item.GetType();
                            var prop = type.GetRuntimeProperty(this.SelectedValuePath);
                            selectedValue = prop.GetValue(item);
                        }
                        break;
                    }
                    index++;
                }
            }
            _disableNestedCalls = true;
            this.SelectedValue = selectedValue;
            this.SelectedIndex = selectedIndex;
            _disableNestedCalls = false;
        }
    
        void InternalSelectedValueChanged()
        {
            if (_disableNestedCalls) {
                return;
            }
    
            if (String.IsNullOrWhiteSpace(this.SelectedValuePath)) {
                return;
            }
            var selectedIndex = -1;
            Object selectedItem = null;
            var hasSelectedValuePath = !String.IsNullOrWhiteSpace(this.SelectedValuePath);
            if (this.ItemsSource != null && hasSelectedValuePath) {
                var index = 0;
                foreach (var item in this.ItemsSource) {
                    if (item != null) {
                        var type = item.GetType();
                        var prop = type.GetRuntimeProperty(this.SelectedValuePath);
                        if (prop.GetValue(item) == this.SelectedValue) {
                            selectedIndex = index;
                            selectedItem = item;
                            break;
                        }
                    }
    
                    index++;
                }
            }
            _disableNestedCalls = true;
            this.SelectedItem = selectedItem;
            this.SelectedIndex = selectedIndex;
            _disableNestedCalls = false;
        }
    
        void ItemsSource_CollectionChanged(Object sender, NotifyCollectionChangedEventArgs e)
        {
            var hasDisplayMemberPath = !String.IsNullOrWhiteSpace(this.DisplayMemberPath);
            if (e.Action == NotifyCollectionChangedAction.Add) {
                foreach (var item in e.NewItems) {
                    if (hasDisplayMemberPath) {
                        var type = item.GetType();
                        var prop = type.GetRuntimeProperty(this.DisplayMemberPath);
                        this.Items.Add(prop.GetValue(item).ToString());
                    } else {
                        this.Items.Add(item.ToString());
                    }
                }
            } else if (e.Action == NotifyCollectionChangedAction.Remove) {
                foreach (var item in e.NewItems) {
                    if (hasDisplayMemberPath) {
                        var type = item.GetType();
                        var prop = type.GetRuntimeProperty(this.DisplayMemberPath);
                        this.Items.Remove(prop.GetValue(item).ToString());
                    } else {
                        this.Items.Remove(item.ToString());
                    }
                }
            } else if (e.Action == NotifyCollectionChangedAction.Replace) {
                foreach (var item in e.NewItems) {
                    if (hasDisplayMemberPath) {
                        var type = item.GetType();
                        var prop = type.GetRuntimeProperty(this.DisplayMemberPath);
                        this.Items.Remove(prop.GetValue(item).ToString());
                    } else {
                        var index = this.Items.IndexOf(item.ToString());
                        if (index > -1) {
                            this.Items[index] = item.ToString();
                        }
                    }
                }
            }
        }
    
        static void OnItemsSourceChanged(BindableObject bindable, Object oldValue, Object newValue)
        {
            if (Equals(newValue, null) && Equals(oldValue, null)) {
                return;
            }
    
            var picker = (BindablePicker)bindable;
            picker.InstanceOnItemsSourceChanged(oldValue, newValue);
        }
    
        void OnSelectedIndexChanged(Object sender, EventArgs e)
        {
            if (_disableNestedCalls) {
                return;
            }
    
            if (this.SelectedIndex < 0 || this.ItemsSource == null || !this.ItemsSource.GetEnumerator().MoveNext()) {
                _disableNestedCalls = true;
                if (this.SelectedIndex != -1) {
                    this.SelectedIndex = -1;
                }
                this.SelectedItem = null;
                this.SelectedValue = null;
                _disableNestedCalls = false;
                return;
            }
    
            _disableNestedCalls = true;
    
            var index = 0;
            var hasSelectedValuePath = !String.IsNullOrWhiteSpace(this.SelectedValuePath);
            foreach (var item in this.ItemsSource) {
                if (index == this.SelectedIndex) {
                    this.SelectedItem = item;
                    if (hasSelectedValuePath) {
                        var type = item.GetType();
                        var prop = type.GetRuntimeProperty(this.SelectedValuePath);
                        this.SelectedValue = prop.GetValue(item);
                    }
    
                    break;
                }
                index++;
            }
    
            _disableNestedCalls = false;
        }
    
        static void OnSelectedItemChanged(BindableObject bindable, Object oldValue, Object newValue)
        {
            var boundPicker = (BindablePicker)bindable;
            boundPicker.ItemSelected?.Invoke(boundPicker, new SelectedItemChangedEventArgs(newValue));
            boundPicker.InternalSelectedItemChanged();
        }
    
        static void OnSelectedValueChanged(BindableObject bindable, Object oldValue, Object newValue)
        {
            var boundPicker = (BindablePicker)bindable;
            boundPicker.InternalSelectedValueChanged();
        }
    
    }
    

    }

    ```

    Can you please help me with this.

    Friday, December 23, 2016 9:48 PM
  • User2148 posted

    Xamarin has its own Bindable picker. Why should you create another one?

    Friday, December 23, 2016 10:58 PM
  • User265422 posted

    @AlessandroCaliaro said: Xamarin has its own Bindable picker. Why should you create another one?

    Hum? Where?

    Wednesday, December 28, 2016 3:49 PM
  • User2148 posted

    should be in last release or in last "pre"...

    Wednesday, December 28, 2016 3:52 PM
  • User3516 posted

    It is available in 2.3.4-pre1, but that version is not released yet (it was shortly available as a nuget package, but got pulled back from nuget)

    Wednesday, December 28, 2016 3:59 PM
  • User200104 posted

    @AlessandroCaliaro said: should be in last release or in last "pre"...

    It is in de source if you build on it directly, which most people don't do :) It sucks that they just not release these worthy components

    Tuesday, January 3, 2017 2:00 PM
  • User236402 posted

    @SimonVilliard
    Can you please help me 'how can we handle the done button click event for a Bindable picker for iOS in Xamarin Forms ?

    Wednesday, January 4, 2017 8:09 PM
  • User43 posted

    @LuukJongebloet said:

    @AlessandroCaliaro said: should be in last release or in last "pre"...

    It is in de source if you build on it directly, which most people don't do :) It sucks that they just not release these worthy components

    You can download the NuGet from GitHub without building from source.

    Wednesday, January 4, 2017 10:26 PM
  • User231636 posted

    Has anyone experienced the SelectedItem not being set @SimonVilliard ? I'm finding that the SelectedItem property and OnSelectedItemPropertyChanged is not even being hit in BindablePicker.cs, therefore my property in the view model isn't being set.

    Any help would be appreciated.

    Thursday, January 5, 2017 12:01 PM
  • User236402 posted

    I have binding bindable picker with a viewmodel. Here I'm unable to do the alphabetical order for the items in picker and reset the bindable picker after choosing the selectedvalue item from that picker. In xamarin forms iOS, when I choose an item from picker it will bind to the Entry control field without clicking the Done button. and if I click on that picker again it is displaying the last selected value in it. Could you please help me how to handle the Done click, how to reset the picker value(like just to show select one) and sorting (like alphabetical order).

    Thanks in advance.

    Friday, January 6, 2017 6:15 PM
  • User253102 posted

    works perfectly! But missing one last thing. Does anybody implemented ItemTemplate on BindablePicker control?

    Thursday, January 12, 2017 6:18 AM
  • User2148 posted

    ItemTemplate? It's a picker, you can't select its template...

    Thursday, January 12, 2017 8:04 AM
  • User253102 posted

    @AlessandroCaliaro said: ItemTemplate? It's a picker, you can't select its template...

    Well, I'm actually learning things in Xamarin.Forms. I came from Windows Phone, Windows Apps, WPF, we have DropBox which you can restyle its appearance, you can even restyle its items using ItemTemplate.

    Anyway, I figured it wont be possible. I'll just throw another question instead.

    I used this BindablePicker from https://github.com/XAM-Consulting/FreshEssentials/blob/master/src/FreshEssentials/Controls/BindablePicker.cs

    In my sqlite database, I have these items (shown in image 1 I attached, listed using ListView). Now I want to show these items using Picker control.

    This is the actual implementation of the BindablePicker in XAML <ctl:BindablePicker Title="select purchased item" DisplayProperty="item_name" ItemsSource="{Binding ItemsListView.ItemsCollection}">

    this is the VM code `private ObservableCollection ItemsCollection = new ObservableCollection(); public ObservableCollection ItemsCollection { get { return _ItemsCollection; } set { if (ItemsCollection != value) { _ItemsCollection = value; base.RaisePropertyChanged("ItemsCollection"); } } } .... public async Task PopulateItemsLists() { List items = await App.Database.tblItemDetails.GetItemsAsync();

    ItemsCollection.Clear();
    foreach (tblItemDetails item in items)
    {
        ItemsCollection.Add(item);
    }
    

    }` PopulateItemsLists() method is called inside the ctor

    but once I ran the code, it looked like this (2nd screenshot)

    sorry, I cannot format the code

    Thursday, January 12, 2017 11:32 AM
  • User253102 posted

    I used the code from the OP and it looked like this

    Thursday, January 12, 2017 11:55 AM
  • User2148 posted

    Last XF version has its own

    Sunday, January 15, 2017 10:45 AM
  • User58169 posted

    Here is the compete solution...

    https://hiranpeiris.com/2017/02/24/how-to-add-a-custom-bindable-property-to-xamarin-forms-control/

    Friday, February 24, 2017 3:51 PM
  • User298288 posted

    Hello! I use this ExtendPicker, it is almost the same as the one published by Mike Peters in this topic. In VisualModel as a list I use the ObservableRangeCollection, which is the successor to the ObservableCollection and has additional methods for working with a range of values. I will not bring all its code of ObservableRangeCollection here, everyone can see it if it creates a solution from the template for cross-platform development Cross Platform App (Xamarin.Form or Native), as in the screenshot:

    In OptionsPage.xaml I binding the property:

                <controls:ExtendedPicker 
                                         ItemsSource="{Binding NativeLanguagesList}" 
                                         DisplayMember="LocName" 
                                         SelectedItem="{Binding FirstLanguageSelected, Mode=TwoWay}" 
                                         SelectedIndexChanged="OnFirstLanguageSelected"
                                         />
    

    It is declaration in ViewModel:

    MyViewModel.cs

        public class MyViewModel: ObservableObject
    ...
            public ObservableRangeCollection<Language> NativeLanguagesList { get; set; }
    ...
    

    ObservableObject.cs

    public class ObservableObject : INotifyPropertyChanged

    After launching the application, Picker displays the collection perfectly. But if I change its contents, it does not change, but continues to display the old list.

    I change the contents like this:

    NativeLanguagesList.ReplaceRange(gc.Dictonary.Languages.Where(x => x.ID != SecondLanguageSelected.ID).ToList());

    ObservableRangeCollection.cs

                    public void ReplaceRange(IEnumerable<T> collection)
                    {
                        if (collection == null)
                            throw new ArgumentNullException("collection");
    
                        Items.Clear();
                        AddRange(collection, NotifyCollectionChangedAction.Reset);
                    }
            ...
                    public void AddRange(IEnumerable<T> collection, NotifyCollectionChangedAction notificationMode = NotifyCollectionChangedAction.Add)
                    {
                        if (collection == null)
                            throw new ArgumentNullException("collection");
    
                        CheckReentrancy();
    
                        if (notificationMode == NotifyCollectionChangedAction.Reset)
                        {
                            foreach (var i in collection)
                            {
                                Items.Add(i);
                            }
    
                            OnPropertyChanged(new PropertyChangedEventArgs("Count"));
                            OnPropertyChanged(new PropertyChangedEventArgs("Item[]"));
                            OnCollectionChanged(new NotifyCollectionChangedEventArgs(NotifyCollectionChangedAction.Reset));
    
                            return;
                        }
    
                        int startIndex = Count;
                        var changedItems = collection is List<T> ? (List<T>)collection : new List<T>(collection);
                        foreach (var i in changedItems)
                        {
                            Items.Add(i);
                        }
    
                        OnPropertyChanged(new PropertyChangedEventArgs("Count"));
                        OnPropertyChanged(new PropertyChangedEventArgs("Item[]"));
                        OnCollectionChanged(new NotifyCollectionChangedEventArgs(NotifyCollectionChangedAction.Add, changedItems, startIndex));
                    }
    

    I watched in debugging which events work. The method ExtendedPicker.OnItemsSourceChanged fired once, when the application starts. After this, he does not react to any changes to NativeLanguagesList.

    What could be the reason? I read here that there are such problems with ListView. To be honest, I'm already a bit tired of "fighting" with this MVVM and XAML, there are already thoughts of getting rid of it and doing everything in code through usual bindings.

    Monday, March 13, 2017 9:22 PM
  • User282558 posted

    Hello can you help in How to bind data to Picker inside ListView in Xamarin Forms

    sample code would be helpful

    Friday, March 17, 2017 12:12 AM
  • User2148 posted

    USE a bindable picker. Xamarin forms should have one now. Otherwise there is something on github like https://github.com/XAM-Consulting/FreshEssentials/blob/master/README.md

    Friday, March 17, 2017 6:04 AM
  • User176749 posted

    @JulienRosen Have you tested your code on any of Andorid 4 versions? I have similar code and it works fine on 5,6,7 but it crashes on 4 version on the line

    base.SetValue(ExtendedPicker.SelectedItemProperty, value);

    When i check output window, I only see the error message as below. I dont understand what it means. Do you guys have any idea?

    03-21 23:42:25.660 F//system/bin/app_process( 3561): stack corruption detected: aborted

    EDIT: I tested with FreshEssentials and it is same behaviour on android 4 versions

    Tuesday, March 21, 2017 10:46 PM
  • User76049 posted

    This picker has proved pretty robust, certainly runs fine on Android 4.3.

    https://github.com/Oceanware/XamarinFormsBindablePicker

    Tuesday, March 21, 2017 11:29 PM
  • User176749 posted

    @NMackay said: This picker has proved pretty robust, certainly runs fine on Android 4.3.

    https://github.com/Oceanware/XamarinFormsBindablePicker

    Is it not nuget? I couldnt find any package. Btw, how does the latest XF beta look like which has bindable picker? anybody could tested it?

    Wednesday, March 22, 2017 12:30 AM
  • User76049 posted

    @batmaci

    Just add the class to your PCL and change the namespace if need be, test and see how it works.

    I haven't tested the new vanilla picker yet, as this picker is used in production code I've just stuck with it.

    Wednesday, March 22, 2017 12:35 AM
  • User176749 posted

    @NMackay

    I dont get it, it is also crashing on Android 4.2 for me on the line SetValue(SelectedItemProperty, value);

    All I see in the output is

    03-22 01:59:07.584 W/EGLemulation( 5606): eglSurfaceAttrib not implemented 03-22 01:59:10.852 D/dalvikvm( 5606): GCCONCURRENT freed 708K, 19% free 5696K/7028K, paused 2ms+1ms, total 7ms Thread finished: #4 03-22 01:59:22.236 D/Mono ( 5606): [0xb98ce410] worker finishing 03-22 01:59:24.296 F//system/bin/app_process( 5606): stack corruption detected: aborted

    Wednesday, March 22, 2017 1:01 AM
  • User176749 posted

    @NMackay

    thanks for your suggestion. this picker works best so far. my problem with Android 4 was caused by something else. i finally could figured that out

    Wednesday, March 22, 2017 5:09 PM
  • User314189 posted

    Hello. I've been using FreshEssentials BindablePicker so far, and as a primarly uwp developer woked great. When tested on Android it made my app crash, though.

    So I switched to Mike Peter's Bindable picker with Jason Hunts suggestion to use TwoWay binding on ItemsSource to allow the ViewModel to handle the PropertyChanged when changing a selected item.

    An interesting "bug" ensues, or maybe it's just me not fully understanding how the binding works.

    What I had already working in FreshEssentials' BindablePicker was that upon selecting an item in the first picker a second one gets populated accordingly.

    That was as simple as adding a method to change the list that populates the second picker in a method fired when the first picker selected item changes. When doing so with the ExtendedPicker what happens is that the items get added to the previous ones instead of substituting them.

    With OneWay binding nothing happens. Using the Clear() method on the binded list had no result.

    I don't really know what else to try, and I'd like to see what I'm doing wrong, not just jump from picker to picker until one works.

    Here's the relevant part of the code. XAML:

      <Label Text="Cliente"/>
             <local:ExtendedPicker ItemsSource="{Binding Customers}"
                                  DisplayProperty="Name"
                                  SelectedItem="{Binding Customer}"/>
             <Label Text="Commessa"/>
             <local:ExtendedPicker ItemsSource="{Binding Orders}"
                                  DisplayProperty="Name"
                                  SelectedItem="{Binding Order}"
                                  IsEnabled="{Binding IsCustomerSelected}"/>
    

    ViewModel:

        List<Customer> customers;
            Customer customer;
    
        List<Order> orders;
        Order order;
    
            public Customer Customer
            {
                set
                {
                    if (SetProperty(ref customer, value))
                    {
                        if (customer.ID == -1)
                        {
                            IsCustomerSelected = false;
                        }
                        else
                        {
                            updateOrdersList();
                            IsCustomerSelected = true;
                        }
                        IsOrderSelected = false;
                    }
                }
                get { return customer; }
            }
    
            public Order Order
            {
                set
                {
                    if (SetProperty(ref order, value))
                    {
                        if (order.ID == -1)
                            IsOrderSelected = false;
                        else
                        {
                            updateTasksList();
                            IsOrderSelected = true;
                        }
                    }
                }
                get { return order; }
            }
    
            public List<Customer> Customers
            {
                private set { SetProperty(ref customers, value); }
                get { return customers; }
            }
    
            public List<Order> Orders
            {
                private set { SetProperty(ref orders, value); }
                get { return orders; }
            }
    
            async void getCustomers()
            {
                Customers = await ws.getCustomers(resource.ID);
                Customers.Add(cNull);
            }
    
            async private void updateOrdersList()
            {
                Orders = await ws.getOrders(customer.ID, resource.ID);
            }
    

    ExtendedPicker:

        namespace listviewsExperiments
        {
            public class ExtendedPicker : Picker
            {
                public ExtendedPicker()
                {
                    base.SelectedIndexChanged += OnSelectedIndexChanged;
                }
    
                public static readonly BindableProperty SelectedItemProperty = BindableProperty.Create("SelectedItem", typeof(object), typeof(ExtendedPicker), null, BindingMode.TwoWay, null, new BindableProperty.BindingPropertyChangedDelegate(ExtendedPicker.OnSelectedItemChanged), null, null, null);
                public static readonly BindableProperty ItemsSourceProperty = BindableProperty.Create("ItemsSource", typeof(IEnumerable), typeof(ExtendedPicker), null, BindingMode.OneWay, null, new BindableProperty.BindingPropertyChangedDelegate(ExtendedPicker.OnItemsSourceChanged), null, null, null);
                public static readonly BindableProperty DisplayPropertyProperty = BindableProperty.Create("DisplayProperty", typeof(string), typeof(ExtendedPicker), null, BindingMode.OneWay, null, new BindableProperty.BindingPropertyChangedDelegate(ExtendedPicker.OnDisplayPropertyChanged), null, null, null);
    
                public IList ItemsSource
                {
                    get { return (IList)base.GetValue(ExtendedPicker.ItemsSourceProperty); }
                    set { base.SetValue(ExtendedPicker.ItemsSourceProperty, value); }
                }
    
                public object SelectedItem
                {
                    get { return base.GetValue(ExtendedPicker.SelectedItemProperty); }
                    set
                    {
                        base.SetValue(ExtendedPicker.SelectedItemProperty, value);
                        if (ItemsSource != null)
                        {
                            if (ItemsSource.Contains(SelectedItem))
                            {
                                SelectedIndex = ItemsSource.IndexOf(SelectedItem);
                            }
                        }
                        else
                        {
                            SelectedIndex = -1;
                        }
                    }
                }
    
                public string DisplayProperty
                {
                    get { return (string)base.GetValue(ExtendedPicker.DisplayPropertyProperty); }
                    set { base.SetValue(ExtendedPicker.DisplayPropertyProperty, value); }
                }
    
                private void OnSelectedIndexChanged(object sender, EventArgs e)
                {
                    this.SelectedItem = ItemsSource[SelectedIndex];
                }
    
    
                private static void OnSelectedItemChanged(BindableObject bindable, object oldValue, object newValue)
                {
                    ExtendedPicker picker = (ExtendedPicker)bindable;
                    picker.SelectedItem = newValue;
                    if (picker.ItemsSource != null && picker.SelectedItem != null)
                    {
                        int count = 0;
                        foreach (object obj in picker.ItemsSource)
                        {
                            if (obj == picker.SelectedItem)
                            {
                                picker.SelectedIndex = count;
                                break;
                            }
                            count++;
                        }
                    }
                }
    
                private static void OnDisplayPropertyChanged(BindableObject bindable, object oldValue, object newValue)
                {
                    ExtendedPicker picker = (ExtendedPicker)bindable;
                    picker.DisplayProperty = (string)newValue;
                    loadItemsAndSetSelected(bindable);
    
                }
                private static void OnItemsSourceChanged(BindableObject bindable, object oldValue, object newValue)
                {
                    ExtendedPicker picker = (ExtendedPicker)bindable;
                    picker.ItemsSource = (IList)newValue;
                    loadItemsAndSetSelected(bindable);
                }
    
                static void loadItemsAndSetSelected(BindableObject bindable)
                {
                    ExtendedPicker picker = (ExtendedPicker)bindable;
                    if (picker.ItemsSource as IEnumerable != null)
                    {
                        int count = 0;
                        foreach (object obj in (IEnumerable)picker.ItemsSource)
                        {
                            string 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();
                            }
                            picker.Items.Add(value);
                            if (picker.SelectedItem != null)
                            {
                                if (picker.SelectedItem == obj)
                                {
                                    picker.SelectedIndex = count;
                                }
                            }
                            count++;
                        }
                    }
                }
            }
        }
    

    Any suggestion would be greatly appreciated, thanks.

    Monday, April 3, 2017 1:39 PM
  • User248484 posted

    POST DELETED

    Thursday, April 20, 2017 5:56 AM
  • User248484 posted

    I just updated Xamarin.Forms to latest Nuget and BindablePicker is out!

    Thursday, April 20, 2017 10:19 PM
  • User37696 posted

    Xamarin.Forms now includes updates to the Picker control to allow binding. Update to version 2.3.4 to use it.

    Monday, April 24, 2017 11:48 AM