简体   繁体   中英

Updating Multiple Rows in DataGridView Programmatically

I'm working on creating a C# windows forms application to manage invoices and I need some assistance to see if I'm heading in the right direction. I need to:

  • Display a group of invoices from a SQL Server table (number of invoices will vary) in an editable grid
  • There needs to be a textbox on the window that the user can enter customer data into that will filter the original grid results down to rows matching the user's entry
  • There needs to be an additional checkbox at the top of the grid that the user can check to update a matching checkbox column on all of the rows currently displayed in the grid. If the filter is entered then the check action should only apply to the displayed rows
  • The changes shouldn't commit immediately to the database as the user may need to uncheck a few rows before committing the changes to the database so there will also need to be an action button to commit/save the changes.

I know that I will probably need to use the DataGridView to achieve this but I'm not sure what to bind it to. This article from MSDN looks promising but I'm not sure how to fill the BindingList class from the database table. The article is calling an Add method to add individual rows but I would want to be able to add all the rows at once (or at least have a loop there to add them all with a couple lines of code).

I assume that is possible with the BindingList class. Also, does the BindingList class support row filtering, similar to the RowFilter property of the DataView class? There's another article on StackOverflow that references this class but it didn't give details on how to fill the BindingList from the database, how to apply a filter to it, or how to pragmatically change rows in the DataGridView .

The MSDN article appears that changes aren't made to the DGV itself but rather the underlying data source and then through the INotifyPropertyChanged interface the DGV is updated. I have some idea of what needs to play out, I just need a little help arranging the pieces. All help is appreciated!

I figured this out and the code is below as an example for anyone else that needs help. This may or may not be the best way to do it, but it worked for me.

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

namespace Insurance_Manager
{
    public partial class InvoiceEditor : Form
    {
        private Boolean ignoreChange = false;
        private Char type;
        private DataTable invoices;
        private SqlDataAdapter adapter;

        #region Class Constructors

        public InvoiceEditor(Char iType)
        {
            // Initialize window components
            InitializeComponent();
            type = iType;
        }

        #endregion

        #region Class Methods

        // This function calculates the grid totals
        private void CalculateTotals()
        {
            // Initialize values
            Double all = 0;
            Double doNotPay = 0;
            Double paid = 0;
            Double wrongAmount = 0;

            // Loop through each row in the displayable grid and total the rows
            foreach (DataRowView viewRow in invoices.DefaultView)
            {
                DataRow row = viewRow.Row;
                all += Double.Parse(row["Amount"].ToString());
                if (Boolean.Parse(row["DoNotPay"].ToString()) == true) { doNotPay += Double.Parse(row["Amount"].ToString()); }
                if (Boolean.Parse(row["Paid"].ToString()) == true) { paid += Double.Parse(row["Amount"].ToString()); }
                if (Boolean.Parse(row["WrongAmount"].ToString()) == true) { wrongAmount += Double.Parse(row["Amount"].ToString()); }
            }

            // Set the totals to the textbox
            tbAllTotal.Text = String.Format(all.ToString(), "{0:c}");
            tbDoNotPayTotal.Text = String.Format(doNotPay.ToString(), "{0:c}");
            tbPaidTotal.Text = String.Format(paid.ToString(), "{0:c}");
            tbWrongAmtTotal.Text = String.Format(wrongAmount.ToString(), "{0:c}");
        }

