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

ASP.NET 伺服器控件開發基礎 (10):集合屬性完整實作與設計時支援

     編輯:關於ASP.NET

前言:集合屬性是豐富控件功能的關鍵

DropDownList.Items、GridView.Columns、ValidatorCollection 等集合屬性,讓開發者能以宣告式標籤配置多項目。本篇完整實作集合屬性架構:集合類別、項目類別、集合編輯器、設計時持久化、ControlBuilder 整合。

集合屬性架構三要素

  1. 項目類別:代表單一項目 (如 ListItem, DataControlField)
  2. 集合類別:繼承 CollectionBase 或 List<T>,管理項目群組
  3. 控件屬性:暴露集合實例,標記設計時屬性

項目類別:DropItem 完整實作


[TypeConverter(typeof(DropItemConverter))]
public class DropItem
{
    private string _text = string.Empty;
    private string _value = string.Empty;
    private Color _backColor = Color.Empty;
    private Color _foreColor = Color.Empty;
    private bool _enabled = true;
    private string _toolTip = string.Empty;
    private object _tag;
    
    [Category("Appearance")]
    [DefaultValue("")]
    [Description("項目顯示文字")]
    [NotifyParentProperty(true)] // 通知父集合屬性已變更
    public string Text
    {
        get => _text;
        set { _text = value ?? string.Empty; OnChanged(); }
    }
    
    [Category("Data")]
    [DefaultValue("")]
    [Description("項目實際值")]
    [NotifyParentProperty(true)]
    public string Value
    {
        get => _value;
        set { _value = value ?? string.Empty; OnChanged(); }
    }
    
    [Category("Appearance")]
    [DefaultValue(typeof(Color), "")]
    [Description("背景顏色")]
    [NotifyParentProperty(true)]
    [TypeConverter(typeof(WebColorConverter))]
    public Color BackColor
    {
        get => _backColor;
        set { _backColor = value; OnChanged(); }
    }
    
    [Category("Appearance")]
    [DefaultValue(typeof(Color), "")]
    [Description("前景顏色")]
    [NotifyParentProperty(true)]
    [TypeConverter(typeof(WebColorConverter))]
    public Color ForeColor
    {
        get => _foreColor;
        set { _foreColor = value; OnChanged(); }
    }
    
    [Category("Behavior")]
    [DefaultValue(true)]
    [Description("是否啟用")]
    [NotifyParentProperty(true)]
    public bool Enabled
    {
        get => _enabled;
        set { _enabled = value; OnChanged(); }
    }
    
    [Category("Appearance")]
    [DefaultValue("")]
    [Description("工具提示")]
    [NotifyParentProperty(true)]
    public string ToolTip
    {
        get => _toolTip;
        set { _toolTip = value ?? string.Empty; OnChanged(); }
    }
    
    [Browsable(false)]
    [DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)]
    public object Tag
    {
        get => _tag;
        set => _tag = value;
    }
    
    // 通知父集合變更
    protected virtual void OnChanged()
    {
        // 實作可透過事件或介面通知父集合
    }
    
    public override string ToString() => Text;
}

項目類別 TypeConverter


public class DropItemConverter : TypeConverter
{
    public override bool GetPropertiesSupported(ITypeDescriptorContext context) => true;
    
    public override PropertyDescriptorCollection GetProperties(ITypeDescriptorContext context, object value, Attribute[] attributes)
    {
        PropertyDescriptorCollection props = TypeDescriptor.GetProperties(typeof(DropItem));
        return props.Sort(new[] { "Text", "Value", "Enabled", "BackColor", "ForeColor", "ToolTip" });
    }
    
    public override bool CanConvertTo(ITypeDescriptorContext context, Type destinationType)
    {
        return destinationType == typeof(InstanceDescriptor) || base.CanConvertTo(context, destinationType);
    }
    
    public override object ConvertTo(ITypeDescriptorContext context, CultureInfo culture, object value, Type destinationType)
    {
        if (destinationType == typeof(InstanceDescriptor) && value is DropItem item)
        {
            ConstructorInfo ctor = typeof(DropItem).GetConstructor(Type.EmptyTypes);
            return new InstanceDescriptor(ctor, null, false);
        }
        return base.ConvertTo(context, culture, value, destinationType);
    }
}

集合類別:DropItemCollection


[CollectionEditor(typeof(DropItemCollectionEditor))]
public class DropItemCollection : CollectionBase
{
    public DropItem this[int index]
    {
        get => (DropItem)List[index];
        set => List[index] = value;
    }
    
    public int Add(DropItem item)
    {
        item.Changed += Item_Changed; // 訂閱項目變更事件
        return List.Add(item);
    }
    
    public void Remove(DropItem item)
    {
        item.Changed -= Item_Changed;
        List.Remove(item);
    }
    
    public void Insert(int index, DropItem item)
    {
        item.Changed += Item_Changed;
        List.Insert(index, item);
    }
    
    public bool Contains(DropItem item) => List.Contains(item);
    public int IndexOf(DropItem item) => List.IndexOf(item);
    public void CopyTo(DropItem[] array, int index) => List.CopyTo(array, index);
    
    // 事件:項目屬性變更通知
    public event EventHandler CollectionChanged;
    
    private void Item_Changed(object sender, EventArgs e)
    {
        CollectionChanged?.Invoke(this, EventArgs.Empty);
    }
    
    // 視圖狀態支援 (若集合本身需持久化)
    private StateBag _viewState;
    public StateBag ViewState => _viewState ??= new StateBag();
}

集合編輯器:設計時視覺化編輯


public class DropItemCollectionEditor : CollectionEditor
{
    public DropItemCollectionEditor(Type type) : base(type) { }
    
