将同一个 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" 会更有意义,因为组合框只能显示一个选定值。
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" 会更有意义,因为组合框只能显示一个选定值。