ds.Tables.Rows.Add() Membuat 3 Baris Saat Dipanggil Sekali

[Pembaruan Catatan:]

Keinginan saya adalah agar pengguna yang menambahkan baris baru ke datagridview (DGV) akan dapat membuat baris tersebut dibuat dalam database Access, dan dapat membuat beberapa baris dan pengeditan yang tidak sinkron pada baris baru tersebut. DGV diisi oleh tabel kumpulan data, dan menurut saya, memiliki kumpulan data antara DGV dan database adalah praktik terbaik (atau setidaknya memungkinkan fleksibilitas).

Untuk mengedit beberapa baris baru yang dibuat pengguna, kumpulan data harus dapat mengambil kunci utama kenaikan otomatis dari database, dan kemudian mengisi ulang DGV dengan nilai kunci yang diperbarui untuk baris baru. Atau setidaknya itulah satu-satunya cara yang saya temukan untuk mendapatkan pekerjaan ini. Masalahnya adalah ketika saya mencoba menambahkan baris ke datatable, akhirnya menghasilkan tiga baris, bukan satu.


[Postingan lama dihapus agar singkatnya]

Berikut adalah cara DGV pertama kali diisi dan diikat ke kumpulan data:

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

Memperbarui:

Saya masih tidak dapat memecahkan masalah ini. Ini disebut ketika menangani DGV.UserAddedRow. Saya telah mempelajarinya kode asli mungkin bukan cara terbaik untuk hanya Menambahkan 1 baris, tetapi bahkan membuat Baris Baru pun mengalami masalah yang sama:

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)

Masalahnya selalu pada langkah ds.Tables(DTName).Rows.Add(). Tidak masalah apa yang dimasukkan dalam tanda kurung pada .Add(). Bisa berupa angka atau bilangan apa pun termasuk 0 dan akan menghasilkan 3 baris. Itu tidak dipanggil berkali-kali. Hanya menelepon ds.Tables(DTName).Rows.Add().

Jika itu bukan cara yang tepat untuk menambahkan satu baris kosong, lalu apa? Saya dapat memposting perintah penyisipan/perbarui SQL, tetapi itu bukan masalah karena penambahan membuat tiga baris, bukan 1. Untuk lebih jelasnya, ini hanya akan dijalankan melalui item MsgBox satu kali dan menghasilkan 3 baris.

Juga membuat saya penasaran, perintah InsertAt memberikan hasil yang sama:

ds.Tables(DTName).Rows.InsertAt(DaZZDataRow, CurrentCellPoint.Y) bukannya ds.Tables(DTName).Rows.Add() masih menghasilkan 3 baris baru. Saya juga sudah mencoba ini pada DB awal baru dari Access dan mendapatkan masalah yang sama.

Berikut adalah contoh kode yang berfungsi, sebagai Proyek VS2012 plus tiruan basis data. Ini cukup sederhana, yaitu tidak ada penanganan kesalahan, tetapi ini menunjukkan kesalahan dan juga memungkinkan penghapusan baris sebagai tambahan kenyamanan. Terima kasih kepada siapa pun yang melihatnya.


