4

[注意更新:]

我希望向 datagridview (DGV) 添加新行的用户能够在 Access 数据库中创建该行,并能够创建多行并对这些新行进行非同步编辑。DGV 由数据集表填充,我发现在 DGV 和数据库之间设置数据集是最佳实践(或至少允许灵活性)。

要对多个用户创建的新行进行编辑,数据集需要能够从数据库中检索自增主键,然后用新行的更新键值重新填充 DGV。或者至少这是我发现完成这项工作的唯一方法。问题是,当我尝试向数据表添加一行时,它最终会生成三行而不是一行。


[为简洁起见,删除了旧帖子]

以下是 DGV 第一次被填充并绑定到数据集的方式:

Dim ConMain As New OleDbConnection("Provider=Microsoft.ACE.OLEDB.12.0;Data Source=" & MasterPath)
Dim ds As New DataSet

Public Sub UniversalFiller(ByVal QueryStringFill As String, ByVal DGV As DataGridView, ByVal AccessDBTableName As String)
    If IsNothing(ds.Tables(AccessDBTableName)) Then
    Else
        ds.Tables(AccessDBTableName).Clear()
    End If
    ConMain.ConnectionString = "Provider=Microsoft.ACE.OLEDB.12.0;Data Source=" & MasterPath
    Dim daZZ As New OleDbDataAdapter(QueryStringFill, ConMain)
    Try
        daZZ.Fill(ds, AccessDBTableName)
        DGV.DataSource = ds
        DGV.DataMember = AccessDBTableName
        DGV.AutoResizeColumns()
    Catch ex As Exception
        WriteToErrorLog(ex.Message, ex.StackTrace, ex.GetHashCode, ex.Source, ex.ToString)
        ds.Clear
        MsgBox("There was an error filling the DGV, ds.cleared") 'this is for my notes, not user error'
    End Try

End Sub

更新:

我仍然无法弄清楚这个问题。这在处理时调用DGV.UserAddedRow我了解到原始代码可能不是仅添加 1 行的最佳方法,但即使制作新行也会遇到同样的问题:

Dim daZZ As New OleDbDataAdapter(DBSelectQuery, ConMain)
Dim CurrentCellPoint As Point = DGV.CurrentCellAddress
Dim CurrentCellText As String = DGV.CurrentCell.Value.ToString
Dim cmdBldr As New OleDbCommandBuilder(daZZ)
cmdBldr.QuotePrefix = "["
cmdBldr.QuoteSuffix = "]"
MsgBox("1")
Dim DaZZDataRow As DataRow = ds.Tables(DTName).NewRow
MsgBox("2")
DaZZDataRow(CurrentCellPoint.X) = CurrentCellText
MsgBox("3")
ds.Tables(DTName).Rows.Add(DaZZDataRow)
MsgBox("4")
daZZ.InsertCommand = cmdBldr.GetInsertCommand()
MsgBox("5")
daZZ.Update(ds.Tables(DTName))
MsgBox("6")
ds.Tables(DTName).Clear()
daZZ.Fill(ds, DTName)
MsgBox("5")
daZZ.Update(ds.Tables(DTName))
DGV.CurrentCell = DGV(CurrentCellPoint.X, CurrentCellPoint.Y)

问题始终是ds.Tables(DTName).Rows.Add()步骤。括号中的内容无关紧要.Add()。它可以是数字或任何包含 0 的整数,它将组成 3 行。它不会被多次调用。只需调用ds.Tables(DTName).Rows.Add().

如果这不是添加一个空白行的正确方法,那是什么?我可以发布 SQL 插入/更新命令,但这不是问题,因为添加是创建三行,而不是 1。要明确的是,它只会遍历MsgBox项目一次并产生 3 行。

我也很好奇, InsertAt 命令产生相同的结果:

ds.Tables(DTName).Rows.InsertAt(DaZZDataRow, CurrentCellPoint.Y) 而不是ds.Tables(DTName).Rows.Add()仍然产生 3 个新行。我也在 Access 的一个新的暂存数据库上尝试过这个并遇到同样的问题。

这是代码的工作示例,作为 VS2012 项目加上虚拟数据库。它非常简单,即没有错误处理,但它显示错误并且还允许删除行以增加便利。感谢任何看的人。

4

2 回答 2

1

这是我对您交易的某些部分的修订版。

    daZZ.InsertCommand = cmdBldr.GetInsertCommand()

我仍然不认为 InsertCommand 是必需的。我认为这是创建您的 3 行之一。

    ds.Tables(DTName).Rows.Add(DaZZDataRow)

这是创建您的第二行。这是有效的,但也不需要。

由于 DataBinding,DataGridView 正在创建第三行。当您将表绑定到 DataGridView 并且允许 DGV“添加行”时,它也会生成插入的行。

尝试完全删除这 2 项,以及清除行(我认为不需要),我敢打赌你会添加一行,即当前添加到 DGV 中的行。

您仍然需要调用 UPDATE 和 FILL 来重新同步网格中的数据。

于 2014-01-16T17:06:03.640 回答
1

我对这个问题采取了回归基础的方法,更多地关注在我添加新记录时让 DataGridView 显示生成的 AutoNumber ID 值的问题。我首先将 DataGridView 控件添加到我的表单并使用“添加项目数据源...”向导将其连接到我的 Access 数据库“Database1.accdb”中名为 [Chemicals] 的表。(问题中示例代码的早期版本引用了一个名为 [Chemicals] 的表,所以这就是我使用的。)

