Как реализовать редактируемый 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, вы можете попробовать следующее...
- 
добавьте новое свойство в ViewModel под названием InstrumentsView. Этот возвращает новый ListCollectionView.
public static string ListCollectionView InstrumentsView { get { return new ListCollectionView(Instruments); } } - 
измените 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>
Я получил это решение, следуя ответ на подобный вопрос.