【问题标题】:Exception thrown when setting DataGridView property设置 DataGridView 属性时抛出异常
【发布时间】:2021-01-10 12:47:16
【问题描述】:

我有一个显示 EmployeeModel 属性的 DataGridView 表。编译时,抛出此异常: 'ColumnCount property cannot be set on a data-bound DataGridView control.'

我听说在绑定到实际列表之前设置.Datasource = null 会有所帮助,但这样做并没有解决问题。我尝试删除 DataGridView 并重新创建它以防出现错误,但这并没有解决问题。

                //CREATE EMPLOYEE TABLE
                public void CreateEmployeeTable()
                {
                    EmployeeGridView.DataSource = null;
                    EmployeeGridView.DataSource = globalEmployeeList;
                    EmployeeGridView.AutoGenerateColumns = false;
                    EmployeeGridView.ColumnCount = 9;
                    EmployeeGridView.Columns[0].HeaderText = "Employee ID";
                    EmployeeGridView.Columns[0].DataPropertyName = "ID";
                    EmployeeGridView.Columns[1].HeaderText = "First Name";
                    EmployeeGridView.Columns[1].DataPropertyName = "FirstName";
                    EmployeeGridView.Columns[2].HeaderText = "Last Name";
                    EmployeeGridView.Columns[2].DataPropertyName = "LastName";
                    EmployeeGridView.Columns[3].HeaderText = "Nickname";
                    EmployeeGridView.Columns[3].DataPropertyName = "Nickname";
                    EmployeeGridView.Columns[4].HeaderText = "Hire Date";
                    EmployeeGridView.Columns[4].DataPropertyName = "HireDate";
                }

        private void EmployeeGridView_CellClick(object sender, DataGridViewCellEventArgs e)
        {
            DataGridViewCell selectedEmployeeCell = EmployeeGridView.CurrentCell;
            int selectedEmployeeRow = selectedEmployeeCell.RowIndex;
            int selectedEmployeeID = (int)EmployeeGridView.Rows[selectedEmployeeRow].Cells[0].Value;
            jobTitleListBox.DataSource = globalEmployeeList.Where(person => person.ID == selectedEmployeeID).ToList();
        }

这是 SQL(是的,我最终将把它变成一个存储过程;我只是还没有。)

       public async Task<List<EmployeeModel>> GetEmployeeList()
        {
            using (IDbConnection connection = new System.Data.SqlClient.SqlConnection(GlobalConfig.CnnString("WorkDeskDB")))
            {
                var sql = @"SELECT e.id, e.FirstName, e.LastName, e.Nickname, 
                                   em.EmployeeID, em.Address, em.Type, 
                                   e.JobTitleID, jt.id, jt.Name, 
                                   p.EmployeeID, p.Number, p.Type,
                                   ect.EmployeeID, ect.NameID, ect.InitialDate, ect.ExpirationDate,
                                   ct.id, ct.Name
                          
                          FROM dbo.Employees e 
                          LEFT JOIN dbo.Emails em
                          ON em.EmployeeID = e.id
                          LEFT JOIN dbo.JobTitles jt  
                          ON e.JobTitleID = jt.id
                          LEFT JOIN Phones p
                          ON p.EmployeeID = e.id
                          LEFT JOIN dbo.EmployeeCertificationType ect
                          ON ect.EmployeeID = e.id
                          LEFT JOIN dbo.CertificationType ct
                          ON ect.NameID = ct.id"; 

                var employees = await connection.QueryAsync<EmployeeModel, EmailModel, TitleModel, PhoneModel, CertificationModel, EmployeeModel>(sql, (e, em, t, p, c) =>
                {
                    e.EmailList.Add(em);
                    e.JobTitle = t;
                    e.PhoneList.Add(p);
                    e.CertificationList.Add(c);
                    return e;
                }, splitOn: "EmployeeID, JobTitleID, EmployeeID, EmployeeID");

                var result = employees.GroupBy(e => e.ID).Select(g =>
                {
                    var groupedEmployee = g.First();
                    groupedEmployee.EmailList = g.Select(e => e.EmailList.Single()).ToList();
                    groupedEmployee.PhoneList = g.Select(e => e.PhoneList.Single()).ToList();
                    groupedEmployee.CertificationList = g.Select(e => e.CertificationList.Single()).ToList();
                    return groupedEmployee;
                });
                return result.ToList();
            }
        }

【问题讨论】:

  • 你为什么要设置ColumnCount,你应该只是删除数据绑定后不需要的列,或者设置AutoGenerateColumns = false并手动创建每一列
  • 您是否尝试将列表绑定到BindingSource 并将其用作DataGridView.DataSource 的源? The documentation 表示这是解决许多绑定问题的首选方法。
  • 错误很明显...DataGridView.ColumnCount Property ...“异常”... “InvalidOperationException” ...“设置此属性时,已设置DataSource属性。 ” ....我会假设这是错误。注释掉代码并将不想显示的列设置为Visible = false. 或手动添加已注释的列。
  • 虽然您不能直接(向上或向下)设置列数,但您可以在 DGV 中添加(和删除)列!
  • 在我发布的代码中,我确实设置了 .AutoGenerateColumns = false,然后我继续手动添加列。我已删除 .ColumnCount 声明,因此不再收到该错误;但是,在运行时,datagridview 仍在自动生成列。例如,我最终得到两列 Hire Date。

