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

WinForms 控件開發:為屬性提供下拉式屬性編輯器 (DropDown UITypeEditor)

     編輯:關於.NET

下拉式 vs 模態對話框編輯器

類型UI 表現適用場景基類返回值
模態對話框彈出獨立視窗,阻塞設計器複雜配置、多步驟、需大量輸入UITypeEditorEditStyle.Modal
下拉式屬性網格內下拉面板,輕量、非阻塞簡單選擇、預覽、少量輸入、即時反饋UITypeEditorEditStyle.DropDown

核心架構:三層協作

  1. 編輯器控件 (繼承 UserControl):實際 UI,處理輸入驗證、鍵盤導航 (Esc/Enter 關閉)、焦點離開提交。
  2. UITypeEditor 衍生類:橋樑,GetEditStyle 返回 DropDown,EditValue 調用 IWindowsFormsEditorService.DropDownControl 顯示控件,獲取結果返回。
  3. 屬性聲明:[Editor(typeof(MyDropDownEditor), typeof(UITypeEditor))] 掛載編輯器。

完整實戰:Scope 範圍屬性下拉編輯器

1. 編輯器控件 (ScopeEditorControl)


public partial class ScopeEditorControl : UserControl {
    private Scope _oldScope;
    private Scope _newScope;
    private bool _canceling;

    public ScopeEditorControl(Scope scope) {
        _oldScope = scope;
        _newScope = scope;
        InitializeComponent(); // 包含兩個 TextBox: txtMax, txtMin
    }

    public Scope Scope => _newScope; // 編輯器服務讀取結果

    // 輸入驗證
    private void txtMax_Validating(object sender, CancelEventArgs e) {
        if (!int.TryParse(txtMax.Text, out _)) { e.Cancel = true; ShowError("無效的最大值"); }
    }
    private void txtMin_Validating(object sender, CancelEventArgs e) {
        if (!int.TryParse(txtMin.Text, out _)) { e.Cancel = true; ShowError("無效的最小值"); }
    }

    // 鍵盤處理:Esc 取消,Enter 確認 (由父容器處理,這裡只處理 Esc 邏輯)
    protected override bool ProcessDialogKey(Keys keyData) {
        if (keyData == Keys.Escape) {
            _newScope = _oldScope; // 恢復原值
            _canceling = true;
        }
        return base.ProcessDialogKey(keyData);
    }

    // 焦點離開 = 用戶點擊外部 = 提交 (除非剛按 Esc)
    private void ScopeEditorControl_Leave(object sender, EventArgs e) {
        if (!_canceling) {
            _newScope.Max = int.Parse(txtMax.Text);
            _newScope.Min = int.Parse(txtMin.Text);
        }
    }

    private void ScopeEditorControl_Load(object sender, EventArgs e) {
        txtMax.Text = _oldScope.Max.ToString();
        txtMin.Text = _oldScope.Min.ToString();
    }
}

2. 下拉式編輯器 (ScopeDropDownEditor)


public class ScopeDropDownEditor : UITypeEditor {
    public override UITypeEditorEditStyle GetEditStyle(ITypeDescriptorContext context) {
        return (context?.Instance != null) ? UITypeEditorEditStyle.DropDown : 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; // 獲取被編輯控件實例
                var editorControl = new ScopeEditorControl(control.Scope); // 創建編輯器控件
                editorService.DropDownControl(editorControl); // 顯示下拉面板,阻塞直到關閉
                return editorControl.Scope; // 返回編輯結果
            }
        }
        return value;
    }
}

3. 掛載到屬性


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

關鍵交互細節

事件/方法觸發時機處理邏輯
DropDownControl用戶點擊屬性值右側下拉按鈕模態顯示控件,**阻塞 EditValue 方法**直到控件關閉
用戶按 Esc編輯器控件有焦點時ProcessDialogKey 設置 _canceling=true、恢復原值,面板關閉
用戶按 Enter文本框內默認觸發驗證 -> 焦點移動 -> Leave 事件提交值
用戶點擊 面板外部失去焦點觸發 Leave 事件提交值 (若非 _canceling)
Validating 取消輸入非法e.Cancel=true 阻止焦點離開,用戶必須修正或按 Esc 取消

UI 效果展示

屬性值右側出現下拉按鈕

下拉面板展開顯示編輯器控件

與模態編輯器的代碼差異 (僅 2 行)


// 模態編輯器
return UITypeEditorEditStyle.Modal;
// ...
editorService.ShowDialog(dialog); // 返回 DialogResult

// 下拉編輯器
return UITypeEditorEditStyle.DropDown;
// ...
editorService.DropDownControl(control); // 直接返回控件屬性

最佳實踐提示

  1. 編輯器控件保持輕量:避免複雜佈局、數據庫訪問、網絡請求,保證下拉展開瞬間響應。
  2. 正確處理鍵盤:Esc 取消、Enter/焦點離開提交,符合 Windows 交互慣例。
  3. 輸入驗證:Validating 事件 + ErrorProvider 或 MessageBox,e.Cancel=true 防止提交無效數據。
  4. 狀態隔離:編輯器控件內部維護 _oldScope/_newScope,不直接修改原對象,直到提交時返回新值。
  5. 泛化復用:編輯器控件、UITypeEditor 可封裝為基類,新屬性只需實現具體控件與值轉換邏輯。
  1. 上一篇文章:
  2. 下一篇文章:
Copyright © 程式碼之家 All Rights Reserved