c#——Winform DatagridView 扩展支持combobox列自定义DropDownStyle

本文介绍如何在DataGridView中实现自定义下拉列表,支持不同行显示不同下拉选项,并允许用户输入。通过扩展DataGridView和DataGridViewComboBoxColumn,实现了既能选择又能输入的功能。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

需求:要求datagridview同一列,每一行显示不同的下拉表内容来提供选择列表项。另外没有下拉内容的行需要支持可输入可编辑。

然而,datagridview 自带的DatagridviewcomboboxColumn 默认的下拉样式为dropdownlist,也就是说是不支持输入的,只可选择。

下面就是对datagridview的扩展,使之支持自带combobox 随着下拉内容修改其下拉样式。


扩展的datagridview代码如下:


/// <summary>
    /// 扩展的 DataGridView
    /// </summary>
    public class DataGridViewEx : DataGridView
    {
        bool _showRowHeaderNumbers;

        /// <summary>
        /// 是否显示行号
        /// </summary>
        [Description("是否显示行号"), DefaultValue(true)]
        public bool ShowRowHeaderNumbers
        {
            get { return _showRowHeaderNumbers; }
            set
            {
                if (_showRowHeaderNumbers != value)
                    Invalidate();
                _showRowHeaderNumbers = value;
            }
        }

        public DataGridViewEx()
        {
            _showRowHeaderNumbers = true;
        }

        protected override void OnEditingControlShowing(DataGridViewEditingControlShowingEventArgs e)
        {
            if (CurrentCell != null && CurrentCell.OwningColumn is DataGridViewComboBoxColumnEx)
            {
                //修改组合框的样式
                var combo = e.Control as ComboBox;
                if (combo != null)
                {
                    combo.DropDownHeight =100;
                    if (combo.DataSource == null)
                    {
                        combo.DropDownStyle = ComboBoxStyle.DropDown;
                        combo.DropDownHeight = 1;
                        combo.Leave += new EventHandler(combo_Leave);
                    }
                }
            }
            base.OnEditingControlShowing(e);
        }

        /// <summary>
        /// 当焦点离开时,需要将新输入的值加入到组合框的 Items 列表中
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        void combo_Leave(object sender, EventArgs e)
        {
            var combo = sender as ComboBox;
            if (combo == null) return;
            combo.Leave -= new EventHandler(combo_Leave);
            if (CurrentCell == null || !(CurrentCell.OwningColumn is DataGridViewComboBoxColumnEx)) return;
            //一定要将新输入的值加入到组合框的值列表中
            //否则下一步给单元格赋值的时候会报错(因为值不在组合框的值列表中)
            var cell = CurrentCell as DataGridViewComboBoxCell;
            if (cell == null || cell.DataSource != null) return;
            cell.Items.Add(combo.Text);
            cell.Value = cell.Items[cell.Items.Count-1];
        }
    }
其中根据combobox的datasource绑定是否为空来判断样式是否为dropdownlist,若为dropdownlist只可选择不可编辑,若为dropdown两者皆可。为使得dropdown只允许输入,不允许选择,通过属性dropdownHeight 为1来使之看起来没有下拉框。

还需要对DataGridViewComboBoxColumn进行简单的扩展,添加一个属性字段DropDownStyle,来控制combobox的样式。

代码如下:

  /// <summary>
    /// 可修改 DropDownStyle 的 DataGridViewComboBoxColumn
    /// </summary>
    public class DataGridViewComboBoxColumnEx : DataGridViewComboBoxColumn
    {
        /// <summary>
        /// 控制组合框的外观和功能
        /// </summary>
        [Description("控制组合框的外观和功能"), DefaultValue(ComboBoxStyle.DropDownList)]
        public ComboBoxStyle DropDownStyle { get; set; }

        public DataGridViewComboBoxColumnEx()
        {
            DropDownStyle = ComboBoxStyle.DropDown;
        }
    }


然后设计窗体的时候,

1.把扩展的datagridviewEx控件拖进来。如果已经使用了datagridview,只需要在designer中,修改InitializeComponent方法中,替换datagridview对应名称的类型为扩展类DataGridViewEx即可。

2.修改列的类型为DataGridViewComboBoxColumnEx 

然后来看一下具体使用吧

在为grid赋值的时候,改变combobox的datasource或者再点击combobox列的cell的时候加载指定的datasource

这里采用的是在赋值的时候就一次加载DataGridViewComboBoxColumnEx ,每个单元格对应的datasource。


