带有按钮、组合框和文本框的 C# WinForms (.NET Framework) DataGridView:使用按钮添加新行以添加组合框项时出错

Posted

技术标签:

【中文标题】带有按钮、组合框和文本框的 C# WinForms (.NET Framework) DataGridView:使用按钮添加新行以添加组合框项时出错【英文标题】:C# WinForms (.NET Framework) DataGridView with Button, Combo Box & Text Box: Errors adding New Row using Button to Add Combo Box Item 【发布时间】:2021-11-21 16:46:50 【问题描述】:

我有一个 Windows 窗体应用程序 (.NET Framework) 来显示 SQL 数据库中的记录。表单显示单个记录并通过记录确定。每条记录可能没有或有更多相关记录(磁盘上与父记录相关的文件列表)(在 M:M 关系中),所以我使用 DataGridView 来显示相关记录。 DGV 有 4 列:一个按钮列,它打开一个表单,作为一个对话框,将新文件添加到数据库的文件列表中,第二个按钮列使用其常用命令行打开文件,一个组合框列绑定到所有文件的列表并显示文件名,以及显示文件类型的文本框列。

就显示而言,一切正常,包括在浏览所有父记录、创建新父记录、删除父记录或更新父记录时。所有也可以添加新行,删除行,甚至更新一行上的选定文件。当尝试使用 Add New 按钮(第一个 Button 列)添加一个不在列表中的新文件时,问题就开始了。如果我在现有行上执行此操作,则没有问题,新文件被添加并在下拉列中选择,新文件的文件类型显示在文本框列中。但是,我无法从新行中完成这项工作,换句话说,我无法使用添加新按钮在 DGV 的新空白行中添加新文件。

我遇到的第一个错误是因为新行的 DataBoundItem 为空。由于这是一个只读属性,我无法设置它。我尝试使用该过程以编程方式添加此处几篇文章中描述的新行,但出现错误,我无法以编程方式添加数据绑定的行。在这一点上,我真的不确定我错过了什么。

以下是代码的相关部分。在选择父记录(或创建新记录)时,调用 BindExperimentFilesDataGridView 方法:

private void BindExperimentFilesDataGridView(ExperimentModel currentExperiment)
    
        // TODO -- set formatting properties for DataGridView
        List<FileModel> filesbyexperiment = _sql.GetFilesByExperimentId(currentExperiment.Id);
        _currentExperimentFileIds = new List<int>();
        foreach ( FileModel f in filesbyexperiment ) 
            _currentExperimentFileIds.Add(f.Id);
        

        _experimentFiles = new BindingList<FileModel>(filesbyexperiment);
        experimentFilesDataGridView.AutoGenerateColumns = false;
        experimentFilesDataGridView.DataSource = _experimentFiles;
        FileNameColumn.DataSource = _allFiles;
        FileNameColumn.DataPropertyName = nameof(FileModel.FileName);
        FileNameColumn.DisplayMember = nameof(FileModel.FileName);
        FileNameColumn.ValueMember = nameof(FileModel.FileName);
        FileTypeColumn.DataPropertyName = nameof(FileModel.FileTypeName);
    

单击添加新按钮是通过 CellClick 事件处理的:

private void ExperimentFilesDataGridView_CellClick(object sender, DataGridViewCellEventArgs e)
    
        // ignore clicks that are not on button cells
        if ( e.RowIndex < 0 || e.ColumnIndex == addNewFileButtonColumn.Index ) 
            NewFileDialogForm newfiledialogform = new NewFileDialogForm(this, e.RowIndex);
            newfiledialogform.Show();
        

        if ( e.RowIndex < 0 || e.ColumnIndex == openFileButtonColumn.Index ) 
            DataGridViewRow row = experimentFilesDataGridView.Rows[e.RowIndex];
            FileModel data = row.DataBoundItem as FileModel;
            OpenFile(data);
        
    

当 NewFileDialogForm 返回时,它会调用父窗体的 SelectFiles 方法:

public void SelectFile(FileModel fileModel, int rowIndex)
    
        DataGridViewRow row = experimentFilesDataGridView.Rows[rowIndex];
        DataGridViewCell cell = row.Cells[FileNameColumn.Index];
        cell.Value = fileModel.FileName;
    

