C# WinForms (.NET Framework) DataGridView with Button, Combobox and Text Box: 使用按钮添加新行添加组合框项目时出错
C# WinForms (.NET Framework) DataGridView with Button, Combo Box & Text Box: Errors adding New Row using Button to Add Combo Box Item
我有一个 Windows 表单应用程序 (.NET Framework) 来显示来自 SQL 数据库的记录。表单显示单个记录并逐条记录确定。每条记录可能有 none 或更多相关记录(磁盘上与父记录相关的文件列表)(在 M:M 关系中),因此我使用 DataGridView 来显示相关记录。 DGV 有 4 列:一个按钮列打开一个窗体,作为一个对话框将新文件添加到数据库的文件列表,第二个按钮列使用它通常的命令行打开文件,一个组合框列是绑定到所有文件的列表并显示文件名,以及一个显示文件类型的文本框列。
就显示而言一切正常,包括在浏览所有父记录、创建新父记录、删除父记录或更新父记录时。添加新行、删除行,甚至更新行中的选定文件,一切都正常。当尝试使用添加新按钮(第一个按钮列)添加列表中不存在的新文件时,问题就开始了。如果我在现有行上执行此操作,则没有问题,新文件会在下拉列中添加和选择,并且新文件的文件类型会显示在文本框列中。但是,我无法从新行开始这项工作,换句话说,我无法使用添加新按钮在 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 returns 时,它调用父窗体的 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事件,其处理程序负责为Text Box列中的文件类型设置值:
private void ExperimentFilesDataGridView_CellValueChanged(object sender, DataGridViewCellEventArgs e)
{
如果 ( 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);
}
}
}
}
正如我所说,除了第 1 次从新行点击“添加新”按钮的情况外,一切正常。从现有行中点击“添加新”按钮效果很好。使用组合框列中的下拉列表在现有行和新行中都可以正常工作。知道我遗漏了什么或如何让它发挥作用吗?
请保持温和,因为这是我在 Whosebug 上的第一个问题。
提前致谢,
皮埃尔
首先,由于您的 table 是数据绑定的,因此不建议直接通过 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 FileModel{FileName = fileName};
_experimentFiles.Add(fileName)
}
else
{
//handle the normal case of existing row as you already doing.
}
您可以考虑将所有这些逻辑移动到您的 OpenFile
函数中,方法是检查 data
是否 null
并按照描述执行操作。
我有一个 Windows 表单应用程序 (.NET Framework) 来显示来自 SQL 数据库的记录。表单显示单个记录并逐条记录确定。每条记录可能有 none 或更多相关记录(磁盘上与父记录相关的文件列表)(在 M:M 关系中),因此我使用 DataGridView 来显示相关记录。 DGV 有 4 列:一个按钮列打开一个窗体,作为一个对话框将新文件添加到数据库的文件列表,第二个按钮列使用它通常的命令行打开文件,一个组合框列是绑定到所有文件的列表并显示文件名,以及一个显示文件类型的文本框列。
就显示而言一切正常,包括在浏览所有父记录、创建新父记录、删除父记录或更新父记录时。添加新行、删除行,甚至更新行中的选定文件,一切都正常。当尝试使用添加新按钮(第一个按钮列)添加列表中不存在的新文件时,问题就开始了。如果我在现有行上执行此操作,则没有问题,新文件会在下拉列中添加和选择,并且新文件的文件类型会显示在文本框列中。但是,我无法从新行开始这项工作,换句话说,我无法使用添加新按钮在 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 returns 时,它调用父窗体的 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事件,其处理程序负责为Text Box列中的文件类型设置值: private void ExperimentFilesDataGridView_CellValueChanged(object sender, DataGridViewCellEventArgs e) { 如果 ( 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);
}
}
}
}
正如我所说,除了第 1 次从新行点击“添加新”按钮的情况外,一切正常。从现有行中点击“添加新”按钮效果很好。使用组合框列中的下拉列表在现有行和新行中都可以正常工作。知道我遗漏了什么或如何让它发挥作用吗?
请保持温和,因为这是我在 Whosebug 上的第一个问题。
提前致谢, 皮埃尔
首先,由于您的 table 是数据绑定的,因此不建议直接通过 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 FileModel{FileName = fileName};
_experimentFiles.Add(fileName)
}
else
{
//handle the normal case of existing row as you already doing.
}
您可以考虑将所有这些逻辑移动到您的 OpenFile
函数中,方法是检查 data
是否 null
并按照描述执行操作。