我正在创建一个 C# Windows 窗体应用程序来管理发票,我需要一些帮助来确定我是否在朝着正确的方向前进。我需要:
- 在可编辑的网格中显示来自 SQL Server 表的一组发票(发票数量会有所不同)
- 窗口上需要有一个文本框,用户可以在其中输入客户数据,将原始网格结果过滤到与用户输入匹配的行
- 需要在网格顶部有一个额外的复选框,用户可以选中该复选框以更新网格中当前显示的所有行的匹配复选框列。如果输入过滤器,则检查操作应仅适用于显示的行
- 更改不应立即提交到数据库,因为用户可能需要在将更改提交到数据库之前取消选中几行,因此还需要一个操作按钮来提交/保存更改。
我知道我可能需要使用 DataGridView
来实现这一点,但我不确定将它绑定(bind)到什么。这篇文章来自 MSDN looks promising,但我不确定如何从数据库表中填充 BindingList
类。这篇文章调用了一个 Add 方法来添加单独的行,但我希望能够一次添加所有行(或者至少有一个循环,用几行代码将它们全部添加)。
我假设 BindingList
类可以做到这一点。另外,BindingList
类是否支持行过滤,类似于RowFilter
类的DataView
属性? StackOverflow 上还有另一篇文章引用了这个类,但它没有详细说明如何从数据库中填充 BindingList
,如何对其应用过滤器,或者如何实用地更改 DataGridView
中的行。
MSDN article 似乎没有对 DGV 本身进行更改,而是对基础数据源进行了更改,然后通过 INotifyPropertyChanged
接口(interface)更新了 DGV。我知道需要播放什么,我只需要一点帮助来安排这些片段。感谢所有帮助!
我想通了,下面的代码作为其他需要帮助的人的示例。这可能是也可能不是最好的方法,但它对我有用。
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
}
}
最佳答案
我会将您的发票数据加载到数据表中。
您可以使用Binding Source 并将其数据源设置为您的DataTable 的数据源。如果您正在考虑使用 DataGridView,则可以将此对象的数据源设置为绑定(bind)源。
这样做意味着您只需要对内存中的 DataTable 执行筛选,所有更改都会自动波及到您的 DataGridView。
您可以使用 DataView 非常轻松地对 DataTable 执行筛选。
希望这对您有所帮助。
关于c# - 以编程方式更新 DataGridView 中的多行,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/12362191/