Динамические элементы столбца со списком DataGridView

#c# #datagridview #datagridcomboboxcolumn

#c# #datagridview #datagridcomboboxcolumn

Вопрос:

У меня есть DataGridView со столбцом со списком. Мне нужно, чтобы у него были разные параметры в зависимости от строки. Значения должны основываться на том же начальном списке, но фильтроваться, чтобы не показывать какие-либо значения, которые уже используются.

Например, у меня есть 4 выпадающих списка: «A», «B», «C» и «D» с 4 строками. Изначально ни для каких строк не задано какое-либо значение для столбца со списком. В первом выпадающем списке, который я нажимаю, я должен увидеть все варианты. Допустим, я выбираю «A». Теперь, если я нажму раскрывающийся список в другой строке, я должен увидеть только «B», «C» и «D», потому что «A» уже используется.

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

Когда я пытаюсь это сделать, я получаю сообщение об ошибке потока данных. Я попытался динамически настроить поле со списком, используя CellClick и CellBeginEdit. В обоих случаях я получаю неожиданное поведение. Иногда в уже выбранных строках значений значение будет просто изменяться, потому что ранее установленное значение больше не находится в выборе. Иногда вообще ничего не происходит.

Просто в качестве примечания, я искал Stack Exchange в течение нескольких часов, все готово, и ни одно из «решений» на самом деле не работает.

РЕДАКТИРОВАТЬ: похоже, что при использовании CellBeginEdit для установки элементов со списком базовые данные в порядке. Проблема заключается только в выбранном значении, отображаемом в поле со списком. Если я просто выбираю ячейку, не опуская поле со списком, значение обновляется до того, каким оно должно быть.

Ответ №1:

Да, это сложно сделать правильно, потому что DataGridView пытается кэшировать и повторно использовать ComboBox в каждом экземпляре DataTemplate. У меня похожий случай, когда мне нужен «фильтрующий» список со списком, который фильтрует список доступных вариантов на основе того, что пользователь ввел до сих пор в ячейку, что является крайней версией того, что вы пытаетесь сделать, поэтому тот же трюк должен сработать. Новое событие FilterChanged может использоваться для привязки к коду, который может обновлять элементы списка со списком по мере необходимости. В вашем случае вы также можете прослушать событие DataContextChanged в FilteringComboBox.

 <DataTemplate x:Key="myTemplateSplitPayeeEdit">
    <local:FilteringComboBox Style="{StaticResource GridComboStyle}"
              SelectedItem="{Binding PayeeOrTransferCaption, Mode=TwoWay}"
              ItemsSource="{Binding RelativeSource={RelativeSource FindAncestor, AncestorType={x:Type views:TransactionsView}}, Path=PayeesAndTransferNames}" 
              PreviewLostKeyboardFocus="ComboBoxForPayee_PreviewLostKeyboardFocus"  
              FilterChanged="ComboBoxForPayee_FilterChanged"
             >
        <ComboBox.ItemsPanel>
            <ItemsPanelTemplate>
                <VirtualizingStackPanel />
            </ItemsPanelTemplate>
        </ComboBox.ItemsPanel>
    </local:FilteringComboBox>
</DataTemplate>


    private void ComboBoxForPayee_FilterChanged(object sender, RoutedEventArgs e)
    {
        FilteringComboBox combo = sender as FilteringComboBox;
        combo.FilterPredicate = new Predicate<object>((o) => { return o.ToString().IndexOf(combo.Filter, StringComparison.OrdinalIgnoreCase) >= 0; });
    }


public class FilteringComboBox : ComboBox
{
    public static RoutedEvent FilterChangedEvent = EventManager.RegisterRoutedEvent("FilterChanged", RoutingStrategy.Bubble, typeof(RoutedEventHandler), typeof(FilteringComboBox));

    public event RoutedEventHandler FilterChanged;

    ListCollectionView view;

    protected override void OnItemsSourceChanged(System.Collections.IEnumerable oldValue, System.Collections.IEnumerable newValue)
    {
        Filter = null;
        Items.Filter = null;
        this.view = newValue as ListCollectionView;
        base.OnItemsSourceChanged(oldValue, newValue);
    }

    public Predicate<object> FilterPredicate
    {
        get { return view.Filter; }
        set { view.Filter = value; }
    }

    public override void OnApplyTemplate()
    {            
        base.OnApplyTemplate();
        TextBox edit = this.Template.FindName("PART_EditableTextBox", this) as TextBox;
        if (edit != null)
        {
            edit.KeyUp  = new System.Windows.Input.KeyEventHandler(OnEditKeyUp);
        }
    }

    void OnEditKeyUp(object sender, System.Windows.Input.KeyEventArgs e)
    {
        TextBox box = (TextBox)sender;
        string filter = box.Text;
        if (string.IsNullOrEmpty(filter))
        {
            Items.Filter = null;
        }
        else if (box.SelectionLength < filter.Length)
        {
            if (box.SelectionStart >= 0)
            {
                filter = filter.Substring(0, box.SelectionStart);
            }
            SetFilter(filter);
        }
    }

    public string Filter { 
        get; set; 
    }

    void SetFilter(string text)
    {
        Filter = text;
        var e = new RoutedEventArgs(FilterChangedEvent);
        if (FilterChanged != null)
        {
            FilterChanged(this, e);
        }
        RaiseEvent(e);
    }

    protected override void OnSelectionChanged(SelectionChangedEventArgs e)
    {
        base.OnSelectionChanged(e);
    }

}
  

Комментарии:

1. Похоже, вы используете WPF. Какие-либо решения для WinForms?