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

WinForms 控件開發:為屬性提供彈出式模態對話框編輯器 (Modal UITypeEditor)

     編輯:關於.NET

模態對話框編輯器適用場景

  • 屬性結構複雜,需多步驟配置 (如字體選擇、顏色選擇、數據庫連接字串構建)。
  • 需要豐富的 UI 控件 (樹形、列表、預覽區、幫助按鈕)。
  • 編輯過程需驗證業務規則、彈出二級對話框。
  • 用戶預期有明確的「確定/取消」語義,支持取消不保存修改。

核心架構:三層協作 (同下拉式,僅顯示方式不同)

  1. 編輯器對話框 (繼承 Form):完整視窗,AcceptButton/CancelButton、DialogResult、驗證邏輯。
  2. UITypeEditor 衍生類:GetEditStyle 返回 Modal,EditValue 調用 editorService.ShowDialog(dialog),根據 DialogResult.OK 決定是否返回新值。
  3. 屬性掛載:[Editor(typeof(MyModalEditor), typeof(UITypeEditor))]。

完整實戰:Scope 屬性模態編輯器

1. 編輯器對話框 (ScopeEditorDialog)


public partial class ScopeEditorDialog : Form {
    private Scope _scope; // 內部持有副本,不直接修改原對象

    public ScopeEditorDialog(Scope scope) {
        InitializeComponent(); // 包含 txtMin, txtMax, btnOK, btnCancel
        _scope = scope;
        txtMin.Text = scope.Min.ToString();
        txtMax.Text = scope.Max.ToString();
    }

    // 驗證邏輯
    private void txtMin_Validating(object sender, CancelEventArgs e) {
        if (!int.TryParse(txtMin.Text, out _)) { e.Cancel = true; ShowError("最小值必須為整數"); }
    }
    private void txtMax_Validating(object sender, CancelEventArgs e) {
        if (!int.TryParse(txtMax.Text, out _)) { e.Cancel = true; ShowError("最大值必須為整數"); }
    }

    // OK 按鈕點擊:驗證通過才關閉
    private void btnOK_Click(object sender, EventArgs e) {
        if (ValidateChildren()) { // 觸發所有 Validating
            _scope.Min = int.Parse(txtMin.Text);
            _scope.Max = int.Parse(txtMax.Text);
            DialogResult = DialogResult.OK; // 關鍵:設置 DialogResult 關閉模態對話框
        }
    }

    // Cancel 按鈕:直接關閉,DialogResult.Cancel
    // 設計器中設置 btnCancel.DialogResult = DialogResult.Cancel 即可,無需代碼

    // 供 UITypeEditor 讀取結果
    public Scope Scope => _scope;
}

2. 模態編輯器 (ScopeEditor)


public class ScopeEditor : UITypeEditor {
    public override UITypeEditorEditStyle GetEditStyle(ITypeDescriptorContext context) {
        return (context?.Instance != null) ? UITypeEditorEditStyle.Modal : base.GetEditStyle(context);
    }

    public override object EditValue(ITypeDescriptorContext context, IServiceProvider provider, object value) {
        if (context?.Instance != null && provider != null) {
            var editorService = (IWindowsFormsEditorService)provider.GetService(typeof(IWindowsFormsEditorService));
            if (editorService != null) {
                var control = (MyListControl)context.Instance;
                using (var dlg = new ScopeEditorDialog(control.Scope)) { // 傳入當前值副本
                    if (editorService.ShowDialog(dlg) == DialogResult.OK) { // 模態顯示,阻塞直到關閉
                        return dlg.Scope; // 返回編輯後的值
                    }
                }
            }
        }
        return value; // 取消或異常返回原值
    }
}

3. 掛載到屬性


[Browsable(true)]
[Editor(typeof(ScopeEditor), typeof(UITypeEditor))]
public Scope Scope {
    get => _scope;
    set { _scope = value; }
}

UI 效果展示

屬性值右側出現彈出按鈕

彈出模態對話框編輯 Scope

模態 vs 下拉式:關鍵代碼差異

環節模態對話框下拉式
GetEditStyleUITypeEditorEditStyle.ModalUITypeEditorEditStyle.DropDown
編輯器 UI 基類FormUserControl
顯示方法editorService.ShowDialog(dialog)editorService.DropDownControl(control)
結果判斷DialogResult == DialogResult.OK直接讀取控件屬性 (Leave 事件已提交)
取消語義原生 DialogResult.Cancel,不修改內部副本Esc/焦點離開邏輯手動控制 (_canceling 旗標)
驗證機制ValidateChildren() + Validating 事件Validating 事件 + e.Cancel

最佳實踐提示

  1. 對話框尺寸固定/最小化:FormBorderStyle = FixedDialog、MaximizeBox = False、MinimizeBox = False、StartPosition = CenterParent。
  2. 數據隔離:構造函數接收值,**內部操作副本** (_scope),OK 時才更新副本並返回,Cancel 直接丟棄,保證原對象不被污染。
  3. 驗證優先:ValidateChildren() 在 OK 時調用,確保所有 Validating 通過才允許關閉。
  4. Help 按鈕:HelpButton = true、HelpRequested 事件顯示上下文幫助,提升專業度。
  5. 復用基類:將通用邏輯 (服務獲取、對話框顯示、結果判斷) 封裝為 ModalUITypeEditorBase<TDialog, TValue>,具體屬性只需實現對話框與值轉換。

總結

模態對話框編輯器為複雜屬性提供專業級編輯體驗。核心在於 UITypeEditor 衍生類返回 Modal 風格,利用 IWindowsFormsEditorService.ShowDialog 顯示 Form 衍生對話框,通過 DialogResult.OK 確認提交。配合數據副本隔離、完整驗證流程、標準 Windows 對話框交互,構建符合用戶預期的屬性編輯體驗。

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