person Atl LED    schedule 14.10.2013    source sumber
comment
Mmm yakin tidak ada 3 kali klik tombol? Pemicu di atas meja?   -  person Yosi Dahari    schedule 15.10.2013
comment
@Yosi Yap, ini terpicu ketika Anda mencoba menulis di sel baris baru.   -  person Atl LED    schedule 15.10.2013
comment
Saya pikir yang Yosi tanyakan di sini adalah apakah fungsinya dipanggil tiga kali karena alasan tertentu? Anda dapat mengonfirmasi hal ini dengan mudah dengan memasukkan Msgbox (Fungsi ini baru saja dipanggil) ke dalam kode dan melihat apa yang terjadi ketika Anda mengklik tombol tersebut sekali. Jika ya, berarti ada yang salah dengan antarmukanya; jika tidak, berarti ada yang tidak beres dengan kode pembaruan data. Langkah pertama Anda adalah mempersempit asal mula perilaku tak terduga tersebut.   -  person nucleon    schedule 11.12.2013
comment
@nucleon Terima kasih atas sarannya, dan saya mencobanya. Itu hanya muncul dengan satu kotak pesan. Saya masih sangat bingung dengan hal ini. Pekerjaan saya adalah segera menghapus dua baris, tapi itu kasar.   -  person Atl LED    schedule 06.01.2014
comment
@nucleon Saya baru saja memposting kode yang diperbarui, menurut saya berdasarkan apa yang Anda dan Yosi sarankan. Faktanya tidak dipanggil 3 kali. Ada pemikiran lain atau pemecahan masalah?   -  person Atl LED    schedule 15.01.2014
comment
@Yosi Saya tidak menambahkan pemicu apa pun ke tabel, database adalah database Access, jadi bisakah ia mendapatkan pemicu secara otomatis dari sana?   -  person Atl LED    schedule 15.01.2014
comment
dapatkah Anda meletakkan file zip di suatu tempat dengan contoh yang berfungsi, kode, dan akses db?   -  person Fredou    schedule 16.01.2014
comment
@Fredou apakah Solusi VS2012+akses db cocok untuk Anda? Saya akan membuat versi tiruan cepat   -  person Atl LED    schedule 16.01.2014
comment
@Fredou Baru saja menambahkan proyek sampel; lihat tautan di bagian bawah pertanyaan. Maaf atas keterlambatannya, saya menyelesaikannya segera setelah permintaan Anda, namun kemudian ditarik dari komputer selama sisa giliran kerja saya.   -  person Atl LED    schedule 16.01.2014
comment
Sayangnya saya tidak dapat membuka proyek Anda karena saya masih menggunakan VS2010. Namun, selain menambahkan baris dalam masalah rangkap tiga, apakah tujuan lainnya adalah ketika Anda menambahkan baris baru ke DataGridView, baris tersebut akan diperbarui dengan ID AutoNumber dari catatan yang baru dimasukkan? (Jika ya, maka saya mungkin dapat membantu Anda.)   -  person Gord Thompson    schedule 16.01.2014
comment
@GordThompson Itu adalah tujuan akhir saya yang sebenarnya, dan menambahkan baris rangkap tiga adalah kesalahan yang saya temui selama ini. Jika saya bisa mengatasi kesalahan itu, saya ingin berada di jalan yang benar.   -  person Atl LED    schedule 16.01.2014


Jawaban (2)


Saya mengambil pendekatan kembali ke dasar untuk pertanyaan ini, lebih berkonsentrasi pada masalah membuat DataGridView menunjukkan nilai ID AutoNumber yang dihasilkan ketika saya menambahkan catatan baru. Saya mulai dengan menambahkan kontrol DataGridView ke formulir saya dan menggunakan wizard "Tambahkan Sumber Data Proyek..." untuk menghubungkannya ke tabel bernama [Bahan Kimia] di database Access saya "Database1.accdb". (Versi sebelumnya dari contoh kode dalam pertanyaan mengacu pada tabel bernama [Bahan Kimia], jadi itulah yang saya gunakan.)

Ketika saya selesai, saya memiliki "barang" berikut di proyek saya:

  • kontrol DataGridView (dataGridView1) di formulir saya, yang terikat ke BindingSource (lihat di bawah)

  • DataSet (database1DataSet) yang terkait dengan database Access saya

  • a BindingSource (chemicalsBindingSource), terkait dengan tabel [Bahan Kimia] di database1DataSet, untuk dijadikan DataSource untuk DataGridView saya

  • TableAdapter (chemicalsTableAdapter) untuk mengelola aliran data antara DataSet dan database sebenarnya.

