#region[Imports] using System; using System.Collections; using System.Collections.Specialized; using System.Reflection; using Xamarin.Forms; #endregion namespace plannerTaxis { public class BindablePicker : Picker { bool _disableNestedCalls; #region[BindableProperties] 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); #endregion #region[values] 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 (SelectedItem != value) { SetValue (SelectedItemProperty, value); InternalSelectedItemChanged (); } } } public object SelectedValue { get { return GetValue (SelectedValueProperty); } set { SetValue (SelectedValueProperty, value); InternalSelectedValueChanged (); } } public string SelectedValuePath { get; set; } #endregion public BindablePicker () { SelectedIndexChanged += OnSelectedIndexChanged; } public event EventHandler ItemSelected; void InstanceOnItemsSourceChanged (object oldValue, object newValue) { _disableNestedCalls = true; Items.Clear (); INotifyCollectionChanged oldCollectionINotifyCollectionChanged = oldValue as INotifyCollectionChanged; if (oldCollectionINotifyCollectionChanged != null) { oldCollectionINotifyCollectionChanged.CollectionChanged -= ItemsSource_CollectionChanged; } INotifyCollectionChanged newCollectionINotifyCollectionChanged = newValue as INotifyCollectionChanged; if (newCollectionINotifyCollectionChanged != null) { newCollectionINotifyCollectionChanged.CollectionChanged += ItemsSource_CollectionChanged; } if (!Equals (newValue, null)) { bool hasDisplayMemberPath = !string.IsNullOrWhiteSpace (DisplayMemberPath); foreach (object item in (IEnumerable)newValue) { if (hasDisplayMemberPath) { Type type = item.GetType (); PropertyInfo prop = type.GetRuntimeProperty (DisplayMemberPath); Items.Add (prop.GetValue (item).ToString ()); } else { Items.Add (item.ToString ()); } } SelectedIndex = -1; _disableNestedCalls = false; if (SelectedItem != null) { InternalSelectedItemChanged (); } else if (hasDisplayMemberPath && SelectedValue != null) { InternalSelectedValueChanged (); } } else { _disableNestedCalls = true; SelectedIndex = -1; SelectedItem = null; SelectedValue = null; _disableNestedCalls = false; } } void InternalSelectedItemChanged () { if (_disableNestedCalls) { return; } int selectedIndex = -1; object selectedValue = null; if (ItemsSource != null) { int index = 0; bool hasSelectedValuePath = !string.IsNullOrWhiteSpace (SelectedValuePath); foreach (object item in ItemsSource) { if (item != null && item.Equals (SelectedItem)) { selectedIndex = index; if (hasSelectedValuePath) { Type type = item.GetType (); PropertyInfo prop = type.GetRuntimeProperty (SelectedValuePath); selectedValue = prop.GetValue (item); } break; } index++; } } _disableNestedCalls = true; SelectedValue = selectedValue; SelectedIndex = selectedIndex; _disableNestedCalls = false; } void InternalSelectedValueChanged () { if (_disableNestedCalls) { return; } if (string.IsNullOrWhiteSpace (SelectedValuePath)) { return; } int selectedIndex = -1; object selectedItem = null; bool hasSelectedValuePath = !string.IsNullOrWhiteSpace (SelectedValuePath); if (ItemsSource != null && hasSelectedValuePath) { int index = 0; foreach (object item in ItemsSource) { if (item != null) { Type type = item.GetType (); PropertyInfo prop = type.GetRuntimeProperty (SelectedValuePath); if (prop.GetValue (item) == SelectedValue) { selectedIndex = index; selectedItem = item; break; } } index++; } } _disableNestedCalls = true; SelectedItem = selectedItem; SelectedIndex = selectedIndex; _disableNestedCalls = false; } void ItemsSource_CollectionChanged (object sender, NotifyCollectionChangedEventArgs e) { bool hasDisplayMemberPath = !string.IsNullOrWhiteSpace (DisplayMemberPath); /*if (e.Action == NotifyCollectionChangedAction.Add) { foreach (var item in e.NewItems) { if (hasDisplayMemberPath) { var type = item.GetType (); var prop = type.GetRuntimeProperty (DisplayMemberPath); Items.Add (prop.GetValue (item).ToString ()); } else { 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 (DisplayMemberPath); Items.Remove (prop.GetValue (item).ToString ()); } else { 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 (DisplayMemberPath); Items.Remove (prop.GetValue (item).ToString ()); } else { var index = Items.IndexOf (item.ToString ()); if (index > -1) { Items[index] = item.ToString (); } } } }*/ switch (e.Action) { case NotifyCollectionChangedAction.Add: foreach (object item in e.NewItems) { if (hasDisplayMemberPath) { Type type = item.GetType (); PropertyInfo prop = type.GetRuntimeProperty (DisplayMemberPath); Items.Add (prop.GetValue (item).ToString ()); } else { Items.Add (item.ToString ()); } } break; case NotifyCollectionChangedAction.Remove: foreach (object item in e.NewItems) { if (hasDisplayMemberPath) { Type type = item.GetType (); PropertyInfo prop = type.GetRuntimeProperty (DisplayMemberPath); Items.Remove (prop.GetValue (item).ToString ()); } else { Items.Remove (item.ToString ()); } } break; case NotifyCollectionChangedAction.Replace: foreach (object item in e.NewItems) { if (hasDisplayMemberPath) { Type type = item.GetType (); PropertyInfo prop = type.GetRuntimeProperty (DisplayMemberPath); Items.Remove (prop.GetValue (item).ToString ()); } else { int index = Items.IndexOf (item.ToString ()); if (index > -1) { Items[index] = item.ToString (); } } } break; } } #region[events] static void OnItemsSourceChanged (BindableObject bindable, object oldValue, object newValue) { if (Equals (newValue, null) && Equals (oldValue, null)) { return; } BindablePicker picker = (BindablePicker)bindable; picker.InstanceOnItemsSourceChanged (oldValue, newValue); } void OnSelectedIndexChanged (object sender, EventArgs e) { if (_disableNestedCalls) { return; } if (SelectedIndex < 0 || ItemsSource == null || !ItemsSource.GetEnumerator ().MoveNext ()) { _disableNestedCalls = true; /*if (SelectedIndex != -1) { SelectedIndex = -1; }*/ SelectedIndex = -1; SelectedItem = null; SelectedValue = null; _disableNestedCalls = false; return; } _disableNestedCalls = true; int index = 0; bool hasSelectedValuePath = !string.IsNullOrWhiteSpace (SelectedValuePath); foreach (object item in ItemsSource) { if (index == SelectedIndex) { SelectedItem = item; if (hasSelectedValuePath) { Type type = item.GetType (); PropertyInfo prop = type.GetRuntimeProperty (SelectedValuePath); SelectedValue = prop.GetValue (item); } break; } index++; } _disableNestedCalls = false; } static void OnSelectedItemChanged (BindableObject bindable, object oldValue, object newValue) { BindablePicker boundPicker = (BindablePicker)bindable; boundPicker.ItemSelected?.Invoke (boundPicker, new SelectedItemChangedEventArgs (newValue)); boundPicker.InternalSelectedItemChanged (); } static void OnSelectedValueChanged (BindableObject bindable, object oldValue, object newValue) { BindablePicker boundPicker = (BindablePicker)bindable; boundPicker.InternalSelectedValueChanged (); } #endregion } }