        // This functions loads the invoices for the grid
        private void LoadInvoices()
        {
            // Fill the data table
            SqlConnection conn = new SqlConnection("Data Source=REPORTSERVER;Initial Catalog=Insurance;Integrated Security=True");
            conn.Open();
            String query = "";
            query += "Select ";
            query += "  IsNull(C.CustomerName,'') CustomerName, ";
            query += "  IsNull(C.[Contract],'') [Contract], ";
            query += "  IsNull(Convert(VarChar(20),I.Receipt),'') Receipt, ";
            query += "  IsNull(I.PolicyNumber,'') PolicyNumber, ";
            query += "  IsNull(I.[Type],'') [Type], ";
            query += "  I.DateBilled, ";
            query += "  I.Amount, ";
            query += "  I.DatePaid, ";
            query += "  I.Paid, ";
            query += "  I.DoNotPay, ";
            query += "  I.WrongAmount, ";
            query += "  I.ID, ";
            query += "  IsNull(I.Notes,'') Notes ";
            query += "From ";
            query += "  Invoice I ";
            query += "  Join Customer C On I.CustomerID = C.ID ";
            switch (type)
            {
                case 'A':
                    query += "Where I.ID Is Not Null And I.CustomerID Is Not Null ";
                    break;
                case 'C':
                    query += "Where I.CustomerID Is Null ";
                    break;
                case 'N':
                    query += "Where I.DoNotPay = 1 And I.CustomerID Is Not Null ";
                    break;
                case 'P':
                    query += "Where I.Paid = 1 And I.CustomerID Is Not Null ";
                    break;
                case 'U':
                    query += "Where I.DoNotPay = 0 And I.Paid = 0 And I.CustomerID Is Not Null ";
                    break;
                case 'W':
                    query += "Where I.WrongAmount = 1 And I.CustomerID Is Not Null ";
                    break;
            }
            query += "Order By ";
            query += "  I.DateBilled ";
            adapter = new SqlDataAdapter(query, conn);
            invoices = new DataTable();
            adapter.Fill(invoices);

            // Link the data table to the grid
            dgvInvoices.DataSource = invoices;
            dgvInvoices.Columns[0].DataPropertyName = "CustomerName";
            dgvInvoices.Columns[0].ReadOnly = true;
            dgvInvoices.Columns[1].DataPropertyName = "Contract";
            dgvInvoices.Columns[1].ReadOnly = true;
            dgvInvoices.Columns[2].DataPropertyName = "Receipt";
            dgvInvoices.Columns[2].ReadOnly = true;
            dgvInvoices.Columns[3].DataPropertyName = "PolicyNumber";
            dgvInvoices.Columns[3].ReadOnly = true;
            dgvInvoices.Columns[4].DataPropertyName = "PolicyNumber";
            dgvInvoices.Columns[4].ReadOnly = true;
            dgvInvoices.Columns[5].DataPropertyName = "DateBilled";
            dgvInvoices.Columns[5].ReadOnly = true;
            dgvInvoices.Columns[6].DataPropertyName = "Amount";
            dgvInvoices.Columns[6].DefaultCellStyle.Format = "c";
            dgvInvoices.Columns[7].DataPropertyName = "DatePaid";
            dgvInvoices.Columns[8].DataPropertyName = "Paid";
            dgvInvoices.Columns[9].DataPropertyName = "DoNotPay";
            dgvInvoices.Columns[10].DataPropertyName = "WrongAmount";
            dgvInvoices.Columns[11].DataPropertyName = "ID";
            dgvInvoices.Columns[11].Visible = false;
            dgvInvoices.Columns[12].DataPropertyName = "Notes";
            dgvInvoices.Columns[12].Visible = false;

            // Close the database connection
            conn.Close();

            // Calculate totals
            CalculateTotals();
        }

        // This function resets all of the fields
        private void ResetFields()
        {
            // Turn on the ignore change flag
            ignoreChange = true;

            // Reset actions
            cbDoNotPay.Checked = false;
            cbPaid.Checked = false;
            cbWrongAmt.Checked = false;

            // Reset filter fields
            dpMinDateBilled.Text = "1/1/1900";
            dpMaxDateBilled.Text = "12/31/2099";
            dpMinDatePaid.Text = "1/1/1900";
            dpMaxDatePaid.Text = "12/31/2099";
            tbContract.Text = "";
            tbName.Text = "";
            tbPolicy.Text = "";
            tbReceipt.Text = "";

            // Turn off the ignore change flag
            ignoreChange = false;
        }