FileNameColumn(也就是ComboBoxColumn)中cell.Value的改变触发了Cell Value Changed事件,它的handler负责设置Text Box列中文件类型的值: 私人无效 ExperimentFilesDataGridView_CellValueChanged(对象发送者,DataGridViewCellEventArgs e) if ( e.RowIndex

        DataGridViewRow row = experimentFilesDataGridView.Rows[e.RowIndex];
        DataGridViewCell cell = row.Cells[e.ColumnIndex];
        if ( cell is DataGridViewComboBoxCell ) 
            string newfilename = (string)cell.Value;
            FileModel newdatafilelink = _allFiles.Where(x => x.FileName == newfilename).FirstOrDefault();
            FileModel editedfilename = row.DataBoundItem as FileModel;
            editedfilename.Id = newdatafilelink.Id;
            editedfilename.FileName = newdatafilelink.FileName;
            editedfilename.FileTypeId = newdatafilelink.FileTypeId;
            editedfilename.FileType = newdatafilelink.FileType;
            editedfilename.CreatedDate = newdatafilelink.CreatedDate;
            editedfilename.LastUpdate = newdatafilelink.LastUpdate;
            row.Cells["FileTypeColumn"].Value = newdatafilelink.FileTypeName;
            experimentFilesDataGridView.InvalidateRow(e.RowIndex);
            if ( newdatafilelink.Id < 1 ) 
                DialogResult result = MessageBox.Show("Do you want to delete the row?", "Confirm Delete of Blank Row", MessageBoxButtons.YesNo, MessageBoxIcon.Warning);
                if ( result == DialogResult.Yes ) 
                    experimentFilesDataGridView.Rows.RemoveAt(e.RowIndex);
                
            
        
    

正如我所说,除了从新行点击“添加新”按钮的第一种情况外,一切正常。从现有行中点击 Add New 按钮可以正常工作。使用组合框列中的下拉菜单在现有行和新行中都可以正常工作。任何想法我缺少什么或如何使它工作?

请保持温和,因为这是我在 *** 上的第一个问题。

提前致谢, 皮埃尔

【问题讨论】:

对不起,如果我遗漏了什么。但是……我不得不问……你为什么要在网格的每一行上添加一个Add New 按钮?如果每一行上的 Add-New 按钮都做同样的事情,那么一 (1) 个外部按钮不是更有意义吗?我只是说“行”上的按钮意味着该按钮仅适用于该行,除非我遗漏了什么,否则这里似乎并非如此。 另外,如果问题是在添加新的数据行时......那么这段代码在哪里?我猜这个代码可能在NewFileDialogForm 中,没有显示。换句话说,实际上尝试将新数据添加到网格的代码似乎丢失了。这是故意的吗? 【参考方案1】:

首先,由于您的表是数据绑定的,不建议直接通过cell.Value="something";修改其内容。最好修改数据源中的数据,例如_experimentFiles[i].FileName="something"。如果您的数据源正确实施,此更改将立即反映在 UI 中。在某些情况下,直接修改也可能有效,但最好避免这种情况。

其次,对于新的行按钮处理程序。正如您已经发现的那样,这是一个极端情况,因为新行确实是空的并且没有数据源。因此,您应该按照以下方式单独处理这种情况

if(row.DataBoundItem as FileModel is null)

    var fileName = //get filename as you want.
    var newFileModel = new FileModelFileName = fileName;
     _experimentFiles.Add(fileName)

else

    //handle the normal case of existing row as you already doing.

您可以考虑通过检查data 中的null 并按照所述执行操作,将所有这些逻辑移到您的OpenFile 函数中。

【讨论】:

谢谢@Serg,解决了边缘情况。 不过,我也为我的礼仪违规行为道歉;我希望我在 之前在 YouTube 上看过 @IAmTimCorey 的 *** 礼仪视频我问了这个问题!!

以上是关于带有按钮、组合框和文本框的 C# WinForms (.NET Framework) DataGridView:使用按钮添加新行以添加组合框项时出错的主要内容,如果未能解决你的问题,请参考以下文章

MS Access - 带有组合框和文本框的 If 语句

在 c# winforms 中扩展文本框

如何用C#制作一个带有输入框,按钮,输出框的Unity插件

C# - Winforms - Combobox - 避免选择更新数据源的第一项

表单提交后怎么样才能不清空文本框和下拉框的值呢?

是否有带有文本框和 2 个旋钮的默认 Android 视图小部件?