Home > OS >  C# WinForms (.NET Framework) DataGridView with Button, Combo Box & Text Box: Errors adding New Row u
C# WinForms (.NET Framework) DataGridView with Button, Combo Box & Text Box: Errors adding New Row u

Time:11-26

I have a Windows Form App (.NET Framework) to display records from a SQL Database. 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. 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.

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. 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.

The first error I ran into was because the DataBoundItem of a New Row is 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:

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:

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:

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

        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. 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.

Thanks in advance, Pierre

CodePudding user response:

Firstly, as your table is databound, it is not recommended to modify it's content directly via cell.Value="something";. It is better to modify data in the datasource, e.g. _experimentFiles[i].FileName="something". If your datasource implemented correctly, this changes will be reflected in UI immediately. 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.

  • Related