处理可编辑DataGridView(DGV)的最佳方法是什么
目标:
我需要实现的是在金额列中输入的金额,平衡列将被更新。 计算用户在DGV中输入的总金额,并将其设置为付款金额文本框。
对象类:
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;
触发功能
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();
}
}
请随时提出问题,批评我的任何代码或评论任何混淆,以便我能纠正并改进我的解释。
答案 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();
这是因为我认为一个特定的发票对象与阅读发票无关。我希望你知道我的意思。阅读不是特定于对象的。
我希望这可以帮到你。