首先是一个绑定datasource的公共方法


  private static void SetComboboxDataSource(DataGridView grid, int rowIndex, int columnIndex, BindingSource source)
        {
            var currentValue = grid.Rows[rowIndex].Cells[columnIndex].Value.ToString();
            var currentCell = grid.Rows[rowIndex].Cells[columnIndex] as DataGridViewComboBoxCell;
            if (currentCell != null && source != null)
            {
                currentCell.DataSource = source;
                currentCell.DisplayMember = "Key";
                currentCell.ValueMember = "Value";
            }
            else if (currentCell != null && !string.IsNullOrEmpty(currentValue))
            {
                if (currentValue == "---")
                {
                    currentCell.ReadOnly = true;
                    currentCell.Style.Alignment = DataGridViewContentAlignment.MiddleCenter;
                }
                currentCell.Items.Add(currentValue);
                currentCell.Value = currentValue;
            }
        }
如果datasource不为空,就为当前combobox的cell绑定datasource

如果datasource为空,当前cell有值,就将该值添加到items中,这样就不会报错,否则会说cell为无效值。

如果都为空则不操作


接下来 为某一个grid重绘,绑定来源

 /// <summary>
        /// 设置propertyType单元格样式和初始化的值
        /// </summary>
        /// <param name="grid"></param>
        private void SetComboboxValue(DataGridView grid)
        {
            for (var i = 0; i < grid.Rows.Count; i++)
            {
                var rowIndex = i;
                var order = Convert.ToInt32(grid.Rows[rowIndex].Cells[1].Value);
                //第三列赋值
                var enumData = _presenter.GetEnumData(order);
                SetComboboxDataSource(grid, rowIndex, 3, enumData);

                //第四列赋值
                var itemValueEnumData = _presenter.GetItemValueEnumData(order);
                SetComboboxDataSource(grid, rowIndex, 4, itemValueEnumData);
            }
        }


以上是为grid当中的两列赋值,说明我们在grid中可以引用多次DataGridViewComboBoxColumnEx ,并且赋值

 

最后看一下效果吧


下拉内容,只允许选择,不允许输入



无下拉内容,不出现下拉框,可输入可编辑


combobox中输入编辑内容。


最后附,初始做法

其实还有另外一种方式可以实现,就是动态的在datagridview的某一列某一行添加combobox,未添加单元格使用默认的textbox列,也就是说同列不同行具有不同的控件。

这样做的话,列表选择 和内容输入的样式区别大,外观上比较好

先看一下动态加载的代码

      //var propertyTypeCell = grid.Rows[rowIndex].Cells[3];
                //var combo = _presenter.GetEnumData(order);
                //if (combo == null) continue;
                //grid.Controls.Add(combo);
                //var rec = grid.GetCellDisplayRectangle(3, rowIndex, false);
                //if (propertyTypeCell.Value != null)
                //{
                //    combo.SelectedValue = propertyTypeCell.Value.ToString();
                //}
                //combo.Name = rowIndex.ToString(CultureInfo.InvariantCulture);
                //combo.Size = rec.Size;
                //combo.Left = rec.Left;
                //combo.Top = rec.Top;
                //combo.Visible = true;
                //combo.SelectedValueChanged += this.dgrdViewBiblio_CellEndEdit;
                //combo.DropDown += this.dgrdViewBiblio_CellBeginEdit;


在penseter层生成一个带有数据源绑定的combobox

public ComboBox GetEnumData(int order)
        {
            var xsb = new XmlSchemaBuilder();
            Dictionary<string, string> dic;
            switch (order)
            {
                case 0:
                    dic = xsb.TitleEnumData();
                    break;
                case 2:
                    dic = xsb.ContributorEnumData();
                    break;
                case 3:
                    dic = xsb.DateEnumData();
                    break;
                case 23:
                    dic = xsb.DescriptionEnumData();
                    break;
                default:
                    return null;
            }
            var datasource = new BindingSource {DataSource = dic};
            return new ComboBox() { DataSource = datasource ,DisplayMember = "Key",ValueMember = "Value"};
        }


但是最后还是弃用,因为自己动态添加的combobox 和datagridview是分离的,看似在一起。实则互不干涉。

但是datagridview的很多操作需要通知combobox变化,比如说滚动条滚动 比如说删除行。

combobox的变化,比如说编辑,选择等也都需要通知datagridview。比较麻烦

以上方法,在没有滚动的情况下还是可以使用的,但是如果有滚动建议不使用。太麻烦。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值