        // This function saves the row to the database
        private void Save(DataRow row)
        {
            // Format values as needed
            Double amount = Double.Parse(row["Amount"].ToString());
            Int32 receipt;
            try
            {
                receipt = Int32.Parse(row["Receipt"].ToString());
            }
            catch
            {
                receipt = 0;
            }

            // Acquire a connection to the database
            SqlConnection conn = new SqlConnection("Data Source=REPORTSERVER;Initial Catalog=Insurance;Integrated Security=True");
            conn.Open();

            // Save the customer to the database
            SqlCommand cmd = new SqlCommand("usp_SaveInvoice", conn);
            cmd.CommandType = CommandType.StoredProcedure;
            cmd.Parameters.Add("@InvoiceID", SqlDbType.Int).Value = row["ID"];
            if (receipt != 0)
            {
                cmd.Parameters.Add("@Receipt", SqlDbType.Int).Value = row["Receipt"];
            }
            else
            {
                cmd.Parameters.Add("@Receipt", SqlDbType.Int).Value = DBNull.Value;
            }
            cmd.Parameters.Add("@Type", SqlDbType.VarChar, 10).Value = row["Type"];
            cmd.Parameters.Add("@Policy", SqlDbType.VarChar, 15).Value = row["PolicyNumber"];
            cmd.Parameters.Add("@DateBilled", SqlDbType.Date).Value = DateTime.Parse(row["DateBilled"].ToString());
            cmd.Parameters.Add("@Amount", SqlDbType.Money).Value = amount;
            if (row["DatePaid"].ToString().Equals(""))
            {
                cmd.Parameters.Add("@DatePaid", SqlDbType.Date).Value = DBNull.Value;
            }
            else
            {
                cmd.Parameters.Add("@DatePaid", SqlDbType.Date).Value = DateTime.Parse(row["DatePaid"].ToString());
            }
            cmd.Parameters.Add("@Paid", SqlDbType.Bit).Value = row["Paid"];
            cmd.Parameters.Add("@DoNotPay", SqlDbType.Bit).Value = row["DoNotPay"];
            cmd.Parameters.Add("@WrongAmount", SqlDbType.Bit).Value = row["WrongAmount"];
            cmd.Parameters.Add("@Notes", SqlDbType.VarChar, 200000000).Value = row["Notes"];
            cmd.Parameters.Add("@UserName", SqlDbType.VarChar, 100).Value = Environment.UserName;
            cmd.ExecuteNonQuery();
            cmd.Dispose();

            // Close the connection to the database
            conn.Close();
            conn.Dispose();
        }

        // This function goes through the rows and saves each one to the database
        private Boolean SaveRows()
        {
            // Initialize the return variable
            Boolean returnVal = true;

            // Change the grid focus to end any edits so they are committed to the datatable
            dgvInvoices.CurrentCell = null;

            // Loop through each row in the data table and validate it
            foreach (DataRow row in invoices.Rows)
            {
                if (returnVal == false)
                {
                    continue;
                }
                else
                {
                    // If the row has changed, validate changes
                    if (row.RowState == DataRowState.Modified)
                    {
                        returnVal = Validate(row);
                    }
                }
            }

            // Loop through each row in the data table and save it
            if (returnVal)
            {
                // Loop through each row in the data table
                foreach (DataRow row in invoices.Rows)
                {
                    // If the row has changed, save changes
                    if (row.RowState == DataRowState.Modified)
                    {
                        Save(row);
                    }
                }
            }

            if (returnVal)
            {
                MessageBox.Show("Database updated!");
                LoadInvoices();
            }

            // Return value
            return returnVal;
        }

        // This function validates the data row to make sure it can be saved to the database
        private Boolean Validate(DataRow row)
        {
            // Validate date received
            DateTime dateBilled;
            try
            {
                dateBilled = DateTime.Parse(row["DateBilled"].ToString());
            }
            catch
            {
                MessageBox.Show("Date received is incorrect");
                return false;
            }
            if (dateBilled < DateTime.Parse("1/1/2010"))
            {
                MessageBox.Show("Date received must be on or after 1/1/2010");
                return false;
            }

            // Validate date paid
            DateTime datePaid;
            if (!row["DatePaid"].ToString().Trim().Equals(""))
            {
                try
                {
                    datePaid = DateTime.Parse(row["DatePaid"].ToString());
                }
                catch
                {
                    MessageBox.Show("Date paid is incorrect");
                    return false;
                }
                if (datePaid < dateBilled)
                {
                    MessageBox.Show("Date paid must be on or after date received");
                    return false;
                }
            }

            // Validate amount
            Double amount;
            try
            {
                amount = Double.Parse(row["Amount"].ToString());
            }
            catch
            {
                MessageBox.Show("Amount is incorrect");
                return false;
            }
            if (amount <= 0)
            {
                MessageBox.Show("Amount must be positive");
                return false;
            }

            // Return true
            return true;
        }

