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

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/6899697/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-09-08 23:00:59  来源:igfitidea点击:

How to implement editable DataGridComboBoxColumn in WPF DataGrid

wpfbindingdatagriddatagridcomboboxcolumn

提问by Alexander Zwitbaum

I want to enable the user to edit some data in WPF DataGrid ( from the .net Framework 4.0). The "instruments" column should allow the user to select an available intrument from a static list or to write a free text. My DataGrid is binded to data using MVVM. I've tried many solutions I've found in internet but none of them work correctly. Here is my code:

我想让用户在 WPF DataGrid(来自 .net Framework 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>

The drop-down-list is shown correctly. The field can be edited with any text, but it sets a null to the SelectedInstrument after the drop-down is closed for the free text. It works only for the selected item. I've tried to change to SelectedValueBinding, but it doesn't help.

下拉列表显示正确。该字段可以使用任何文本进行编辑,但在自由文本的下拉列表关闭后,它会将 SelectedInstrument 设置为 null。它仅适用于所选项目。我尝试更改为 SelectedValueBinding,但没有帮助。

How to implement this requirements properly? Can someone post here a working sample?

如何正确执行此要求?有人可以在这里发布工作样本吗?

Additional: Orders is ObservableCollection Order has Property like string Title, DateTime Ordered, string SelectedInstrument, Instruments is a string[]

附加:订单是 ObservableCollection 订单具有类似字符串标​​题、日期时间有序、字符串 SelectedInstrument、仪器是字符串 [] 的属性

Solutions: Following suggest as a workaround from bathineniworks:

解决方案:以下建议作为Bathineni作品的解决方法:

<DataGrid Margin="0,6" ItemsSource="{Binding Path=Orders}" AutoGenerateColumns="False" CanUserAddRows="False" CanUserDeleteRows="False" CanUserResizeRows="True">
 <DataGrid.Columns>
  <DataGridTemplateColumn Header="Instrument" MinWidth="140">
   <DataGridTemplateColumn.CellTemplate>
    <DataTemplate>
     <TextBlock Text="{Binding Path=SelectedInstrument, Mode=OneWay}"/>
    </DataTemplate>
   </DataGridTemplateColumn.CellTemplate>
   <DataGridTemplateColumn.CellEditingTemplate>
    <DataTemplate>
     <ComboBox IsEditable="True" Text="{Binding Path=SelectedInstrument}" 
      ItemsSource="{x:Static ViewModel.Instruments}"/>                   
    </DataTemplate>
   </DataGridTemplateColumn.CellEditingTemplate>
  </DataGridTemplateColumn>   
 </DataGrid.Columns>
</DataGrid>

采纳答案by Bathineni

this is happening because the free text which is enter is of type string and selected item what you have binded to the comboBox is of some complex type....

发生这种情况是因为输入的自由文本是字符串类型,而您绑定到组合框的所选项目是某种复杂类型....

instead of using DataGridComboBoxColumnuse DataGridTemplateColumnand you can bind Textproperty of the comboBox to some property which will hold the free text value after closing drop down list.

而不是使用DataGridComboBoxColumnuse DataGridTemplateColumn,您可以将组合Text框的属性绑定到某些属性,该属性将在关闭下拉列表后保存自由文本值。

you can get better idea by looking at the following sample.

通过查看以下示例,您可以获得更好的主意。

<DataGrid>
    <DataGrid.Columns>
        <DataGridTemplateColumn>
            <DataGridTemplateColumn.CellTemplate>
                <DataTemplate>
                    <ComboBox IsEditable="True" 
                              Text="{Binding NewItem}" 
                              ItemsSource="{Binding Sourcelist.Files}"/>
                </DataTemplate>
            </DataGridTemplateColumn.CellTemplate>
        </DataGridTemplateColumn>
    </DataGrid.Columns>
</DataGrid>

回答by WPF-it

Try to use SelectedValue only but along with it use DisplayMemberPath and TextSearch.TextPath.

尝试仅使用 SelectedValue,但同时使用 DisplayMemberPath 和 TextSearch.TextPath。

   <ComboBox IsEditable="True" DisplayMemberPath="MyDisplayProperty" SelectedValuePath="MyValueProperty" SelectedValue="{Binding MyViewModelValueProperty}" TextSearch.TextPath="MyDisplayProperty" />

For editable comboboxes we must synchronize what value the combo selects, what value the items display and what value we must search based on user input.

对于可编辑的组合框,我们必须同步组合选择的值、项目显示的值以及我们必须根据用户输入搜索的值。

But If you are using a string collection to bind your combobox then you can try following...

但是,如果您使用字符串集合来绑定组合框,那么您可以尝试遵循...

  1. Add a new property in your ViewModel called InstrumentsView. This returns a new ListCollectionView.

    public static string ListCollectionView InstrumentsView
    {
            get
            {
                    return new ListCollectionView(Instruments);
            }
    }
    
  2. Change your DataGridComboBoxColumn XAML as below...

    <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>
    
  1. 在您的 ViewModel 中添加一个名为 InstrumentsView 的新属性。这将返回一个新的 ListCollectionView。

    public static string ListCollectionView InstrumentsView
    {
            get
            {
                    return new ListCollectionView(Instruments);
            }
    }
    
  2. 更改您的 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>
    

Tell me if this works....

告诉我这是否有效....

回答by vvnurmi

You can create your own ComboBox column type by subclassing DataGridBoundColumn. Compared to bathineni's solution of subclassing DataGridTemplateColumnthe below solution has the benefit of better user experience (no double-tabbing) and you have more options to tune the column to your specific needs.

您可以通过子类化来创建自己的 ComboBox 列类型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;
    }
}

You can then use the component for example like this.

然后,您可以像这样使用该组件。

<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>

I got this solution by following this answerto a similar question.

我通过对类似问题的回答得到了这个解决方案。