DataGridView:在DataGridView中处理可编辑数据的最佳方法是什么?

时间:2017-02-06 10:43:38

标签: c# winforms datagridview

处理可编辑DataGridView(DGV)的最佳方法是什么

目标:

我需要实现的是在金额列中输入的金额,平衡列将被更新。 计算用户在DGV中输入的总金额,并将其设置为付款金额文本框。

enter image description here

对象类:

class Invoice
{
    public int id { get; set; }
    public int invoice_id { get; set; }
    public string invoicenumber { get; set; }
    public DateTime date { get; set; }
    public DateTime due_date{ get; set; }
    public decimal total_gross { get; set; }
    public bool is_service { get; set; }
    public int customer_id { get; set; }
    public List<Invoice> invoices = new List<Invoice>();
    // and other properties

    public decimal get_invoice_balance(decimal payment_amount)
    {
        return total_gross - payment_amount;
    }

    public List<Invoice> read(string where_query)
    {
        try
        {
            string query = "SELECT invoice.id as id,invoicenumber,name,date,due_date,account_name,remarks,total_gross,invoice_id FROM invoice ";
            query += "LEFT JOIN customer ON customer.customer_id = invoice.customer_id ";
            query += "LEFT JOIN account ON account.account_id = invoice.ar_account_id "; 

            conn.cmd.Parameters.AddWithValue("@company_id", Variables.Company_ID);

            if (where_query != null && where_query.Length > 0)
            {
                query += where_query;
            }else
            {
                query += "WHERE invoice.company_id = @company_id ";
            }

            query += "ORDER BY invoice_id ";

            conn.OPEN(query);

            while (conn.reader.Read())
            {
                Invoice invoice = new Invoice();
                invoice.id = Int32.Parse(conn.reader["id"].ToString());
                invoice.invoice_id = Int32.Parse(conn.reader["invoice_id"].ToString());
                invoice.invoicenumber = (string)conn.reader["invoicenumber"];
                invoice.customer_name = Global_Functions.object_to_string(conn.reader["name"]);
                invoice.date = Convert.ToDateTime(conn.reader["date"]).Date;
                invoice.due_date = Convert.ToDateTime(conn.reader["due_date"]).Date;
                invoice.ar_account_name = Global_Functions.object_to_string(conn.reader["account_name"]);
                invoice.remarks = Global_Functions.object_to_string(conn.reader["remarks"]);
                invoice.total_gross = (decimal)conn.reader["total_gross"];

                invoices.Add(invoice);
            }

            conn.CLOSE();

            return invoices;

        } catch (Exception err)
        {
            Global_Functions.open_error_dialog(err.Message.ToString());
            return invoices;
        }
    }      

}

填充DataGridView - 我手动在DataGridView中添加了列,并根据类属性命名

List<Invoice> invoices = new Invoice().read(null);

foreach (Invoice invoice_item in invoices)
{
            DataGridViewRow row = (DataGridViewRow)dgv_invoices.Rows[0].Clone();
            row.Cells[0].Value = invoice_item.id;
            row.Cells[1].Value = invoice_item.invoicenumber;
            row.Cells[2].Value = invoice_item.date;
            row.Cells[3].Value = invoice_item.due_date;
            row.Cells[4].Value = invoice_item.total_gross;
            row.Cells[5].Value = invoice_item.get_invoice_balance(Convert.ToDecimal(row.Cells[6].Value));
            row.Cells[6].Value = 0;
            row.Tag = invoice_item;
            dgv_invoices.Rows.Add(row);
}

使用DataSource填充DataGrid - 如果我像这样填充DataGridView,那么很容易将该行转换为发票对象。但问题是我必须手动设置不需要的列不显示,而不是只选择我想要显示的列。任何建议都将不胜感激。

List<Invoice> invoices = new Invoice().read(null);
dgv_invoices.DataSource = invoices;

enter image description here

触发功能

private void dgv_invoices_CellEndEdit(object sender, DataGridViewCellEventArgs e)
{
      if(dgv_invoices.Columns[e.ColumnIndex].Name == "Amount")
      {                       
            // Cast the Datagridrow into an Object Invoice, this works if i populate the datagridview with dgv_invoices.Datasouce = invoices;
            Invoice invoice = (Invoice)dgv_invoices.CurrentRow.DataBoundItem;


            // use the instance to compute, (Note: I need to put the computations in the class so it would be easy to reuse the code and manage it)
            decimal balance = invoice.get_invoice_balance("Amount inputted in Amount Cell")

            // Update the Cell in Column
            dgv_invoices.CurrentRow.Cell = balance;

            // Update Payment Amount based on the list - Pseudo
            tx_payment_amount.Text = function_to_compute_total();
      }
 }

请随时提出问题,批评我的任何代码或评论任何混淆,以便我能纠正并改进我的解释。

1 个答案:

答案 0 :(得分:2)

请注意,在C#中,您为方法和属性编写了CamelCase:GetInvoiceBalance()AccountName。而不是循环,您可以设置属性dataGridView.DataSource。到目前为止,我知道可能的DataSource是DataTables和所有类型的IList实现。所以你可以这样做:

List<Invoice> invoices = new Invoice().Read(null);
dataGridView.DataSource = invoices;

对您的DataSource的更改将反映到网格中。因此,您可以定义Balance-Property的getter,如:

public int Balance {get {return Amount + 10;}}

因此,如果您编辑金额,网格会自动将余额更新为您的金额+ 10.请注意,可能需要dataGridView.Refresh()

您注意到网格会将每个属性填充为列。如果您不想要这个,可以自己添加列。您可以在Designer中或直接在代码中定义它们:

DataGridViewColumn col = new DataGridViewColumn();
col.DataPropertyName = nameof(Invoice.Amount); //This binds the value to your column
col.HeaderText = "Amount";
col.Name = "Amount";
dgViewStudents.Columns.Add(col);

因此,您可以创建所需的列,并且Grid不会为每个属性填充列,这是默认行为。

最后一步是计算总金额。我建议尽可能多地使用DataSource,让视图只是做视图。您的DataSource是您创建的List<Invoice>。因此,将INotifyPropertyChanged添加到您的Invoice-Class将是一个不错的方法。

class Invoice : INotifyPropertyChanged
{
    private int _amount;

    public event PropertyChangedEventHandler PropertyChanged;

    public int Amount 
    {
       get{return _amount;}
       set
       {
          _amount = value;
          PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(nameof(Amount)))
       }
    }
}

您应该为每个物业执行此操作。这个界面有点样板,所以如果你有兴趣更好地使用PostSharp

现在,您可以将List<Invoice>更改为BindingList<Invoice>并将其应用于您的网格。如果更改了某个元素,则BindingList允许您通知。为此注册ListChanged-Event。请注意,您必须实施INotifyPropertyChanged才能收到有关元素更改的通知。否则,您只会收到有关新物品或已移除物品的通知。

 BindingList<Invoice> invoices = new Invoice.Read(null);
 invoices.ListChanged += Invoice_ListChanged;

 private void Test_ListChanged(object sender, ListChangedEventArgs e)
 {
    if (e.ListChangedType == ListChangedType.ItemChanged)
    {
       //Calculate Amount and populate to TextBox (invoices is your DataSource)
       textBoxSum.Text = invoices.Sum(invoice => invoice.Amount);
    }
 }

此外,我建议您使Read();静态。所以你可以这样称呼它:

Invoice.Read();

而不是:

new Invoice.Read();

这是因为我认为一个特定的发票对象与阅读发票无关。我希望你知道我的意思。阅读不是特定于对象的。

我希望这可以帮到你。