        #endregion

        #region Window Events

        // This function marks the column as checked
        private void CheckColumn(String columnName, Boolean checkState)
        {
            // Turn on the ignore change flag to prevent recalculating totals for each row
            ignoreChange = true;

            // Loop through each row in the displayable grid
            foreach (DataRowView viewRow in invoices.DefaultView)
            {
                DataRow row = viewRow.Row;
                row[columnName] = checkState;
                if ((columnName == "Paid") && (checkState == true))
                {
                    row["DoNotPay"] = false;
                }
                if ((columnName == "DoNotPay") && (checkState == true))
                {
                    row["Paid"] = false;
                }
            }

            // Turn off the ignore change flag
            ignoreChange = false;

            // Recalculate the totals
            CalculateTotals();

            // Refresh the grid
            dgvInvoices.Refresh();
        }

        // This function is used to limit the characters input in a date box
        private void Date_KeyPress(object sender, KeyPressEventArgs e)
        {
            if (("/0123456789".Contains(e.KeyChar)) || ((Int32)e.KeyChar == 8))
            {
                e.Handled = false;
            }
            else
            {
                e.Handled = true;
            }
        }

        // This function is called when filters update
        private void FilterTextChanged(object sender, EventArgs e)
        {
            // Build the filter
            if (!ignoreChange)
            {
                String filter = "";
                filter += "CustomerName Like '%" + tbName.Text.Replace("'", "''") + "%' ";
                filter += "And [Contract] Like '%" + tbContract.Text.Replace("'", "''") + "%' ";
                filter += "And PolicyNumber Like '%" + tbPolicy.Text.Replace("'", "''") + "%' ";
                filter += "And Convert(Receipt,System.String) Like '%" + tbReceipt.Text.Replace("'", "''") + "%' ";
                filter += "And IsNull(DateBilled,'1/1/1900') >= '" + dpMinDateBilled.Text + "' ";
                filter += "And IsNull(DateBilled,'1/1/1900') <= '" + dpMaxDateBilled.Text + "' ";
                filter += "And IsNull(DatePaid,'1/1/1900') >= '" + dpMinDatePaid.Text + "' ";
                filter += "And IsNull(DatePaid,'1/1/1900') <= '" + dpMaxDatePaid.Text + "' ";

                // Apply the filter to the table
                invoices.DefaultView.RowFilter = filter;

                // Calculate totals
                CalculateTotals();
            }
        }

        // This function is called when the form is closing
        private void InvoiceEditor_FormClosing(object sender, FormClosingEventArgs e)
        {
            // By default, assume not closing
            e.Cancel = false;

            // Check if changes have been made
            Boolean changed = false;
            foreach (DataRow row in invoices.Rows)
            {
                if (changed)
                {
                    continue;
                }
                else
                {
                    if (row.RowState == DataRowState.Modified)
                    {
                        changed = true;
                    }
                }
            }

            // If changes were made, ask the user if they want to save changes
            if (changed == true)
            {
                // Ask the customer if they want to save changes
                DialogResult choice = MessageBox.Show("Changes have been made to invoices. Do you want to save them?", "Save Changes", MessageBoxButtons.YesNoCancel);
                if (choice == DialogResult.Yes)
                {
                    e.Cancel = !SaveRows();
                }
                else if (choice == DialogResult.Cancel)
                {
                    e.Cancel = true;
                }
            }
        }

        // This function is called when the form is loading
        private void InvoiceEditor_Load(object sender, EventArgs e)
        {
            // Set the window fields to defaults
            ResetFields();

            // Load invoices
            LoadInvoices();

            // Set the window header
            switch (type)
            {
                case 'A':
                    this.Text = "All Invoices";
                    break;
                case 'C':
                    this.Text = "Invoices w/o Customers";
                    break;
                case 'N':
                    this.Text = "Do Not Pay Invoices";
                    break;
                case 'P':
                    this.Text = "Paid Invoices";
                    break;
                case 'U':
                    this.Text = "Unpaid Invoices";
                    break;
                case 'W':
                    this.Text = "Wrong Amount Invoices";
                    break;
            }
        }

