You need to enable JavaScript to run this app.
最新活动
大模型
产品
解决方案
定价
生态与合作
支持与服务
开发者
了解我们

WinForms中DataGridView下拉框按外键过滤问题(C#)

实现DataGridViewComboBox列根据当前物料动态筛选价目表

这个需求我之前做过类似的,核心就是抓住用户进入ComboBox单元格编辑状态的时机,根据当前行的物料ID去筛选对应的价目表数据源,替换掉ComboBox的默认选项。下面给你一步步拆解实现方法:

1. 准备好数据源

假设你已经有三个核心DataTable:

  • materialsTable:存储物料信息,包含MaterialId(主键)和MaterialName(显示用的物料名称)
  • pricelistsTable:存储价目表信息,包含PricelistId(主键)、PricelistName(显示用的价目表名称)、MaterialId(关联物料的外键)
  • mainTable:绑定到DataGridView的主表,包含MaterialIdPricelistId两个字段

2. 初始化DataGridView控件

在窗体加载时,先配置好列的基础属性,包括MaterialId的只读显示(显示物料名称而非ID),以及PricelistId的ComboBox列初始设置:

private void Form1_Load(object sender, EventArgs e)
{
    // 绑定主表到DataGridView
    dataGridView1.DataSource = mainTable;

    // 配置MaterialId列:只读,显示物料名称
    var materialCol = dataGridView1.Columns["MaterialId"] as DataGridViewTextBoxColumn;
    materialCol.ReadOnly = true;
    // 通过CellFormatting事件把ID转换成名称显示
    dataGridView1.CellFormatting += DataGridView1_CellFormatting;

    // 配置PricelistId的ComboBox列
    var pricelistCol = dataGridView1.Columns["PricelistId"] as DataGridViewComboBoxColumn;
    pricelistCol.DisplayMember = "PricelistName";
    pricelistCol.ValueMember = "PricelistId";
    // 先绑定全部价目表,后续会动态替换
    pricelistCol.DataSource = pricelistsTable;
}

// 处理MaterialId列的显示转换
private void DataGridView1_CellFormatting(object sender, DataGridViewCellFormattingEventArgs e)
{
    // 判断当前列是MaterialId列且值不为空
    if (e.ColumnIndex == dataGridView1.Columns["MaterialId"].Index && e.Value != null && e.Value != DBNull.Value)
    {
        // 根据ID找到对应的物料名称
        var targetRow = materialsTable.Select($"MaterialId = {e.Value}").FirstOrDefault();
        if (targetRow != null)
        {
            e.Value = targetRow["MaterialName"];
            e.FormattingApplied = true;
        }
    }
}

3. 核心:动态筛选ComboBox的数据源

通过EditingControlShowing事件,在用户点击ComboBox单元格进入编辑状态时,获取当前行的物料ID,筛选出对应的价目表并绑定到ComboBox:

private void DataGridView1_EditingControlShowing(object sender, DataGridViewEditingControlShowingEventArgs e)
{
    // 只处理PricelistId列的单元格
    if (dataGridView1.CurrentCell.ColumnIndex == dataGridView1.Columns["PricelistId"].Index)
    {
        var editComboBox = e.Control as ComboBox;
        if (editComboBox != null)
        {
            // 先移除旧的SelectedIndexChanged事件,避免重复触发
            editComboBox.SelectedIndexChanged -= EditComboBox_SelectedIndexChanged;

            // 获取当前行的物料ID
            var currentMaterialId = dataGridView1.CurrentRow.Cells["MaterialId"].Value;
            if (currentMaterialId != null && currentMaterialId != DBNull.Value)
            {
                // 筛选对应物料的价目表(注意如果MaterialId是字符串,要加单引号:$"MaterialId = '{currentMaterialId}'")
                DataRow[] filteredRows = pricelistsTable.Select($"MaterialId = {currentMaterialId}");
                DataTable filteredPricelist = filteredRows.CopyToDataTable();

                // 如果没有匹配的价目表,可以添加一个提示项
                if (filteredPricelist.Rows.Count == 0)
                {
                    filteredPricelist.Rows.Add(-1, "无对应价目表");
                }

                // 绑定筛选后的数据源到ComboBox
                editComboBox.DataSource = filteredPricelist;
                editComboBox.DisplayMember = "PricelistName";
                editComboBox.ValueMember = "PricelistId";

                // 保持当前单元格已有的选中值
                if (dataGridView1.CurrentCell.Value != DBNull.Value)
                {
                    editComboBox.SelectedValue = dataGridView1.CurrentCell.Value;
                }
            }

            // 重新绑定SelectedIndexChanged事件,确保选择后更新单元格值
            editComboBox.SelectedIndexChanged += EditComboBox_SelectedIndexChanged;
        }
    }
}

// 处理ComboBox选择后的单元格值更新
private void EditComboBox_SelectedIndexChanged(object sender, EventArgs e)
{
    var comboBox = sender as ComboBox;
    if (comboBox != null && dataGridView1.CurrentCell != null)
    {
        dataGridView1.CurrentCell.Value = comboBox.SelectedValue;
    }
}

注意事项

  • 如果你的MaterialId是字符串类型,筛选语句里要给值加上单引号,避免语法错误(比如$"MaterialId = '{currentMaterialId}'"
  • 要处理DBNull.Value的情况,避免空引用异常
  • 如果筛选后没有匹配的价目表,建议添加提示项,提升用户体验

内容的提问来源于stack exchange,提问作者Alex

火山引擎 最新活动