BindableProperty not updated on ViewModel

In Xamarin.Forms, I have implemented a custom Picker

. ItemsSource

installed correctly. However, when I change the selected item, it does not update the property in my ViewModel.

BindablePicker

:

public class BindablePicker : Picker
{
    public BindablePicker()
    {
        this.SelectedIndexChanged += OnSelectedIndexChanged;
    }

    public static BindableProperty ItemsSourceProperty =
        BindableProperty.Create<BindablePicker, IEnumerable>(o => o.ItemsSource, default(IEnumerable), propertyChanged: OnItemsSourceChanged);

    public static BindableProperty SelectedItemProperty =
        BindableProperty.Create<BindablePicker, object>(o => o.SelectedItem, default(object), propertyChanged: OnSelectedItemChanged);


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

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

    private static void OnItemsSourceChanged(BindableObject bindable, IEnumerable oldvalue, IEnumerable newvalue)
    {
        var picker = bindable as BindablePicker;
        picker.Items.Clear();
        if (newvalue != null)
        {
            //now it works like "subscribe once" but you can improve
            foreach (var item in newvalue)
            {
                picker.Items.Add(item.ToString());
            }
        }
    }

    private void OnSelectedIndexChanged(object sender, EventArgs eventArgs)
    {
        if (SelectedIndex < 0 || SelectedIndex > Items.Count - 1)
        {
            SelectedItem = null;
        }
        else
        {
            SelectedItem = Items[SelectedIndex];
        }
    }
    private static void OnSelectedItemChanged(BindableObject bindable, object oldvalue, object newvalue)
    {
        var picker = bindable as BindablePicker;
        if (newvalue != null)
        {
            picker.SelectedIndex = picker.Items.IndexOf(newvalue.ToString());
        }
    }
}

      

Page Xaml

:

<controls:BindablePicker Title="Category"
        ItemsSource="{Binding Categories}"
        SelectedItem="{Binding SelectedCategory}"
        Grid.Row="2"/>

      

Properties were ViewModel

not implemented NotifyPropertyChanged

in properties, since they only need to be updated from the 'View to the

ViewModel`:

public Category SelectedCategory { get; set; }
public ObservableCollection<Category> Categories { get; set; }

      

+3


source to share


2 answers


Apart from adding Mode=TwoWay

to my binding, I had to change a few things in my collector so that it can work with the actual objects I was binding to.

The property Items

Xamarin Picker

is IList<string>

since all my elements are appended to it as a string, it keeps the same indexed value.

For this, the ItemsSource parameter is changed to IList

:

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

      



I also modified the method SelectedIndexChanged

so that it does not retrieve the item from Items

, but from ItemsSource

, which in my case is IList<Category>

:

private void OnSelectedIndexChanged(object sender, EventArgs eventArgs)
    {
        if (SelectedIndex < 0 || SelectedIndex > Items.Count - 1)
        {
            SelectedItem = null;
        }
        else
        {
            SelectedItem = ItemsSource[SelectedIndex];
        }
    }

      

In mine, ViewModel

I no longer use ObservableCollection

for mine Categories

, but add these elements to IList<Category>

.

ObservableCollection

doesn't make sense since mine is BindablePicker

linked to ItemsSource

, items are added to the inner IList<string>

. when you add an item to the collection, it will not update. Now I am updating the whole ItemSource

if the item is changed.

0


source


When creating your BindableProperty:

public static BindableProperty SelectedItemProperty =
    BindableProperty.Create<BindablePicker, object>(o => o.SelectedItem, default(object), propertyChanged: OnSelectedItemChanged);

      

without specifying defaultBindingMode

, the parameter is BindingMode

set to a value OneWay

, which means the binding is updated from the source (your view model) to the target (your view).

This can be fixed by changing the defaultBindingMode:



public static BindableProperty SelectedItemProperty =
    BindableProperty.Create<BindablePicker, object>(o => o.SelectedItem, default(object), BindingMode.TwoWay, propertyChanged: OnSelectedItemChanged);

      

or, if this is the default you want for your collector, but only want to update the source in that view, you can specify the BindingMode for that Binding instance only:

<controls:BindablePicker Title="Category"
    ItemsSource="{Binding Categories}"
    SelectedItem="{Binding SelectedCategory, Mode=TwoWay}"
    Grid.Row="2"/>

      

+3


source







All Articles