    // 自訂新增項目型別
    protected override Type[] CreateNewItemTypes()
    {
        return new[] { typeof(DropItem) };
    }
    
    // 自訂表單標題
    protected override string GetDisplayText(object value)
    {
        return value is DropItem item ? item.Text : base.GetDisplayText(value);
    }
    
    // 自訂編輯表單
    protected override CollectionForm CreateCollectionForm()
    {
        CollectionForm form = base.CreateCollectionForm();
        form.Text = "下拉選單項目集合編輯器";
        // 可進一步自訂表單 UI
        return form;
    }
}

控件整合集合屬性


[ControlBuilder(typeof(DropDownListControlBuilder))]
public class CustomDropDownList : WebControl, INamingContainer, IPostBackDataHandler
{
    private DropItemCollection _items;
    
    [Category("Data")]
    [DefaultValue(null)]
    [DesignerSerializationVisibility(DesignerSerializationVisibility.Content)]
    [PersistenceMode(PersistenceMode.InnerProperty)]
    [Editor(typeof(DropItemCollectionEditor), typeof(UITypeEditor))]
    [MergableProperty(false)] // 不合併繼承層級的集合
    public DropItemCollection Items
    {
        get
        {
            if (_items == null)
            {
                _items = new DropItemCollection();
                if (IsTrackingViewState)
                    ((IStateManager)_items).TrackViewState();
            }
            return _items;
        }
    }
    
    // ControlBuilder 限制只允許 ListItem 標籤
    public class DropDownListControlBuilder : ControlBuilder
    {
        public override Type GetChildControlType(string tagName, IDictionary attributes)
        {
            if (tagName == "asp:ListItem" || tagName == "ListItem")
                return typeof(DropItem);
            return null;
        }
    }
}

頁面宣告式使用



    
        
        
        
    

渲染輸出實作


protected override void RenderContents(HtmlTextWriter writer)
{
    writer.RenderBeginTag(HtmlTextWriterTag.Select);
    writer.AddAttribute(HtmlTextWriterAttribute.Id, ClientID);
    writer.AddAttribute(HtmlTextWriterAttribute.Name, UniqueID);
    
    foreach (DropItem item in Items)
    {
        writer.RenderBeginTag(HtmlTextWriterTag.Option);
        writer.AddAttribute(HtmlTextWriterAttribute.Value, item.Value);
        
        if (!item.Enabled)
            writer.AddAttribute(HtmlTextWriterAttribute.Disabled, "disabled");
        
        if (!string.IsNullOrEmpty(item.ToolTip))
            writer.AddAttribute(HtmlTextWriterAttribute.Title, item.ToolTip);
        
        if (!item.BackColor.IsEmpty)
            writer.AddStyleAttribute(HtmlTextWriterStyle.BackgroundColor, ColorTranslator.ToHtml(item.BackColor));
        
        if (!item.ForeColor.IsEmpty)
            writer.AddStyleAttribute(HtmlTextWriterStyle.Color, ColorTranslator.ToHtml(item.ForeColor));
        
        writer.WriteEncodedText(item.Text);
        writer.RenderEndTag(); // option
    }
    
    writer.RenderEndTag(); // select
}

視圖狀態持久化


protected override object SaveViewState()
{
    object baseState = base.SaveViewState();
    object itemsState = _items != null ? _items.ViewState.SaveViewState() : null;
    return new Pair(baseState, itemsState);
}

protected override void LoadViewState(object savedState)
{
    if (savedState == null) return;
    Pair state = (Pair)savedState;
    base.LoadViewState(state.First);
    if (state.Second != null && _items != null)
        _items.ViewState.LoadViewState(state.Second);
}

進階:資料繫結集合屬性


public class DataBoundDropDownList : CustomDropDownList
{
    public string DataTextField { get; set; }
    public string DataValueField { get; set; }
    public string DataColorField { get; set; }
    
    protected override void PerformDataBinding(IEnumerable dataSource)
    {
        base.PerformDataBinding(dataSource);
        Items.Clear();
        
        PropertyDescriptor textProp = DataTextField != null ? TypeDescriptor.GetProperties(dataSource).Find(DataTextField, true) : null;
        PropertyDescriptor valueProp = DataValueField != null ? TypeDescriptor.GetProperties(dataSource).Find(DataValueField, true) : null;
        PropertyDescriptor colorProp = DataColorField != null ? TypeDescriptor.GetProperties(dataSource).Find(DataColorField, true) : null;
        
        foreach (object item in dataSource)
        {
            DropItem dropItem = new DropItem
            {
                Text = textProp?.GetValue(item)?.ToString() ?? item.ToString(),
                Value = valueProp?.GetValue(item)?.ToString() ?? item.ToString(),
            };
            
            if (colorProp != null)
            {
                object colorValue = colorProp.GetValue(item);
                if (colorValue is Color c) dropItem.BackColor = c;
                else if (colorValue is string s) dropItem.BackColor = ColorTranslator.FromHtml(s);
            }
            
            Items.Add(dropItem);
        }
    }
}

總結

集合屬性完整實作包含:

  1. 項目類別:屬性定義、NotifyParentProperty、TypeConverter 支援屬性網格
  2. 集合類別:CollectionBase、項目變更事件、CollectionEditor 視覺化編輯
  3. 控件整合:DesignerSerializationVisibility.Content、PersistenceMode.InnerProperty、ControlBuilder 限制標籤
  4. 視圖狀態:集合與項目雙層持久化
  5. 資料繫結:PerformDataBinding 自動從資料源建立項目

此架構支援 DropDownList、ListBox、CheckBoxList、Menu、TreeView 等所有集合型控件的核心需求。

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