DataGridView ComboBox列动态项

时间:2016-10-11 00:37:26

标签: c# datagridview datagridcomboboxcolumn

我有一个带有ComboBox列的DataGridView。我需要它根据行有不同的选项。值必须基于相同的初始列表,但过滤后不显示任何已使用的值。

例如,我有4个下拉选项:" A"," B"," C"和" D"有4行。最初,没有行设置为组合框列的任何值。第一次下拉我点击我应该看到所有的选择。让我们说我选择" A"。现在,如果我点击另一行的下拉列表,我现在应该只看到" B"," C"和" D"因为" A"已被使用。

我还希望一直在顶部有一个空选项。

当我尝试这样做时,我收到DataRow错误。我尝试使用CellClick和CellBeginEdit动态设置ComboBox。在这两种情况下,我都会遇到意外行有时已选择的值行将具有仅更改的值,因为先前设置的值不再在选项中。有时根本没有任何事情发生。

就像一张纸条一样,我已经搜索了Stack Exchange几个小时的准备就绪,并且没有一个"解决方案"实际上工作。

编辑:看来通过使用CellBeginEdit来设置ComboBox项目,基础数据就可以了。它只是在组合框中显示的选定值,即问题。如果我只选择单元格而不删除组合框,则值会刷新到它应该是什么。

1 个答案:

答案 0 :(得分:0)

是的,因为DataGridView尝试在每个DataTemplate实例化中缓存并重用ComboBox,所以要做对是很棘手的。我有一个类似的情况,我想要一个“过滤”组合框,根据用户到目前为止输入到单元格中的内容过滤可用选项列表,这是你想要做的极端版本​​,所以同样的技巧应该管用。新的FilterChanged事件可用于绑定到可根据需要更新组合框列表项的代码。在您的事业中,您还可以在FilteringComboBox上收听DataContextChanged事件。

<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);
    }

}