0
votes

I am trying to edit a row from the datagridview. Every row has a button. When I press one of the rows button, a second form opens and show me the information in textboxes about that row and I need to edit what I want.

The problem is that I already wrote the code for editing but I can't add the DataGridViewCellEventArgs in the button function, or I can't use RowIndex to edit a specific row.

Here is the code:

public void btnUpdate_Click(object sender, EventArgs e)
{
    SqlConnection conn = new SqlConnection(@"Data Source=DESKTOP-VUPD668;Initial Catalog=dbApp;Integrated Security=True");
    SqlCommand cmd;
    cmd = new SqlCommand("UPDATE tableApplication SET Name='" + txtName.Text + "',Package='" + txtPackage.Text + "',Hour='" + txtHour.Text + "',Date='" + txtDate.Text + "',Phone='" + txtPhone.Text + "',Observations='" + txtObservations.Text + "' WHERE ID=" + f1.dgvContactList.Rows[rowIndex].Cells[0].Value.ToString(), conn);
    conn.Open();

    cmd.ExecuteNonQuery();

    conn.Close();
    MessageBox.Show("Edit was saved");
    this.Close();
}

and here is the code from the main form with the dgv

public void dgvContactList_CellContentClick(object sender, DataGridViewCellEventArgs e)
{       
    if (e.ColumnIndex == 7)
    {            
        formAddEditContact f2 = new formAddEditContact();
        int rowIndex = e.RowIndex;
        formContact f1 = new formContact();
        f2.lblTitle.Text = "Edit";
        f2.btnSave.Visible = false;
        f2.btnUpdate.Visible = true;
        
        f2.btnDelete.Visible = true;
        f2.txtName.Text = f1.dgvContactList.Rows[rowIndex].Cells[1].Value.ToString();
        f2.txtPackage.Text = f1.dgvContactList.Rows[rowIndex].Cells[2].Value.ToString();
        f2.txtHour.Text = f1.dgvContactList.Rows[rowIndex].Cells[3].Value.ToString();
        f2.txtDate.Text = f1.dgvContactList.Rows[rowIndex].Cells[4].Value.ToString();
        f2.txtPhone.Text = f1.dgvContactList.Rows[rowIndex].Cells[5].Value.ToString();
        f2.txtObservations.Text = f1.dgvContactList.Rows[rowIndex].Cells[6].Value.ToString();
        f2.ShowDialog();

How I can use RowIndex in the button function. How I can add DataGridViewCellEventArgs.

1
Why are you “creating” a new formContact f1? The form is never shown, so the code … f1.dgvContactList.Rows[rowIndex].Cells[1].Value.ToString(); is not going to have any rows since the grid has not been displayed. Have you tried… dgvContactList.Rows[rowIndex].Cells[1].Value.ToString();?JohnG
Where are you adding the query results to the DGV? You are executing the query with cmd.ExecuteNonQuery(); but never taking the results of the cmd and reading the results.jdweng
The following article may be of interest, see the screenshot. Full source. I stubbed out for saving back to the database.Karen Payne

1 Answers

0
votes

It is seldom a good idea to access the displayed data in the DataGridView directly. You should separate the way that data is displayed from the actual values. This way you can easily change the display, without having to change the code that uses the data.

Apparently your DataGridView shows several properties of a sequence of Contacts. Every Row shows one Contact:

class Contact
{
    public int Id {get; set;}
    public string Name {get; set;}
    public DateTime BirthDay {get; set;}
    ...
}

You have added DataGridViewColumns to your DataGridView. Every DataGridViewColumn shows one property. The name of the property that should be shown is in DataGridViewColumn.DataPropertyName

DataGridViewColumn columnBirthDay = new DataGridViewColumn();
columnBirthDay.DataPropertyName = nameof(Contact.BirthDay);
... // set other properties.

Now all you have to do is, get your data and put them in the DataSource of the DataGridView:

IEnumerable<Contact> contactsToDisplay = ...
this.DataGridViewContacts.DataSource = new BindingList<Contact>(contactsToDisplay);

Now every change that the operator edits, is automatically updated in the DataSource. Every change that your program makes to the data source is automatically displayed.

Programmatically add a contact:

BindingList<Contact> DisplayedContacts => (BindingList<Contact>)this.DataGridViewContacts.DataSource;

private void DisplayContact(Contact contact)
{
    this.DisplayedContacts.Add(contact);
}

Access edited contacts, for instance after pressing a button:

private void OnButtonOkClicked(object sender, ...)
{
     Collection<Contact> editedContacts = this.DisplayedContacts;
     this.ProcessEditedContacts(editedContacts);   
}

Every row in your DatagridView has a button. If the operator pressed the button you want to use the Contact that is displayed in that row to do something.

private Contact GetContact(DataGridViewRow row)
{
    return (Contact)row.DataBoundItem;
}

private void dgvContactList_CellContentClick(object sender, DataGridViewCellEventArgs e)
{
    // in baby steps:
    DataGridView dataGridView = (DataGridView)sender;
    DataGridViewRow row = dataGridView.Rows[e.RowIndex];
    Contact contact = GetContact(row);
    EditContact(contact);
}

You can do this in one big statement. Not sure if this improves readability though.

private void EditContact(Contact contact)
{
    using (var dlg = new EditContactDlg())
    {
        dlg.Contact = contact;
        ... // other dialog properties

        var dialogResult = dlg.Show(this);
        if (dlgResult == DialogResult.OK)
        {
            ... // process edited contact
        }
    }
}

Be careful: you attach the original unedited contact to the dialog box. If the operator changes values and presses Cancel, the original contact might still be changed. Better is to attach a Cloned contact to the dialog, and if needed use the properties of the edited cloned contact to update the original contact.

Did you see, that because I didn't do everything in one big procedure, the procedures are much easier to understand. They are much easier to be changed slightly, and they can also be reused easily.

For instance, if you decide not to add a column with buttons, because you don't want to display 50 buttons in your form, you decide to add one button to edit the "currently selected row", code changes will be minimal:

private void OnButtonEditCurrentRow_Clicked(object sender, ...)
{
    DataGridViewRow row = this.DataGridViewContacts.CurrentRow;
    Contact contact = GetContact(row);
    EditContact(contact);
}

This procedure can of course also be reused if you want to add a menu item to edit the current row.

Because you separated the way that Contacts are displayed in the datagridview from the actual contacts, code changes will be small if you decide to display the Contact differently, for instance if you decide not to display the Id of the contact anymore, or if you plan to use a Japanese method of displaying the birthday. Or if you implement ordering of the data. The BindingList always contains the internal contact data.

Similarly, if you want to change your contact. For example separate the firstname and the lastName, but still want to display them in one column, code changes will be very small.