Wizard juga memberi saya baris kode berikut dalam metode Form1_Load saya. (Saya menggunakan C#, tetapi VB.NET akan sangat mirip.)

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

Hanya itu yang saya perlukan untuk menjalankan aplikasi saya dan melihat catatan yang ada di DataGridView saya.

Sekarang, untuk mendorong pembaruan DataGridView kembali ke database dan mengambil nilai AutoNumber untuk catatan yang baru dibuat, saya menggunakan acara RowLeave. Saya menemukan bahwa acara UserAddedRow diaktifkan terlalu cepat (segera setelah pengguna mulai mengetik di baris baru), dan saya ingin menunggu hingga pengguna selesai mengedit baris sebelum melakukan apa pun dengannya.

Kode yang akhirnya saya gunakan adalah sebagai berikut (sekali lagi, 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;
    }
}

Ini tentu saja bukan kode produksi lengkap tapi saya harap ini membantu.

Sunting ulang: komentar

Saya mencoba lagi solusi saya sebelumnya untuk melihat apakah saya dapat mencapai hasil yang sama tanpa bergantung pada "keajaiban" Visual Studio. Pendekatan saya kali ini adalah:

  • Saya membuka proyek C# Windows Forms baru.

  • Saya menarik dan melepas objek DataSet, objek BindingSource, dan objek DataGridView ke formulir kosong. Mereka masing-masing diberi nama dataSet1, bindingSource1, dan dataGridView1.

  • Saya membuat event handler Form1_Load dan dataGridView1_RowLeave, lalu menambahkan kode saya sendiri yang akhirnya terlihat seperti ini:

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();
        }
    }
}
person Gord Thompson    schedule 16.01.2014
comment
Saya akan bermain-main dengan ini sebentar untuk melihat apakah saya bisa membuatnya berfungsi. Dalam solusi saya yang sebenarnya, saya memiliki banyak database dan sejumlah tabel dinamis. Saya berharap untuk tidak harus bergantung pada wizard karena hal ini, tetapi jika saya dapat menyelesaikan masalah dengan benar untuk satu contoh, semoga akan lebih mudah untuk memperluasnya. - person Atl LED; 17.01.2014
comment
@AtlLED Saya dapat memposting tautan ke salinan proyek C # jika Anda merasa terbantu, misalnya, untuk melihat apa yang telah dilakukan pengkabelan wizard di latar belakang. - person Gord Thompson; 17.01.2014
comment
Itu akan sangat membantu, terima kasih. Sebenarnya kode asli saya hampir berfungsi dengan mematikan dan menghidupkan DGV.AllowUserToAddRows, tetapi saya tidak dapat mengambil karakter awal yang diketik ke dalam sel baris baru karena nilainya terbaca sebagai DBNull. Itu hanya karakter pertama, jadi saya mencoba mengambilnya sekarang. - person Atl LED; 17.01.2014
comment
@AtlLED Saya telah memperbarui jawaban saya. Anda dapat mengunduh salinan proyek C# baru yang bebas wizard di sini. - person Gord Thompson; 17.01.2014
comment
Wow itu jawaban yang luar biasa. Beri saya waktu satu hari untuk benar-benar membahasnya dan menerapkannya, tetapi saya yakin ini akan menjadi jawaban yang diterima. Agar saya mengerti, Anda akan tetap membuka koneksi sepanjang formulir dibuka? - person Atl LED; 17.01.2014
comment
@AtlLED Untuk koneksi ke database Access, saya tidak percaya bahwa menjaga koneksi tetap terbuka terlalu menyakitkan. (Tidak seperti lingkungan basis data klien/server multi-pengguna di mana koneksi aktif adalah komoditas yang lebih berharga.) Jika Anda akan memiliki beberapa pengguna secara bersamaan, maka menambahkan pernyataan Close() dan Open() akan berdampak pada membuang perubahan ke pengguna lain. (Hal ini akan terjadi secara otomatis, namun terkadang dibutuhkan beberapa detik agar perubahan pada satu koneksi terlihat pada koneksi lainnya.) - person Gord Thompson; 17.01.2014
comment
Maaf untuk kembali lagi ke sini, tapi bagaimana Anda bisa mengikat DGV tertentu dengan benar ke bs.DataMember tertentu? Saya mencoba this.dataGridView1.DataSource = this.bindingSource1.Current() pada gagasan yang hanya mengatur anggota data, tetapi tidak berhasil. Saya pikir ini adalah pukulan terakhir saya dalam hal ini. - person Atl LED; 22.01.2014
comment
@AtlLED Yang saya lakukan hanyalah this.dataGridView1.DataSource = this.bindingSource1 - apakah itu tidak berhasil untuk Anda? - person Gord Thompson; 22.01.2014

