Xamarin.Forms Bindable Picker

SimonVilliardSimonVilliard Simon VilliardUSMember
edited January 2015 in Xamarin.Forms

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 :)

«134

Posts

  • IanVinkIanVink Ian Vink CAInsider, University ✭✭✭

    That'll be really handy , thanks for posting

  • ApurvaGoyalApurvaGoyal Apurva Goyal USMember ✭✭

    Thanks so much!

  • IanVinkIanVink Ian Vink CAInsider, University ✭✭✭
    picker.SelectedIndex = picker.ItemsSource.IndexOf
    

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

  • SimonVilliardSimonVilliard Simon Villiard USMember

    @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;
        }
    }
    

    `

  • ThomasHagstrmThomasHagstrm Thomas Hagström SEMember ✭✭

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

    Great work

  • JohnBugoJohnBugo John Bugo CAMember

    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());
                }
              }
            };
          }
    
  • GeoffArmstrongGeoffArmstrong Geoff Armstrong CAMember ✭✭

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

  • xamadeve003xamadeve003 xamadeve003 USMember

    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

  • moljacmoljac Miljenko Cvjetko HRBeta ✭✭✭

    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"
            />
    
  • AdamPAdamP Adam Pedley AUUniversity ✭✭✭✭✭

    @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.

  • MarkRadcliffeMarkRadcliffe Mark Radcliffe NZMember ✭✭

    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];
            }
        }
    }
    
  • MarkRadcliffeMarkRadcliffe Mark Radcliffe NZMember ✭✭

    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

  • BjornBBjornB Björn Bentmar USMember ✭✭✭

    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?

  • JoshuaLymanJoshuaLyman Joshua Lyman USMember

    @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?

  • MarkRadcliffeMarkRadcliffe Mark Radcliffe NZMember ✭✭

    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

  • JoshuaLymanJoshuaLyman Joshua Lyman USMember

    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.

  • MarkRadcliffeMarkRadcliffe Mark Radcliffe NZMember ✭✭

    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

  • QAcGQAcG Jorge Gutierrez MXMember

    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!
  • MauroGaggero.1198MauroGaggero.1198 Mauro Gaggero USMember

    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

  • BobisbackBobisback Sean Hoffman USUniversity
    edited September 2015

    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?

  • BobisbackBobisback Sean Hoffman USUniversity

    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];
                }
            }
        }
    
  • Richard.LeonardRichard.Leonard Richard Leonard GBMember

    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

  • MikePetersMikePeters Mike Peters USMember

    @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!

  • MarkRadcliffeMarkRadcliffe Mark Radcliffe NZMember ✭✭

    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

  • jaspalsinghkhokharjaspalsinghkhokhar jaspal singh khokhar GBMember

    @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

  • MikePetersMikePeters Mike Peters USMember
    edited February 2016

    @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).

  • MikePetersMikePeters Mike Peters USMember
    edited February 2016

    @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());
                            }
    
  • MikePetersMikePeters Mike Peters USMember
    edited February 2016

    @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!

  • MarkRadcliffeMarkRadcliffe Mark Radcliffe NZMember ✭✭

    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?

  • MikePetersMikePeters Mike Peters USMember
    edited February 2016

    @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>
    
  • MarkRadcliffeMarkRadcliffe Mark Radcliffe NZMember ✭✭

    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

  • MikePetersMikePeters Mike Peters USMember

    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!!!!

  • MarkRadcliffeMarkRadcliffe Mark Radcliffe NZMember ✭✭

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

  • MikePetersMikePeters Mike Peters USMember
    edited February 2016

    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}" />

  • jaspalsinghkhokharjaspalsinghkhokhar jaspal singh khokhar GBMember

    @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

  • JulienRosenJulienRosen Julien Rosen CAMember ✭✭✭

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

  • MikePetersMikePeters Mike Peters USMember

    @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

  • JulienRosenJulienRosen Julien Rosen CAMember ✭✭✭
    edited February 2016

    @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);
                    }
                }
            }
    
  • MarkRadcliffeMarkRadcliffe Mark Radcliffe NZMember ✭✭

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

«134
This discussion has been closed.