Как реализовать редактируемый DataGridComboBoxColumn в WPF DataGrid

Я хочу, чтобы пользователь мог редактировать некоторые данные в 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>

раскрывающийся список отображается правильно. Поле можно редактировать любым текстом, но оно устанавливает значение null в SelectedInstrument после раскрывающегося списка закрывается для свободного текста. Он работает только для выбранного элемента. Я попытался изменить SelectedValueBinding, но это не помогает.

Как правильно реализовать эти требования? Может кто-нибудь разместить здесь рабочий образец?

дополнительные: Заказы коллекции ObservableCollection Order имеет свойство, как заголовок строки, datetime Ordered, string SelectedInstrument, Инструментов string[]

решений: Следующее предложение в качестве обходного пути от 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>

3 ответов


это происходит потому, что свободный текст, который вводится, имеет тип string и выбранный элемент, который вы привязали к comboBox, имеет какой-то сложный тип....

вместо DataGridComboBoxColumn использовать DataGridTemplateColumn и вы можете привязать Text свойство comboBox к некоторому свойству, которое будет удерживать значение свободного текста после закрытия выпадающего списка.

вы можете получить лучшую идею путем смотреть следующий образец.

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

попробуйте использовать только SelectedValue, но вместе с ним используйте DisplayMemberPath и TextSearch.TextPath.

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

для редактируемых комбинаций мы должны синхронизировать, какое значение выбирает комбо, какое значение отображают элементы и какое значение мы должны искать на основе пользовательского ввода.

но если вы используете коллекцию строк для привязки combobox, вы можете попробовать следующее...

  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>
    

скажите мне, если это работает....


вы можете создать свой собственный тип столбца ComboBox путем подкласса DataGridBoundColumn. По сравнению с решением батинени подклассов 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>

Я получил это решение, следуя ответ на подобный вопрос.