简体   繁体   English

C# WinForms (.NET Framework) DataGridView with Button, Combo Box & Text Box: 使用按钮添加新行以添加组合框项时出错

[英]C# WinForms (.NET Framework) DataGridView with Button, Combo Box & Text Box: Errors adding New Row using Button to Add Combo Box Item

I have a Windows Form App (.NET Framework) to display records from a SQL Database.我有一个 Windows 表单应用程序(.NET Framework)来显示来自 SQL 数据库的记录。 The form displays a single record and goes through record by record ok.表单显示单个记录并通过记录确定。 Each record may have none or more related records (a list of files on disk related to the parent record) (in a M:M relationship), so I am using a DataGridView to display the related records.每条记录可能没有或有更多相关记录(磁盘上与父记录相关的文件列表)(在 M:M 关系中),所以我使用 DataGridView 来显示相关记录。 The DGV has 4 columns: a Button Column that opens a form that acts as a dialog to add a new file to the database's list of files, a second Button Column that opens the file using its usual command line, a Combo Box column that is bound to the list of all files & displays the file name, and a Text Box column that displays the file type. DGV 有 4 列:一个按钮列,它打开一个表单,作为一个对话框,将新文件添加到数据库的文件列表中,第二个按钮列使用其常用命令行打开文件,一个组合框列绑定到所有文件列表并显示文件名,以及显示文件类型的文本框列。

Everything works as far as displaying, including when navigating through all the parent records, creating a new parent record, deleting a parent record, or updating a parent record.就显示而言,一切都有效,包括在浏览所有父记录、创建新父记录、删除父记录或更新父记录时。 All also works ok to add a new row, remove a row, and even update the selected file on a row.所有也可以添加新行,删除行,甚至更新一行上的选定文件。 The problems start when trying to use the Add New button (the first Button column) to add a new file that is not already in the list.当尝试使用 Add New 按钮(第一个 Button 列)添加一个不在列表中的新文件时,问题就开始了。 If I do this on an existing row, there is no issue, the new file gets added and selected in the drop-down column, and the file type of the new file gets display in the text box column.如果我在现有行上执行此操作,则没有问题,新文件被添加并在下拉列中选择,新文件的文件类型显示在文本框列中。 However, I have not been able to make this work from a new row, in other words, I can't use the Add New Button to add a new file in a new blank row of the DGV.但是,我无法从新行完成这项工作,换句话说,我无法使用添加新按钮在 DGV 的新空白行中添加新文件。

The first error I ran into was because the DataBoundItem of a New Row is null.我遇到的第一个错误是因为新行的 DataBoundItem 是 null。 And since that is a read-only property, I can't set it.由于这是一个只读属性,我无法设置它。 I tried using the process to programmatically add a new row described in several articles here, but got an error that I cannot programmatically add a row that is data bound.我尝试使用该过程以编程方式添加此处几篇文章中描述的新行,但出现错误,我无法以编程方式添加数据绑定的行。 At this point, I am really not sure what I am missing.在这一点上,我真的不确定我错过了什么。

Here are the relevant portions of the code.以下是代码的相关部分。 On selecting a parent record (or creating a new one), the BindExperimentFilesDataGridView method gets called:在选择父记录(或创建新记录)时,调用 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);
    }

Clicking on the Add New Button is handled via the CellClick Event:单击添加新按钮是通过 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);
        }
    }

When the NewFileDialogForm returns, it calls the parent form's SelectFiles method:当 NewFileDialogForm 返回时,它调用父窗体的 SelectFiles 方法:

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

The change of cell.Value in the FileNameColumn (which is the ComboBoxColumn) triggers the Cell Value Changed event, and its handler is responsible for setting the value for the file type in the Text Box column: private void ExperimentFilesDataGridView_CellValueChanged(object sender, DataGridViewCellEventArgs e) { if ( e.RowIndex < 0 || e.ColumnIndex < 0 ) { return; FileNameColumn(即ComboBoxColumn)中cell.Value的变化触发了Cell Value Changed事件,其handler负责设置Text Box列中文件类型的值:private void ExperimentFilesDataGridView_CellValueChanged(object sender, DataGridViewCellEventArgs e ) { if ( e.RowIndex < 0 || e.ColumnIndex < 0 ) { return; } }

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

As I said, everything works ok except in the 1 case of hitting the Add New button from a new row.正如我所说,除了从新行中点击“添加新”按钮的第一种情况外,一切正常。 Hitting the Add New button from an existing row works fine.从现有行中点击 Add New 按钮可以正常工作。 Using the drop-down in the combo box column works fine in both existing rows and new rows.使用组合框列中的下拉菜单在现有行和新行中都可以正常工作。 Any ideas what I'm missing or how to get this to work?任何想法我错过了什么或如何让它工作?

Please be gentle, as this is my first question on StackOverflow.请温柔一点,因为这是我在 StackOverflow 上的第一个问题。

Thanks in advance, Pierre在此先感谢,皮埃尔

Firstly, as your table is databound, it is not recommended to modify it's content directly via cell.Value="something";首先,由于您的表格是数据绑定的,因此不建议直接通过cell.Value="something"; . . It is better to modify data in the datasource, eg _experimentFiles[i].FileName="something" .最好修改数据源中的数据,例如_experimentFiles[i].FileName="something" If your datasource implemented correctly, this changes will be reflected in UI immediately.如果您的数据源正确实施,此更改将立即反映在 UI 中。 Direct modifications may also work under the some circumstances, but better to avoid this.在某些情况下,直接修改也可能有效,但最好避免这种情况。

Second, for the new row button handler.其次,对于新的行按钮处理程序。 As you already found, this is a corner case because the new row is really empty and do not have datasource.正如您已经发现的那样,这是一个极端情况,因为新行确实是空的并且没有数据源。 So, you should handle this case separately as following因此,您应该按以下方式单独处理这种情况

if(row.DataBoundItem as FileModel is null)
{
    var fileName = //get filename as you want.
    var newFileModel = new FileModel{FileName = fileName};
     _experimentFiles.Add(fileName)
}
else
{
    //handle the normal case of existing row as you already doing.
}

You can consider to move all this logic into your OpenFile function by checking data for null and performing manipulations as described.您可以考虑通过检查nulldata并按照所述执行操作,将所有这些逻辑移动到OpenFile function 中。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM