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

ASP.NET 自訂 Multi-ListBox 控制項開發深度解析:ViewState 管理與複合控制項模式

     編輯:關於ASP.NET

控制項需求與設計目標

雙向清單方塊:左側「可選項目」、右側「已選項目」,支援雙擊/按鈕移動、多重選取、資料繫結、ViewState 完整保留、PostBack 後狀態不遺失。採用 複合控制項 模式封裝兩個 ListBox 與操作按鈕。

類別結構與關鍵屬性

[ToolboxData("<{0}:MultiListBox runat=server>")]
public class MultiListBox : CompositeControl {
    // 內部子控制項
    protected ListBox _lstSource;
    protected ListBox _lstTarget;
    protected Button _btnAdd, _btnRemove, _btnAddAll, _btnRemoveAll;

    // 資料繫結屬性
    [Category("Data"), DefaultValue("")]
    public string DataSourceID { get; set; }
    public string DataTextField { get; set; }
    public string DataValueField { get; set; }

    // 狀態屬性
    [Browsable(false), DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)]
    public ListItemCollection SourceItems => _lstSource.Items;
    [Browsable(false), DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)]
    public ListItemCollection TargetItems => _lstTarget.Items;

    // 事件
    public event EventHandler ItemsMoved;
}

ViewState 管理核心難點

ListItemCollection 內部未公開 SaveViewState/LoadViewState,必須透過 反射 呼叫私有方法,並將兩個 ListBox 的狀態與基類狀態封裝為 Triplet 儲存。

SaveViewState 實作

protected override object SaveViewState() {
    if (!EnableViewState) return null;

    object baseState = base.SaveViewState();
    object sourceState = CallPrivateMethod(_lstSource.Items, "SaveViewState");
    object targetState = CallPrivateMethod(_lstTarget.Items, "SaveViewState");

    if (baseState == null && sourceState == null && targetState == null) return null;
    return new Triplet(baseState, sourceState, targetState);
}

private object CallPrivateMethod(object obj, string methodName) {
    var method = obj.GetType().GetMethod(methodName, BindingFlags.NonPublic | BindingFlags.Instance);
    return method?.Invoke(obj, null);
}

LoadViewState 實作

protected override void LoadViewState(object savedState) {
    if (savedState == null) {
        base.LoadViewState(null);
        return;
    }

    var triplet = (Triplet)savedState;
    base.LoadViewState(triplet.First);
    CallPrivateMethod(_lstSource.Items, "LoadViewState", triplet.Second);
    CallPrivateMethod(_lstTarget.Items, "LoadViewState", triplet.Third);
    _stateLoaded = true;
}

CreateChildControls 與資料繫結流程

protected override void CreateChildControls() {
    Controls.Clear();
    _lstSource = new ListBox { ID = "lstSource", SelectionMode = ListSelectionMode.Multiple, Rows = 10 };
    _lstTarget = new ListBox { ID = "lstTarget", SelectionMode = ListSelectionMode.Multiple, Rows = 10 };
    _btnAdd = new Button { ID = "btnAdd", Text = ">>", Width = Unit.Pixel(50) };
    _btnRemove = new Button { ID = "btnRemove", Text = "<<", Width = Unit.Pixel(50) };
    // ... 事件綁定、版面配置(Table/TableRow/TableCell)
    Controls.Add(/* 版面容器 */);
    base.CreateChildControls();
}

protected override void PerformDataBinding(IEnumerable data) {
    base.PerformDataBinding(data);
    _lstSource.Items.Clear();
    foreach (var item in data) {
        string text = DataBinder.GetPropertyValue(item, DataTextField)?.ToString();
        string value = DataBinder.GetPropertyValue(item, DataValueField)?.ToString();
        _lstSource.Items.Add(new ListItem(text, value));
    }
    // Target 保留現有選取項目
}

移動邏輯與事件觸發

private void MoveItems(ListBox from, ListBox to) {
    var selected = from.Items.Cast().Where(i => i.Selected).ToList();
    foreach (var item in selected) {
        from.Items.Remove(item);
        to.Items.Add(item);
    }
    ItemsMoved?.Invoke(this, EventArgs.Empty);
}

protected void btnAdd_Click(object sender, EventArgs e) => MoveItems(_lstSource, _lstTarget);
protected void btnRemove_Click(object sender, EventArgs e) => MoveItems(_lstTarget, _lstSource);

渲染與樣式隔離

protected override void Render(HtmlTextWriter writer) {
    // 確保子控制項 ID 唯一、樣式類別隔離
    _lstSource.CssClass = "multilist-source";
    _lstTarget.CssClass = "multilist-target";
    base.Render(writer);
}

設計工具支援

[Designer(typeof(MultiListBoxDesigner))]
public class MultiListBoxDesigner : CompositeControlDesigner {
    public override void Initialize(IComponent component) {
        base.Initialize(component);
        SetViewFlags(ViewFlags.TemplateEditing, false);
    }
}

遷移至 ASP.NET Core / Blazor

  • Blazor Component:``,使用 `EventCallback>` 雙向綁定。
  • Tag Helper:``。
  • 前端元件庫:Ant Design Transfer、Element Plus Transfer、MudBlazor Transfer,功能完善且無障礙支援。

總結

自訂 Multi-ListBox 控制項展示了 ASP.NET WebForms 複合控制項開發的核心技巧:CreateChildControls、PerformDataBinding、反射操作 ListItemCollection ViewState、Triplet 封裝多重狀態。理解頁面生命週期與 ViewState 機制,是開發高品質伺服器控制項的關鍵。

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