        // This function is called when the Do Not Pay flag is clicked
        private void cbDoNotPay_CheckedChanged(object sender, EventArgs e)
        {
            // If the user checked the box, check all of the rows in the grid
            if (!ignoreChange)
            {
                CheckColumn("DoNotPay", cbDoNotPay.Checked);
            }
        }

        // This functions is called when the Paid flag is clicked
        private void cbPaid_CheckedChanged(object sender, EventArgs e)
        {
            // If the user checked the box, check all of the rows in the grid
            if (!ignoreChange)
            {
                CheckColumn("Paid", cbPaid.Checked);
            }
        }

        // This function is called when the Wrong Amount flag is clicked
        private void cbWrongAmt_CheckedChanged(object sender, EventArgs e)
        {
            // If the user checked the box, check all of the rows in the grid
            if (!ignoreChange)
            {
                CheckColumn("WrongAmount", cbWrongAmt.Checked);
            }
        }

        // This function is called when the cell value changes
        private void dgvInvoices_CellContentClick(object sender, DataGridViewCellEventArgs e)
        {
            // If one of the flag changes, recalculate the totals
            if ((!ignoreChange) && (" 8 9 10 ".Contains(e.ColumnIndex.ToString())))
            {
                // Change focus to end the edit
                dgvInvoices.CurrentCell = dgvInvoices.Rows[e.RowIndex].Cells[0];

                // If the paid flag changes, flip the other one
                if ((e.ColumnIndex == 8) && (Boolean.Parse(dgvInvoices.Rows[e.RowIndex].Cells[8].Value.ToString()) == true))
                {
                    dgvInvoices.Rows[e.RowIndex].Cells[9].Value = false;
                }

                // If the do not pay flag changes, flip the other one
                if ((e.ColumnIndex == 9) && (Boolean.Parse(dgvInvoices.Rows[e.RowIndex].Cells[9].Value.ToString()) == true))
                {
                    dgvInvoices.Rows[e.RowIndex].Cells[8].Value = false;
                }

                dgvInvoices.Refresh();

                CalculateTotals();
            }
        }

        // This function is called when a row in grid is double clicked
        private void dgvInvoices_DoubleClick(object sender, EventArgs e)
        {
            // Get the selected row
            DataRowView row = (DataRowView)dgvInvoices.BindingContext[invoices].Current;
            Int32 InvoiceID;
            try
            {
                InvoiceID = Int32.Parse(row["ID"].ToString().Trim());
            }
            catch
            {
                InvoiceID = 0;
            }

            // Ensure a row was selected
            if (InvoiceID != 0)
            {
                // Display the window for editing this invoice
                Invoice inv = new Invoice(InvoiceID,row["CustomerName"].ToString());
                inv.ShowDialog(this.MdiParent);
            }
        }

        #endregion

        #region Menu Items

        // This function is called when the close button is clicked on the menu
        private void closeToolStripMenuItem_Click(object sender, EventArgs e)
        {
            // Close the form
            this.Close();
        }

        // This function is called when the save button is clicked on the menu
        private void saveToolStripMenuItem_Click(object sender, EventArgs e)
        {
            // Call the save method
            SaveRows();
        }

        // This function is called when the undo button is clicked on the menu
        private void undoToolStripMenuItem_Click(object sender, EventArgs e)
        {
            // Reset filters
            ResetFields();

            // Reload the grid
            LoadInvoices();
        }

        #endregion
    }
}

I would load your Invoices Data into a DataTable.

You can use a Binding Source and set its datasource to that of your DataTable. If you are looking at using a DataGridView you then set the datasource for this object to the binding source.

Doing this will mean you only ever need to perform a filter on the DataTable in memory, and all changes will automatically ripple through to your DataGridView.

You can use DataView's to perform the filters on the DataTable very easily.

Hope this helps.

During databinding event. there is a Event argument that contains the data to be binded... You can set the values in this argument and it will be automatcaly set into dataGridView

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM