ItemsSource 更改时选择器未更新

Picker not updating when ItemsSource changed

我有一个Xamarin.Forms(3.2,最新版本)Picker实现为

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

BindingContext 是这个 ViewModel:

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

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

现在,当我在 ViewModel 中添加教室时,选择器尽职尽责地将项目添加到其列表中:

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

但是如果我修改一个教室,Picker不会更新它的项目列表:

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

我也试过显式调用:

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

如果它很重要,Classroom 确实派生自 ObservableObject:

public class Classroom : ObservableObject
{
   private string classroomName;

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

编辑,我在此澄清 不是 collection-replacement 问题:

请注意,我 不是 Classrooms collection 替换为新的 ObservableCollection —— 就像在其他可能与我的问题类似的帖子中发生的那样.我只是 修改 collection 的成员之一。当此 collection 绑定到不同的控件(如 ListView)时,它的行为完全符合预期——ListView 更新以反映新的 ClassroomName

谁能指出我做错了什么?

通常,当我们使用绑定时,我们希望目标(选择器项)与绑定源(在本例中为 Classroom 对象)保持同步 - 因此,您的代码,通过设计,是正确的。

然而,在深入研究 XF github 中的 Picker 源代码之后 - 看起来它在从 Binding 中提取第一个值后取消了对 binding/property-changes 的订阅。 Here you can see the explicit call to UnApply() 到绑定,然后到 picker-item 显示名称的绑定表达式。

CustomerViewModel 上调用 OnPropertyChanged("Classrooms");(而不是在 Customer 上)在技术上应该可行。或者,您可以尝试从集合中删除修改后的 Customer 对象,然后在同一索引处重新添加以解决此问题。这两个操作都会触发 ResetItems() - which in turn should trigger the refresh for picker-item's display name.

出于某种原因,将源设置为 null 然后重置它对我有用:

var items = picker.ItemsSource as List<string>;
items.Add("item");
picker.ItemsSource = null;
picker.ItemsSource = items;