Как реализовать редактируемый 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>
Я получил это решение, следуя ответ на подобный вопрос.