完成后,我的项目中有以下“东西”:

  • 我的表单上的 DataGridView 控件 ( dataGridView1),它绑定到 BindingSource(见下文)

  • database1DataSet与我的 Access 数据库关联的 DataSet ( )

  • 一个 BindingSource ( chemicalsBindingSource),与 中的 [Chemicals] 表关联,database1DataSet用作DataSource我的 DataGridView

  • 一个 TableAdapter ( chemicalsTableAdapter) 来管理 DataSet 和实际数据库之间的数据流。

该向导还在我的Form1_Load方法中为我提供了以下代码行。(我使用 C#,但 VB.NET 会非常相似。)

// TODO: This line of code loads data into the 'database1DataSet.Chemicals' table. You can move, or remove it, as needed.
this.chemicalsTableAdapter.Fill(this.database1DataSet.Chemicals);

这就是我运行我的应用程序并在我的 DataGridView 中查看现有记录所需的全部内容。

现在,为了将 DataGridView 更新推回数据库并检索新创建记录的 AutoNumber 值,我使用了该RowLeave事件。我发现UserAddedRow事件触发得太快了(在用户开始输入新行之后立即),我想等到用户完成编辑该行后再对其进行任何操作。

我最终使用的代码如下(同样,C#)

private void Form1_Load(object sender, EventArgs e)
{
    fillErUp();
}

private void fillErUp()
{
    // (moved from Form1_Load to its own method so it could also be called from _RowLeave, below)
    // TODO: This line of code loads data into the 'database1DataSet.Chemicals' table. You can move, or remove it, as needed.
    this.chemicalsTableAdapter.Fill(this.database1DataSet.Chemicals);
}

private void dataGridView1_RowLeave(object sender, DataGridViewCellEventArgs e)
{
    DataGridView dgv = (DataGridView)sender;
    if (dgv.IsCurrentRowDirty)
    {
        Cursor.Current = Cursors.WaitCursor;
        dgv.EndEdit();  // flush changes from DataGridView to BindingSource
        chemicalsBindingSource.EndEdit();  // flush changes from BindingSource to DataSet
        this.chemicalsTableAdapter.Update(this.database1DataSet);  // update database
        int currentRow = e.RowIndex;
        if (Convert.ToInt32(dgv.Rows[currentRow].Cells[0].Value) < 0)
        {
            // negative AutoNumber value indicates that the row has just been added
            int currentCol = e.ColumnIndex;
            fillErUp();  // re-fill the table in the DataSet, and hence the DataGridView as well
            dgv.CurrentCell = dataGridView1[currentCol, currentRow];
        }
        Cursor.Current = Cursors.Default;
    }
}

这当然不是完整的生产代码,但我希望它有所帮助。

编辑回复:评论

我对以前的解决方案进行了另一次尝试,看看是否可以在不依赖任何 Visual Studio“向导”的情况下完成相同的结果。我这次的做法是:

  • 我打开了一个新的 C# Windows 窗体项目。

  • DataSet我将一个对象、一个BindingSource对象和一个对象拖放DataGridView到空白表单上。它们分别被命名为dataSet1bindingSource1dataGridView1

  • 我创建了Form1_Load事件dataGridView1_RowLeave处理程序,然后添加了我自己的代码,最终看起来像这样:

using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Data.OleDb;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Windows.Forms;

namespace dgvTest2
{
    public partial class Form1 : Form
    {
        OleDbConnection con = new OleDbConnection();
        OleDbDataAdapter da = new OleDbDataAdapter();
        OleDbCommandBuilder cb;

        public Form1()
        {
            InitializeComponent();
        }

        private void Form1_Load(object sender, EventArgs e)
        {
            con.ConnectionString = @"Provider=Microsoft.ACE.OLEDB.12.0;Data Source=C:\Users\Public\Database1.accdb";
            con.Open();
            da.SelectCommand = new OleDbCommand("SELECT * FROM Chemicals", con);
            cb = new OleDbCommandBuilder(da);
            cb.QuotePrefix = "["; cb.QuoteSuffix = "]";
            this.dataSet1.Tables.Add(new DataTable("Chemicals"));
            this.bindingSource1.DataSource = this.dataSet1;
            this.bindingSource1.DataMember = "Chemicals";
            this.dataGridView1.DataSource = this.bindingSource1;
            fillErUp();
        }

        private void fillErUp()
        {
            this.dataSet1.Tables["Chemicals"].Clear();
            da.Fill(this.dataSet1.Tables["Chemicals"]);
        }

        private void dataGridView1_RowLeave(object sender, DataGridViewCellEventArgs e)
        {
            DataGridView dgv = (DataGridView)sender;
            if (dgv.IsCurrentRowDirty)
            {
                Cursor.Current = Cursors.WaitCursor;
                dgv.EndEdit();  // flush changes from DataGridView to BindingSource
                bindingSource1.EndEdit();  // flush changes from BindingSource to DataSet
                da.Update(this.dataSet1.Tables["Chemicals"]);  // update database
                int currentRow = e.RowIndex;
                if (dgv.Rows[currentRow].Cells[0].Value == DBNull.Value)
                {
                    // a null ID value indicates that the row has just been added
                    int currentCol = e.ColumnIndex;
                    fillErUp();  // re-fill the table in the DataSet, and hence the DataGridView as well
                    dgv.CurrentCell = dataGridView1[currentCol, currentRow];
                }
                Cursor.Current = Cursors.Default;
            }
        }

        private void Form1_FormClosed(object sender, FormClosedEventArgs e)
        {
            con.Close();
        }
    }
}
于 2014-01-16T20:05:20.577 回答