WinForms 應用示範:從介面輸入商品資訊(名稱、單價、數量),自動計算金額並顯示於 ListView(Details 模式)。若商品名稱已存在,則**合併數量與金額**而非新增行。支援從 ListView 選取並刪除對應資料庫記錄。
ComboBox comboBox1:商品名稱(可編輯)TextBox textBox1:單價TextBox textBox2:數量Button simpleButton1:新增/合併ListView listView1:View=Details,四欄:商品、單價、數量、金額Button simpleButton2:刪除選取項using System;
using System.Data.SqlClient;
using System.Windows.Forms;
namespace ShopDemo {
public partial class MainForm : Form {
string connStr = "Server=.;Database=ShopDB;Uid=sa;Pwd=123456;";
int selectedRow = -1;
public MainForm() { InitializeComponent(); SetupListView(); }
void SetupListView() {
listView1.View = View.Details;
listView1.FullRowSelect = true;
listView1.GridLines = true;
listView1.Columns.Add("商品", 120);
listView1.Columns.Add("單價", 80);
listView1.Columns.Add("數量", 80);
listView1.Columns.Add("金額", 100);
}
// 新增/合併按鈕
private void simpleButton1_Click(object sender, EventArgs e) {
string goods = comboBox1.Text.Trim();
string priceText = textBox1.Text.Trim();
string qtyText = textBox2.Text.Trim();
if (string.IsNullOrEmpty(goods)) { Msg("商品名稱不能為空"); return; }
if (string.IsNullOrEmpty(priceText)) { Msg("單價不能為空"); return; }
if (string.IsNullOrEmpty(qtyText)) { Msg("數量不能為空"); return; }
if (!decimal.TryParse(priceText, out decimal price) ||
!int.TryParse(qtyText, out int qty)) {
Msg("單價或數量格式錯誤"); return;
}
decimal amount = price * qty;
string[] row = { goods, price.ToString("0.##"), qty.ToString(), amount.ToString("0.##") };
if (TryMergeExisting(row)) return; // 已合併,直接返回
// 新增列
var item = new ListViewItem(row);
listView1.Items.Add(item);
}
// 嘗試合併同名商品
bool TryMergeExisting(string[] row) {
string goods = row[0];
int newQty = int.Parse(row[2]);
decimal newAmt = decimal.Parse(row[3]);
foreach (ListViewItem item in listView1.Items) {
if (item.Text == goods) {
int oldQty = int.Parse(item.SubItems[2].Text);
decimal oldAmt = decimal.Parse(item.SubItems[3].Text);
item.SubItems[2].Text = (oldQty + newQty).ToString();
item.SubItems[3].Text = (oldAmt + newAmt).ToString("0.##");
return true; // 合併完成
}
}
return false;
}
// 刪除按鈕
private void simpleButton2_Click(object sender, EventArgs e) {
if (listView1.SelectedItems.Count == 0) { Msg("請先選取要刪除的商品"); return; }
string goods = listView1.SelectedItems[0].Text;
using (var conn = new SqlConnection(connStr)) {
conn.Open();
// 參數化查詢防注入
string sql = "DELETE FROM Goods WHERE Name = @name";
using (var cmd = new SqlCommand(sql, conn)) {
cmd.Parameters.AddWithValue("@name", goods);
int affected = cmd.ExecuteNonQuery();
Msg(affected > 0 ? "刪除成功" : "資料庫無此商品");
}
}
// 同步移除 ListView 項目
listView1.SelectedItems[0].Remove();
}
void Msg(string s) => MessageBox.Show(s, "提示", MessageBoxButtons.OK, MessageBoxIcon.Information);
}
}
| 項目 | 說明 |
|---|---|
| ListView 合併邏輯 | 遍歷 Items,比對 Text (第 0 欄),相同則累加 SubItems[2] (數量) 與 SubItems[3] (金額)。 |
| 參數化 SQL | 使用 @name 參數,避免字串拼接導致 SQL 注入。 |
| 資料型別轉換 | decimal.TryParse、int.TryParse 安全轉換,失敗時給予友善提示。 |
| 金額格式化 | ToString("0.##") 保留最多兩位小數,避免顯示多餘尾零。 |
| 同步 UI 與 DB | 資料庫刪除成功後,立即 listView1.SelectedItems[0].Remove() 移除視圖項目。 |
CREATE TABLE Goods (
Id INT IDENTITY(1,1) PRIMARY KEY,
Name NVARCHAR(50) NOT NULL,
Price DECIMAL(10,2) NOT NULL,
Qty INT NOT NULL,
Amount AS Price * Qty PERSISTED
);
RowVersion 或樂觀鎖。ListView 不支援虛擬模式,超過數千筆建議改用 DataGridView + BindingSource。NumericUpDown 替代數量輸入,限制最小值 1。try/catch (SqlException),記錄日誌並顯示友善錯誤。完整專案原始碼:GitHub Repository