如何在WPF DataGrid中实现可编辑的DataGridComboxColumn
我想使用户能够编辑WPF DataGrid(来自.NETFramework 4.0)中的一些数据。“仪器”栏应允许用户从静态列表中选择可用仪器或编写自由文本。 我的DataGrid使用MVVM绑定到数据。我在互联网上找到了很多解决方案,但没有一个能正常工作。 这是我的密码:如何在WPF DataGrid中实现可编辑的DataGridComboxColumn,wpf,binding,datagrid,datagridcomboboxcolumn,Wpf,Binding,Datagrid,Datagridcomboboxcolumn,我想使用户能够编辑WPF DataGrid(来自.NETFramework 4.0)中的一些数据。“仪器”栏应允许用户从静态列表中选择可用仪器或编写自由文本。 我的DataGrid使用MVVM绑定到数据。我在互联网上找到了很多解决方案,但没有一个能正常工作。 这是我的密码: <DataGrid Margin="0,6" ItemsSource="{Binding Path=Orders}" AutoGenerateColumns="False" CanUserAddRows="False
<DataGrid Margin="0,6" ItemsSource="{Binding Path=Orders}" AutoGenerateColumns="False" CanUserAddRows="False" CanUserDeleteRows="False" CanUserResizeRows="True">
<DataGrid.Columns>
<DataGridComboBoxColumn Header="Instrument" MinWidth="140"
ItemsSource="{x:Static ViewModel.Instruments}" SelectedItemBinding="{Binding Path=SelectedInstrument}">
<DataGridComboBoxColumn.EditingElementStyle>
<Style TargetType="ComboBox">
<Setter Property="IsEditable" Value="True"/>
</Style>
</DataGridComboBoxColumn.EditingElementStyle>
</DataGridComboBoxColumn>
</DataGrid.Columns>
</DataGrid>
下拉列表显示正确。该字段可以使用任何文本进行编辑,但在自由文本的下拉列表关闭后,它会将SelectedInstrument设置为空。它仅适用于所选项目。我尝试更改为SelectedValueBinding,但没有帮助
如何正确实施这些要求?有人能在这里发布一个工作样本吗
其他:
订单是可观察的集合
Order具有诸如字符串标题、日期时间顺序、字符串SelectedInstrument、,
乐器是一根弦
解决方案:
以下建议作为工程的解决办法:
尝试仅使用SelectedValue,但同时使用DisplayMemberPath和TextSearch.TextPath
<ComboBox IsEditable="True" DisplayMemberPath="MyDisplayProperty" SelectedValuePath="MyValueProperty" SelectedValue="{Binding MyViewModelValueProperty}" TextSearch.TextPath="MyDisplayProperty" />
<DataGridComboBoxColumn Header="Instrument" MinWidth="140"
ItemsSource="{x:Static ViewModel.InstrumentsView}">
<DataGridComboBoxColumn.EditingElementStyle>
<Style TargetType="ComboBox">
<Setter Property="IsEditable" Value="True"/>
<Setter Property="IsSynchronizedWithCurrentItem" Value=True" />
<Setter Property="SelectedItem" Value="{Binding SelectedInstrument, Mode=OneWayToSource}" /> <!-- Assuming that SelectedInstrument is string -->
</Style>
</DataGridComboBoxColumn.EditingElementStyle>
</DataGridComboBoxColumn>
之所以会出现这种情况,是因为输入的自由文本是string类型,而绑定到组合框的选定项是某种复杂类型
不要使用DataGridComboxColumn
而使用DataGridTemplateColumn
,您可以将组合框的Text
属性绑定到关闭下拉列表后将保存自由文本值的某个属性
通过查看以下示例,您可以获得更好的想法
<DataGrid>
<DataGrid.Columns>
<DataGridTemplateColumn>
<DataGridTemplateColumn.CellTemplate>
<DataTemplate>
<ComboBox IsEditable="True"
Text="{Binding NewItem}"
ItemsSource="{Binding Sourcelist.Files}"/>
</DataTemplate>
</DataGridTemplateColumn.CellTemplate>
</DataGridTemplateColumn>
</DataGrid.Columns>
</DataGrid>
您可以通过子类化DataGridBoundColumn
来创建自己的组合框列类型。与bathineni的子类化DataGridTemplateColumn
的解决方案相比,下面的解决方案具有更好的用户体验(无双选项卡),并且您可以根据自己的具体需要调整专栏
public class DataGridComboBoxColumn : DataGridBoundColumn {
public Binding ItemsSourceBinding { get; set; }
protected override FrameworkElement GenerateElement(DataGridCell cell, object dataItem) {
var textBox = new TextBlock();
BindingOperations.SetBinding(textBox, TextBlock.TextProperty, Binding);
return textBox;
}
protected override FrameworkElement GenerateEditingElement(DataGridCell cell, object dataItem) {
var comboBox = new ComboBox { IsEditable = true };
BindingOperations.SetBinding(comboBox, ComboBox.TextProperty, Binding);
BindingOperations.SetBinding(comboBox, ComboBox.ItemsSourceProperty, ItemsSourceBinding);
return comboBox;
}
protected override object PrepareCellForEdit(FrameworkElement editingElement, RoutedEventArgs editingEventArgs) {
var comboBox = editingElement as ComboBox;
if (comboBox == null) return null;
comboBox.Focus(); // This solves the double-tabbing problem that Nick mentioned.
return comboBox.Text;
}
}
然后可以像这样使用组件
<DataGrid AutoGenerateColumns="False" ItemsSource="{Binding MyItems}">
<DataGrid.Columns>
<DataGridTextColumn Header="Name" Binding="{Binding Name}"/>
<local:DataGridComboBoxColumn Header="Thingy" Binding="{Binding Thingy}"
ItemsSourceBinding="{Binding
RelativeSource={RelativeSource AncestorType={x:Type local:MainWindow}},
Path=Thingies}"/>
</DataGrid.Columns>
</DataGrid>
我通过回答一个类似的问题得到了这个解决方案。也许它对某些人仍然有用。此解决方案允许向选择列表添加新输入的值,并且在编辑时没有副作用
XAML:
<DataGridTemplateColumn Header="MyHeader" Width="Auto">
<DataGridTemplateColumn.CellTemplate>
<DataTemplate>
<ComboBox IsEditable="True"
Text="{Binding MyTextProperty, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}"
DisplayMemberPath="MyTextProperty"
SelectedValuePath="MyTextProperty"
ItemsSource="{Binding RelativeSource={RelativeSource FindAncestor, AncestorType={x:Type DataGrid}}, Path=DataContext.SelectionList}"/>
</DataTemplate>
</DataGridTemplateColumn.CellTemplate>
</DataGridTemplateColumn>
public class MyViewModel
{
public class MyItem : INotifyPropertyChanged {
private string myTextProperty;
public string MyTextProperty {
get { return myTextProperty; }
set { myTextProperty = value;
OnPropertyChanged("MyTextProperty"); }
}
public event PropertyChangedEventHandler PropertyChanged;
public void OnPropertyChanged([CallerMemberName]string prop = "")
{
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(prop));
}
}
public ObservableCollection<MyItem> MyItems { get; set; }
public object SelectionList { get; set; }
}
MyWindow.DataContext = MyViewModelInstance;
MyDataGrid.ItemsSource = MyItems;
// Before DataGrid loading and each time after new MyProperty value adding, you must execute:
MyViewModelInstance.SelectionList = MyViewModelInstance.MyItems.OrderBy(p => p.MyTextProperty).GroupBy(p => p.MyTextProperty).ToList();
您是要更改DataGridComboxColumn.EditingElementStyle还是要使用DataGridTemplateColumn而不是DataGridComboxColumn?而且我的对象没有任何类似MyValueProperty的子属性,因为它是一个字符串。请查看我上面编辑的回复,以及字符串集合的新可能解决方案。如果选择了仪器{get{return InstrumentView.CurrentItem;}},我如何实现设置以初始化初始视图?CurrentItem是只读的。很遗憾,您的解决方案不起作用。我得到一个空的下拉列表,我以前选择的仪器没有显示在封闭的单元格中。谢谢,如果我加上,它工作得很好。这是一个糟糕的用户体验。您必须在该列中进行一次tab,然后在组合框中进行第二次tab。datagrid中的所有其他列只需要1个tab,这一列需要按tab两次。使用DataGridTemplateColumn+ComboBox也会使按“tab”键在单元格之间进行更改变得奇怪。它将卡在组合框列中,或从上一个选定的正常单元格(左)继续到下一个单元格(右)。我认为在您的解决方案中,您应该替换
public class MyViewModel
{
public class MyItem : INotifyPropertyChanged {
private string myTextProperty;
public string MyTextProperty {
get { return myTextProperty; }
set { myTextProperty = value;
OnPropertyChanged("MyTextProperty"); }
}
public event PropertyChangedEventHandler PropertyChanged;
public void OnPropertyChanged([CallerMemberName]string prop = "")
{
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(prop));
}
}
public ObservableCollection<MyItem> MyItems { get; set; }
public object SelectionList { get; set; }
}
MyWindow.DataContext = MyViewModelInstance;
MyDataGrid.ItemsSource = MyItems;
// Before DataGrid loading and each time after new MyProperty value adding, you must execute:
MyViewModelInstance.SelectionList = MyViewModelInstance.MyItems.OrderBy(p => p.MyTextProperty).GroupBy(p => p.MyTextProperty).ToList();