統一「新增」與「修改」邏輯:
Info 屬性判斷(null=新增,非 null=修改)。| 屬性 | 說明 |
|---|---|
Info | 修改模式時傳入的實體,新增時為 null |
IsEditMode => Info != null | 唯讀屬性判斷模式 |
// 呼叫端
var editForm = new ProductEditForm { Info = selectedProduct }; // 修改
// 或
var editForm = new ProductEditForm(); // 新增
if (editForm.ShowDialog() == DialogResult.OK)
RefreshList();
public abstract class BaseEditForm : Form
{
public BaseModel Info { get; set; }
// 1. 視窗載入:初始化下拉選單、載入修改資料
protected override void OnLoad(EventArgs e)
{
base.OnLoad(e);
InitComboBoxes();
if (IsEditMode) FillForm(Info);
else ClearForm();
RegisterActiveControl(); // 設定各分頁預設焦點
}
// 2. 建立實體集合型別(供反射/泛型)
protected virtual void CreateListInstance(Type type) { }
// 3. 將實體資料填入控制項
protected abstract void FillForm(BaseModel source);
// 4. 清空控制項(新增模式)
protected abstract void ClearForm();
// 5. 從控制項收集資料填入實體
protected abstract void SetInfo(BaseModel target);
// 6. 驗證輸入
protected abstract bool ValidateData();
// 7. 新增實體
protected abstract BaseModel AddInfo();
// 8. 修改實體
protected abstract bool ModifyInfo(BaseModel source);
// 9. 註冊各 TabPage 預設焦點控制項
protected virtual void RegisterActiveControl() { }
// 10. 確定按鈕統一邏輯
private void btnOK_Click(object sender, EventArgs e)
{
if (!ValidateData()) return;
BaseModel result = IsEditMode ? ModifyInfo(Info) : AddInfo();
if (result != null)
{
Info = result; // 回傳給呼叫端
DialogResult = DialogResult.OK;
}
}
}
public class ProductEditForm : BaseEditForm
{
private int _typeId;
private string _unitName;
protected override void OnLoad(EventArgs e)
{
base.OnLoad(e);
// 載入分類樹下拉
cboCategory.LoadTree(typeof(ProductCategory), "Name", "Id", "ParentId");
// 載入單位
cboUnit.DataSource = new UnitDAL().GetAll();
}
protected override void FillForm(BaseModel source)
{
var p = (Product)source;
txtCode.Text = p.Code;
txtName.Text = p.Name;
numPrice.Value = p.Price;
txtSpec.Text = p.Spec;
_typeId = p.CategoryId;
_unitName = p.Unit;
cboCategory.SelectedValue = _typeId;
cboUnit.Text = _unitName;
}
protected override void ClearForm()
{
txtCode.Clear(); txtName.Clear(); txtSpec.Clear();
numPrice.Value = 0;
cboCategory.SelectedIndex = -1;
cboUnit.SelectedIndex = -1;
txtCode.Focus();
}
protected override void SetInfo(BaseModel target)
{
var p = (Product)target;
p.Code = txtCode.Text.Trim();
p.Name = txtName.Text.Trim();
p.Price = numPrice.Value;
p.Spec = txtSpec.Text.Trim();
p.CategoryId = (int)cboCategory.SelectedValue;
p.Unit = cboUnit.Text.Trim();
p.CompanyId = AppContext.CurrentCompanyId;
}
protected override bool ValidateData()
{
if (string.IsNullOrWhiteSpace(txtCode.Text)) { ShowError(txtCode, "請輸入編碼"); return false; }
if (string.IsNullOrWhiteSpace(txtName.Text)) { ShowError(txtName, "請輸入名稱"); return false; }
if (cboCategory.SelectedValue == null) { ShowError(cboCategory, "請選擇分類"); return false; }
// 檢查編碼唯一性
var dal = new ProductDAL();
if (dal.ExistsCode(txtCode.Text, IsEditMode ? ((Product)Info).Id : 0))
{ ShowError(txtCode, "編碼已存在"); return false; }
return true;
}
protected override BaseModel AddInfo()
{
var p = new Product(); SetInfo(p);
return new ProductDAL().Insert(p) ? p : null;
}
protected override bool ModifyInfo(BaseModel source)
{
SetInfo(source);
return new ProductDAL().Update(source);
}
protected override void RegisterActiveControl()
{
// TabPage1 -> txtCode, TabPage2 -> txtSpec
tabControl1.TabPages[0].Enter += (s, e) => txtCode.Focus();
tabControl1.TabPages[1].Enter += (s, e) => txtSpec.Focus();
}
}
protected void ShowError(Control ctrl, string message)
{
errorProvider1.SetError(ctrl, message);
ctrl.Focus();
// 可選:tabControl1.SelectedTab = 找到 ctrl 所在的 TabPage
}
protected override BaseModel AddInfo()
{
var dal = new ProductDAL();
// 1. 檢查是否有同編碼的已刪除記錄
var deleted = dal.GetDeletedByCode(txtCode.Text);
if (deleted != null)
{
var dr = MessageBox.Show($"發現已刪除的同編碼產品「{deleted.Name}」\n是:還原該記錄 否:永久刪除並新建",
"歷史記錄衝突", MessageBoxButtons.YesNoCancel, MessageBoxIcon.Question);
if (dr == DialogResult.Yes)
{
dal.Restore(deleted.Id); // UPDATE IsDelete=0
return deleted;
}
if (dr == DialogResult.No) dal.HardDelete(deleted.Id); // 真刪除
else return null; // Cancel
}
// 2. 正常新增
return base.AddInfo();
}
BaseEditForm 以 Info 屬性區分模式 + 七大虛擬方法契約 + 統一 OK 按鈕流程 封裝新增/修改邏輯。子類專注於:控制項↔實體雙向綁定、業務驗證、DAL 呼叫。配合 ErrorProvider、RegisterActiveControl、軟刪除還原等細節,可構建一致、健壯、使用者友善的編輯體驗。