本文演示使用 List<T> 和 Dictionary<K,V> 構建一個完整的員工管理與打卡考勤小系統,涵蓋 UI 設計、數據綁定、增刪改查、右鍵菜單交互、多窗體傳值等 WinForms 核心技術。
// 員工實體
public class Employee {
public int Id { get; set; }
public string Name { get; set; }
public int Age { get; set; }
public string Sex { get; set; }
public int SignState { get; set; } // 0=未簽到, 1=已簽到, 2=已簽退
}
// 打卡記錄
public class SignRecord {
public DateTime SignIn { get; set; }
public DateTime SignOut { get; set; }
public int EmployeeId { get; set; }
public string EmployeeName { get; set; }
}
public partial class MainForm : Form {
public List<Employee> Employees = new List<Employee>();
public static Dictionary<string, SignRecord> SignRecords = new Dictionary<string, SignRecord>();
// Key 為員工姓名,值為當日打卡記錄
private void MainForm_Load(object sender, EventArgs e) {
// 初始化測試數據
Employees.AddRange(new[] {
new Employee { Id=111, Name="王小毛", Age=26, Sex="男" },
new Employee { Id=112, Name="周新雨", Age=22, Sex="女" },
new Employee { Id=113, Name="張燁", Age=30, Sex="男" }
});
dgvEmployees.DataSource = Employees; // DataGridView 自動綁定屬性列
}
}
// 主窗體按鈕
private void btnAdd_Click(object sender, EventArgs e) {
using (var frm = new AddEmployeeForm()) {
if (frm.ShowDialog() == DialogResult.OK) {
Employees.Add(frm.NewEmployee);
RefreshGrid();
}
}
}
// AddEmployeeForm.cs
public Employee NewEmployee { get; private set; }
private void btnSave_Click(object sender, EventArgs e) {
NewEmployee = new Employee {
Id = int.Parse(txtId.Text),
Name = txtName.Text,
Age = int.Parse(txtAge.Text),
Sex = txtSex.Text
};
this.DialogResult = DialogResult.OK;
this.Close();
}
// 主窗體雙擊/右鍵修改
private void btnEdit_Click(object sender, EventArgs e) {
if (dgvEmployees.CurrentRow?.DataBoundItem is Employee emp) {
using (var frm = new EditEmployeeForm(emp)) { // 構造函數傳入實體
frm.ShowDialog();
RefreshGrid(); // 實體已被修改,直接刷新
}
}
}
// EditEmployeeForm.cs
private Employee _emp;
public EditEmployeeForm(Employee emp) { InitializeComponent(); _emp = emp; txtId.Text = emp.Id.ToString(); ... }
private void btnSave_Click(object sender, EventArgs e) {
_emp.Id = int.Parse(txtId.Text);
_emp.Age = int.Parse(txtAge.Text);
_emp.Sex = txtSex.Text;
this.Close();
}
// 為 DataGridView 綁定 ContextMenuStrip (cmsSign)
private void cmsSign_ItemClicked(object sender, ToolStripItemClickedEventArgs e) {
if (dgvEmployees.CurrentRow?.DataBoundItem is Employee emp) {
string action = e.ClickedItem.Text; // "簽到" 或 "簽退"
if (action == "簽到") DoSignIn(emp);
else if (action == "簽退") DoSignOut(emp);
}
}
private void DoSignIn(Employee emp) {
if (emp.SignState != 0) { MessageBox.Show("已簽到,不可重複操作"); return; }
SignRecords[emp.Name] = new SignRecord { EmployeeId=emp.Id, EmployeeName=emp.Name, SignIn=DateTime.Now };
emp.SignState = 1;
MessageBox.Show("簽到成功!");
RefreshGrid();
}
private void DoSignOut(Employee emp) {
if (emp.SignState != 1) { MessageBox.Show("未簽到或已簽退"); return; }
if (SignRecords.TryGetValue(emp.Name, out var rec)) rec.SignOut = DateTime.Now;
emp.SignState = 2;
MessageBox.Show("簽退成功!");
RefreshGrid();
}
private void btnViewRecords_Click(object sender, EventArgs e) {
var list = SignRecords.Values.Select(r => new {
r.EmployeeName, r.SignIn, r.SignOut, Duration = r.SignOut - r.SignIn
}).ToList();
dgvRecords.DataSource = list;
}
DataGridView 極其方便;DataSource = List 即可顯示所有公共屬性列;ItemClicked 統一處理命令。這個小系統雖簡單,卻串聯了 WinForms 業務開發 80% 的高頻場景,是入門項目與快速原型驗證的優秀範本。