将同一个 Collection 绑定到 ComboBox 和 Datagrid

Binding the same Collection to ComboBox and Datagrid

GitHub Link: https://github.com/babakin34/wpf_test/tree/master/WpfApp1

我有以下 类:

VIEWMODELS:


public class PersonVM : BindableBase
{
  public int ID { get; set; }

  private string _lastName;
  public string LastName
  {
      get { return _lastName; }
      set { SetProperty(ref _lastName, value); }
  }
}

public class MainVM : BindableBase
{
  public ObservableCollection<PersonVM> People { get; set; }

  private PersonVM _selectedPerson;
  public PersonVM SelectedPerson
  {
      get { return _selectedPerson; }
      set { SetProperty(ref _selectedPerson, value); }
  }

  public MainVM()
  {
      People = new ObservableCollection<PersonVM>()
      {
          new PersonVM()
          {
              ID = 1,
              LastName = "AA"
          },
          new PersonVM()
          {
              ID = 2,
              LastName = "BB"
          },
      };

      SelectedPerson = People.First();
  }
}

查看


<Grid>
    <StackPanel>
        <ComboBox ItemsSource="{Binding People}" 
                  SelectedItem="{Binding SelectedPerson}"
                  DisplayMemberPath="LastName" 
                  Margin="0,5,0,25"/>

        <DataGrid ItemsSource="{Binding People}"/>
    </StackPanel>
</Grid>


当用户选择空元素时,如何实现 ComboBox 的 "MainVM.SelectedPerson" 得到通知,这是由 Datagrid 的默认最后一个条目引起的?

PS: 我正在使用 Prism,但问题与 Prism 无关。您可以将 BindableBase 替换为 INotifyPropertyChanged。

编辑: 这里的真正问题与我起初想的有点不同。选择插入行时,您会在输出 window 中看到 WPF 无法将 "NamedObject" 转换为 "PersonVM"。 Datagrid 为要使用的插入行创建了一个 NamedObject,但绑定根本不起作用,因为它的类型错误。 干净简单的解决方案是创建一个转换器来检查对象是否属于 PersonVM 类型,否则 return null(而不是 NamedObject 实例)。

public class MyConverter : IValueConverter
{
    public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
    {
        return value;
    }

    public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture)
    {
        if (value is PersonVM)
            return value;
        return null;
    }
}

并且在 xaml

<DataGrid x:Name="DataGrid"
                  ItemsSource="{Binding People}"
                  SelectedCellsChanged="DataGrid_OnSelectedCellsChanged"
                  SelectedItem="{Binding SelectedPerson,
                                         Converter={StaticResource myConverter}}"


又旧又脏

不是最好的方法,但如果您不介意在代码隐藏中使用视图模型(或通过接口抽象它),您可以使用 "SelectionChanged" 事件将 ViewModel 中的 SelectedPerson 设置为 null如果任何选定的项目不是您需要的类型。

private void Selector_OnSelectionChanged(object sender, SelectionChangedEventArgs e)
    {
        bool invalidStuffSelected = false;
        //throw new System.NotImplementedException();
        foreach (var obj in DataGrid.SelectedItems)
        {
            if (!(obj is PersonVM))
                invalidStuffSelected = true;
        }
        MainVM vm = (MainVM) this.DataContext;
        if (invalidStuffSelected)
            vm.SelectedPerson = null;
    }

在您的示例中,选择模式 "single" 会更有意义,因为组合框只能显示一个选定值。