如何在WPF DataGrid中实现可编辑的DataGridComboxColumn

如何在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

我想使用户能够编辑WPF DataGrid(来自.NETFramework 4.0)中的一些数据。“仪器”栏应允许用户从静态列表中选择可用仪器或编写自由文本。 我的DataGrid使用MVVM绑定到数据。我在互联网上找到了很多解决方案,但没有一个能正常工作。 这是我的密码:

<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 XAML,如下所示

    <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();