标签: c# winforms datagridview


【解决方案1】:

不,不,您永远不应该摆弄显示数据的单元格。

或者换句话说:将您的数据与其显示方式分开。除了您的代码将更容易阅读之外,它也将更容易更改。例如,如果您想在 ListView 或图表中显示您的数据。

将数据与其显示方式分开还可以更轻松地重复使用、更改数据和测试数据。

获取员工

所以,假设你知道你的 SQL,下面的应该没问题。

(我省略了所有异步等待的东西,这不是你的问题的一部分)。

private IEnumerable<Employee> FetchEmployees()
{
    // TODO: implement, fetch employees from the database
}

好消息是,您隐藏了数据库的结构。因此,如果您决定更改数据库的表,则此方法的用户(= 代码,而不是操作员)将不必更改。如果您决定将数据保存在 XML 文件或字典中,它们甚至不必更改。如果您需要对代码进行单元测试,后者可能会非常方便:您不必为每次测试都填写数据库,使用Employees 的字典就可以了。

显示获取的员工

所以让我们假设您有一种方法可以以某种智能方式获取所有员工。

要显示它,您可以摆弄单元格。但是一个很好的方法是使用DataSourceDatagridView

假设您的员工具有属性 Id 和 Name 以及其他几个属性。您希望逐行显示您的员工,并且希望将属性 Id 和 Name 显示为列:

DataGridViewColumn columnEmployeeId  new DataGridViewColumn();
DataGridViewColumn columnEmployeeName  new DataGridViewColumn();
columnEmployeeId.DataPropertyName = nameof(Employee.Id);    
columnEmployeeName.DataPropertyName = nameof(Employee.Name);

Property DataPropertyName 包含应该显示的属性的名称。

现在您要做的就是获取员工,并将他们分配给 DataGridView 的数据源:

var employees = this.FetchEmployees().ToList();
this.DatagridView1.DataSource = employees;

这足以展示您的员工。操作员对显示数据所做的更改不会在对象员工中更新。如果你想这样,你应该把你的数据放在一个实现IBindingList的对象中,比如BindingList&lt;T&gt;

DataGridView1.DataSource = new BindingList<Employee>(FetchEmployees().ToList());

现在,操作员对显示的员工所做的每项更改都会在 BindingList 中自动更新。即使他添加或删除员工。

BindingList 不依赖于数据的显示方式。因此,如果您决定不再显示列 Id,或者如果您决定按 Name 降序对显示的数据进行排序,这对 BindingList 没有影响。如果你仔细观察,你会发现 BindingList 实际上不是一个 List,而是一个 Collection。没有Employee[3] 的概念。这是意料之中的,因为如果操作员对显示的数据进行排序,则该 Employee 可能不再显示在 Row[3] 中。

适当的表单设计将包含以下方法:

IEnumerable<Employee> FetchEmployees() {... see above}

BindingList<Employee> DisplayedEmployees
{
    get => (BindingList<Employee>)this.dataGridView1.DataSource;
    set => this.dataGridView1.DataSource = value;
}

Employee CurrentEmployee =>
   (Employee) this.datagridView1.SelectedCell?.OwningRow.DataBoundItem;

IEnumerable<Employee> SelectedEmployees =>
    this.DatagridView1.SelectedRows
        .Select(row => row.DataBoundItem)
        .Cast<Employee>();

把它们放在一起:

在表单加载时:显示所有员工:

void Form_Load(object sender, ...)
{
    this.DisplayedEmployees = this.FetchEmployees();
}

ButtonRemoveClicked 后:删除所有选定的员工:

void ButtonRemove_Clicked(object sender, ...)
{
    void employeestoRemove = SelectedEmployees.ToList();
    if (employeesToRemove.Any())
    {
        // TODO: ask operator if remove employees is ok
        this.Repository.RemoveEmployees(employeesToRemove);
    }
}

编辑后,数据源中的所有项目都会更新:

void OnButtonOk_Clicked(object sender, ...)
{
    var displayedEmployees = this.DisplayedEmployees;
    var originalEmployees = this.FetchEmployees;

    // TODO: find out which employees were added / removed / changed
    // and Add / Remove / Update the data
}

结论

由于您将数据库的处理与员工的显示分开,因此程序更小,更易于理解,更易于重用、测试和更改。

您的代码不必担心排序、列重新排序、数据显示方式的更改(生日是显示为月-日-年还是 2021-01-15?),您的 Form 类没有担心这个,如果改变了也不需要大的改变。

【讨论】:

  • 非常感谢您抽出宝贵时间。如何定义新列与 EmployeeGridView 的关系?我可以只创建我需要的那些列,但是什么说它们属于 EmployeeGridView?我实现了您的指示,并且该表自动生成了查询返回的所有列。 public void CreateEmployeeTable() { DataGridViewColumn employeeIDColumn = new DataGridViewColumn(); employeeIDColumn.DataPropertyName = "EmployeeID"; EmployeeGridView.DataSource = globalEmployeeList; } 我的剧情输了吗?
  • 创建一个游乐场表单解决方案。给它一个 DataGridView 和一些列。在 InitializeComponent 中查看创建的代码并查看列如何附加到 DataGridView。我忽略了这一点,因为这个标准代码只会让我的示例与我假设您在提问之前已经调查过的代码混淆
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2011-11-04
  • 2010-10-12
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2017-07-22
相关资源
最近更新 更多