Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

BindableProperty not updated on ViewModel

In Xamarin.Forms I implemented a custom Picker. The ItemsSource is set correctly. However when i change the selected item it does not update the property on my ViewModel.

The 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());
        }
    }
}

The Xamlpage:

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

The ViewModel properties, didn't implement the NotifyPropertyChanged on the properties since they only need to be updated from the ´Viewto theViewModel`:

public Category SelectedCategory { get; set; }
public ObservableCollection<Category> Categories { get; set; }
like image 413
JMan Avatar asked Nov 05 '25 06:11

JMan


1 Answers

When creating your BindableProperty:

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

without specifying the defaultBindingMode, the BindingMode is set to OneWay, meaning the Binding is updated from source (your view model) to 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 it's the default you want for your picker, but want to update the source only in this view, you can specify the BindingMode for this instance of the Binding only:

<controls:BindablePicker Title="Category"
    ItemsSource="{Binding Categories}"
    SelectedItem="{Binding SelectedCategory, Mode=TwoWay}"
    Grid.Row="2"/>
like image 59
Stephane Delcroix Avatar answered Nov 08 '25 14:11

Stephane Delcroix