VB.NET中如何重写DataGridViewComboBoxColumn实现自定义交互

实现方案
核心逻辑分三步实现:
- 先将默认只读的ComboBox列改为可直接输入文本的模式
- 拦截ComboBox下拉按钮的点击事件,屏蔽原生下拉菜单的弹出逻辑
- 在拦截到点击动作时,将自定义Form定位到当前单元格下方弹出,选择完成后将结果回写到单元格即可
完整代码实现
首先自定义可拦截下拉、支持自由输入的编辑控件:
public class EditableComboEditingControl : ComboBox, IDataGridViewEditingControl { private DataGridView _dataGridView; private int _rowIndex; private bool _valueChanged; private bool _dropDownHandled = false; public EditableComboEditingControl() { DropDownStyle = ComboBoxStyle.DropDown; // 拦截默认下拉事件 DropDown += (s, e) => { if (!_dropDownHandled) { e.Cancel = true; _dropDownHandled = true; // 替换为你自己的自定义选择窗体 using (var customSelectForm = new YourCustomSelectionForm()) { // 计算窗体弹出位置,对齐单元格左下角 var currentCell = _dataGridView.CurrentCell; var cellBounds = _dataGridView.GetCellDisplayRectangle(currentCell.ColumnIndex, currentCell.RowIndex, false); var screenLocation = _dataGridView.PointToScreen(new Point(cellBounds.Left, cellBounds.Bottom)); customSelectForm.StartPosition = FormStartPosition.Manual; customSelectForm.Location = screenLocation; customSelectForm.ShowInTaskbar = false; customSelectForm.FormBorderStyle = FormBorderStyle.FixedToolWindow; if (customSelectForm.ShowDialog() == DialogResult.OK) { // 回写选中值 Text = customSelectForm.SelectedResult?.ToString() ?? string.Empty; _valueChanged = true; _dataGridView.NotifyCurrentCellDirty(true); } } _dropDownHandled = false; } }; } // IDataGridViewEditingControl 接口标准实现 public DataGridView EditingControlDataGridView { get => _dataGridView; set => _dataGridView = value; } public object EditingControlFormattedValue { get => Text; set => Text = value?.ToString() ?? string.Empty; } public int EditingControlRowIndex { get => _rowIndex; set => _rowIndex = value; } public bool EditingControlValueChanged { get => _valueChanged; set => _valueChanged = value; } public Cursor EditingPanelCursor => Cursors.Default; public bool RepositionEditingControlOnValueChange => false; public void ApplyCellStyleToEditingControl(DataGridViewCellStyle dataGridViewCellStyle) { Font = dataGridViewCellStyle.Font; ForeColor = dataGridViewCellStyle.ForeColor; BackColor = dataGridViewCellStyle.BackColor; } public bool EditingControlWantsInputKey(Keys keyData, bool dataGridViewWantsInputKey) { var key = keyData & Keys.KeyCode; return key is Keys.Left or Keys.Right or Keys.Up or Keys.Down or Keys.Home or Keys.End or Keys.Delete || !dataGridViewWantsInputKey; } public object GetEditingControlFormattedValue(DataGridViewDataErrorContexts context) => Text; public void PrepareEditingControlForEdit(bool selectAll) { if (selectAll) SelectAll(); } protected override void OnTextChanged(EventArgs e) { base.OnTextChanged(e); _valueChanged = true; _dataGridView?.NotifyCurrentCellDirty(true); } }
再配套自定义单元格和列类型:
public class EditableComboCell : DataGridViewComboBoxCell { public override Type EditType => typeof(EditableComboEditingControl); public override Type ValueType => typeof(string); public override object DefaultNewRowValue => string.Empty; } public class EditableComboColumn : DataGridViewComboBoxColumn { public EditableComboColumn() { CellTemplate = new EditableComboCell(); DropDownStyle = ComboBoxStyle.DropDown; } }
使用注意事项
- 把DataGridView中原生的
DataGridViewComboBoxColumn替换为上面实现的EditableComboColumn即可生效,用户可直接在单元格内输入任意文本 - 自定义选择窗体可以根据需求实现搜索、多选、树形选择等任意逻辑,只要在确认选择后把结果赋值给窗体的
SelectedResult属性即可自动回写到单元格 - 如果需要实现点击窗体外自动关闭的效果,可以重写自定义窗体的
WndProc方法,捕获非客户区的鼠标点击消息触发Close即可。
内容的提问来源于stack exchange,提问作者Senior Guru
相关产品推荐
相关产品推荐

