7

I have a Xamarin.Forms (3.2, latest version) Picker implemented as

<Picker x:Name="ClassSelector"
            Title="Select Class"
            ItemsSource="{Binding Classrooms}"
            ItemDisplayBinding="{Binding ClassroomName}"
            SelectedItem="{Binding SelectedClassroom}">
</Picker>

The BindingContext is this ViewModel:

public class ClassroomsViewModel : BaseViewModel
{
  public ObservableCollection<Classroom> Classrooms { get; set; }

  public ClassroomsViewModel()
  {
     Classrooms = new ObservableCollection<Classroom>(_ClassroomsRepository.GetAll());
  }
}

Now, when I add a classroom in the ViewModel, the Picker dutifully adds the item to its list:

 Classrooms.Add(new Classroom() { ClassroomName = "test" });   // this works fine

But if I modify a classroom, the Picker doesn't update its items list:

Classrooms[0].ClassroomName = "test";  // this doesn't have an effect,
                                       // although the value is set
                                       // in the ObservableCollection

I've also tried explicitly calling:

    OnPropertyChanged();  // nothing
    OnPropertyChanged("Classrooms"); // nope

If it's important, Classroom does derive from ObservableObject:

public class Classroom : ObservableObject
{
   private string classroomName;

   public string ClassroomName { get => classroomName; set => SetProperty(ref classroomName, value); }
}

Edit, in which I clarify that this isn't a collection-replacement issue:

Note that I am not replacing the Classrooms collection with a new ObservableCollection -- as happens in other posts that may appear similar to my issue. I am simply modifying one of the collection's members. When this collection is bound to a different control (like a ListView), it behaves exactly as expected -- the ListView updates to reflect the new ClassroomName.

Can anyone point me to what I'm doing wrong?

Graham Charles
  • 9,394
  • 3
  • 26
  • 41
  • Possible duplicate of [Updating ObservableCollection does not properly update ListView in Xamarin Forms](https://stackoverflow.com/questions/49137114/updating-observablecollection-does-not-properly-update-listview-in-xamarin-forms) – EvZ Oct 13 '18 at 19:19

2 Answers2

6

Typically, when we use bindings, we expect the target (a picker-item) to keep itself in sync with the binding source (in this case a Classroom object) - so, your code, by design, is correct.

However, after digging into Picker source code in XF github - it looks like it unsubscribes from binding/property-changes after pulling the first value from Binding. Here you can see the explicit call to UnApply() to the binding, and subsequently to the binding-expression for picker-item display name.

Invoking OnPropertyChanged("Classrooms"); on CustomerViewModel (not on Customer) should technically work. Or, you can try removing the modified Customer object from the collection, and re-adding at same index to fix this issue. Both of these actions will trigger the ResetItems() - which in turn should trigger the refresh for picker-item's display name.

Sharada Gururaj
  • 13,471
  • 1
  • 22
  • 50
  • 2
    Thank you for this. Using `OnPropertyChanged("Classrooms")` on the view model does not seem to trigger `ResetItems()`, but the remove/replace item workaround takes care of it. – Graham Charles Oct 14 '18 at 19:48
  • 1
    I assume that the `UnApply()` call is how Xamarin controls make themselves `OneWay` bound? Which would imply that the Picker's ItemsSource binding is *only* `OneWay`. This isn't noted in the documentation, so I'm going to file it as a bug report (or, at the very least, a need for an update to the documentation). – Graham Charles Oct 14 '18 at 19:49
  • 1
    Issue added to Xamarin.Forms : https://github.com/xamarin/Xamarin.Forms/issues/4077 – Graham Charles Oct 14 '18 at 21:13
  • @GrahamCharles: It seems UnApply pretty much disconnects the binding connection between source and target. That is different from `OneWay` mode, where you expect the change notification to always flow from source to target. Were you talking about `OneTime` mode? – Sharada Gururaj Oct 14 '18 at 22:57
  • Having said that, thank you for reporting the issue. This should be documented at-least. – Sharada Gururaj Oct 14 '18 at 22:58
  • Oh right, `OneTime` makes more sense. Thanks again. – Graham Charles Oct 16 '18 at 00:39
2

For some reason, setting the source to null and then resetting it works for me:

var items = picker.ItemsSource as List<string>;
items.Add("item");
picker.ItemsSource = null;
picker.ItemsSource = items;
Savage
  • 2,296
  • 2
  • 30
  • 40