首頁
編程語言
C語言|JAVA編程
Python編程
網頁編程
ASP編程|PHP編程
JSP編程
數據庫
MYSQL數據庫|SqlServer數據庫
Oracle數據庫|DB2數據庫
您现在的位置: 程式碼之家 >> 編程語言 >  >> .NET 網頁程式設計 >> C# >> 關於C#

C# WinForms 編輯視窗框架 BaseEditForm:新增/修改統一、驗證、實體綁定完整實作

     編輯:關於C#

設計目標

統一「新增」與「修改」邏輯:

  • 單一視窗類別處理兩種模式,由 Info 屬性判斷(null=新增,非 null=修改)。
  • 欄位驗證、實體綁定、儲存邏輯集中化。
  • 支援 TabControl 分頁、預設焦點、快捷鍵。

核心屬性與流程

屬性說明
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、軟刪除還原等細節,可構建一致、健壯、使用者友善的編輯體驗。

  1. 上一篇文章:
  2. 下一篇文章:
Copyright © 程式碼之家 All Rights Reserved