Ini adalah pendapat saya yang Revisi mengenai beberapa bagian transaksi Anda.

    daZZ.InsertCommand = cmdBldr.GetInsertCommand()

Saya masih merasa InsertCommand tidak diperlukan. Saya pikir ini membuat salah satu dari 3 baris Anda.

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

Ini membuat baris kedua Anda. Itu valid, tapi juga tidak diperlukan.

Baris KETIGA sedang dibuat oleh DataGridView karena DataBinding. Saat Anda Mengikat Tabel ke DataGridView dan DGV diizinkan untuk "AddRows", itu juga menghasilkan Baris yang Disisipkan.

Coba hapus 2 item ini secara total, serta garis Clear (menurut saya tidak diperlukan) dan saya yakin Anda akan mendapatkan satu baris tambahan yaitu baris yang saat ini sedang ditambahkan di DGV.

Anda masih harus memanggil UPDATE dan FILL untuk menyinkronkan ulang data di grid.

person DarrenMB    schedule 16.01.2014
comment
Sebenarnya saya rasa saya memerlukannya untuk menulis kembali ke database. Jika saya hanya memperbarui tabel data, itu tidak akan menghasilkan ID dari Database. Saya menghapusnya dan mengisinya kembali dengan kunci utama dari DB agar tidak mengalami masalah konkurensi. Namun, bahkan jika kita mengabaikan DB secara bersamaan, dan mengomentari bagian ini, baris `ds.tables(DTname).Rows.Add() membuat 3 baris di DGV (dengan sendirinya). Saya pikir itulah masalah sebenarnya. - person Atl LED; 16.01.2014
comment
Jawaban yang Direvisi. Saya pikir ini adalah masalah Anda sekarang. - person DarrenMB; 16.01.2014
comment
Saya rasa tidak, saya akan memposting kode revisi sesuai pemahaman saya di pertanyaan setelah saya menghadiri seminar. Dari contoh, mengomentari baris 86-88, 94, 98, dan 102 serta melarang baris tambahan DGV membawa kita ke 2 baris yang sedang dibuat, tetapi juga tabel menjadi dua kali lipat dan ID tidak diambil yang menyebabkan kesalahan konkurensi . Saya akan memainkan ide ini lagi dan memperbaruinya. - person Atl LED; 16.01.2014
comment
Sebenarnya, menurut saya menonaktifkan DGV untuk dapat menambahkan baris sambil membiarkan yang lainnya tetap di tempatnya mungkin bisa menjadi solusinya. - person Atl LED; 16.01.2014
comment
Semoga ini membantu Anda, saya biasanya menghindari penyatuan data seperti wabah karena ini adalah kotak hitam besar yang sulit ditangani. - person DarrenMB; 17.01.2014
comment
Saya ingin mengatakan bahwa jika saya memiliki sisa tenaga, saya juga akan mencoba memberi Anda hadiah. 3 baris tersebut sebenarnya disebabkan oleh Ditjen Pajak yang mampu membuat baris baru. Mematikannya di skrip asli membuatnya berfungsi, meskipun Anda kehilangan karakter pertama yang diketik di baris (yang tidak dapat saya tangkap). Meskipun demikian, skrip lainnya sepertinya diperlukan agar dapat berfungsi dengan baik. - person Atl LED; 21.01.2014
comment
Senang Anda menyelesaikannya. jangan khawatir tentang hal itu. jumlahnya tidak masalah. Saya pikir banyak dari kita menggunakan situs ini untuk hal yang sama, tempat lain untuk melontarkan ide ketika terjebak pada sesuatu. - person DarrenMB; 21.01.2014