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

ASP.NET 伺服器控件開發基礎 (9):TypeConverter 類型轉換器與設計時屬性編輯器

     編輯:關於ASP.NET

前言:屬性瀏覽器的智慧來自 TypeConverter

Visual Studio 屬性視窗能根據屬性型別自動提供對應編輯器:Color 顯示調色盤、Font 顯示字體對話框、列舉顯示下拉選單、日期顯示日期選擇器。這背後是 TypeConverter 機制。自訂複雜屬性時,實作專屬 TypeConverter 可獲得同樣的設計時體驗。

預設型別轉換器對照表

屬性型別預設轉換器設計時編輯器
stringStringConverter文字方塊
int, double, boolNumberConverter / BooleanConverter文字方塊 / 核取方塊
enumEnumConverter下拉選單
ColorColorConverter調色盤
FontFontConverter字體對話框
DateTimeDateTimeConverter日期選擇器
UnitUnitConverter尺寸編輯器
UrlUrlConverterURL 編輯器

自訂 TypeConverter 實作步驟

1. 定義複雜屬性類別


public class GameSettings
{
    public string GameName { get; set; }
    public int MaxPlayers { get; set; }
    public bool AllowSpectators { get; set; }
    public DifficultyLevel Difficulty { get; set; }
}

public enum DifficultyLevel { Easy, Normal, Hard, Expert }

2. 實作 TypeConverter


public class GameSettingsConverter : TypeConverter
{
    // 1. 能否從字串轉換 (支援宣告式標記)
    public override bool CanConvertFrom(ITypeDescriptorContext context, Type sourceType)
    {
        return sourceType == typeof(string) || base.CanConvertFrom(context, sourceType);
    }
    
    // 2. 字串轉換為物件 (格式: "GameName|MaxPlayers|AllowSpectators|Difficulty")
    public override object ConvertFrom(ITypeDescriptorContext context, CultureInfo culture, object value)
    {
        if (value is string str)
        {
            string[] parts = str.Split('|');
            if (parts.Length == 4)
            {
                return new GameSettings
                {
                    GameName = parts[0],
                    MaxPlayers = int.Parse(parts[1]),
                    AllowSpectators = bool.Parse(parts[2]),
                    Difficulty = (DifficultyLevel)Enum.Parse(typeof(DifficultyLevel), parts[3])
                };
            }
        }
        return base.ConvertFrom(context, culture, value);
    }
    
    // 3. 物件轉換為字串 (序列化至標記)
    public override object ConvertTo(ITypeDescriptorContext context, CultureInfo culture, object value, Type destinationType)
    {
        if (destinationType == typeof(string) && value is GameSettings settings)
        {
            return $"{settings.GameName}|{settings.MaxPlayers}|{settings.AllowSpectators}|{settings.Difficulty}";
        }
        return base.ConvertTo(context, culture, value, destinationType);
    }
    
    // 4. 支援屬性網格展開 (子屬性編輯)
    public override bool GetPropertiesSupported(ITypeDescriptorContext context) => true;
    
    public override PropertyDescriptorCollection GetProperties(ITypeDescriptorContext context, object value, Attribute[] attributes)
    {
        PropertyDescriptorCollection props = TypeDescriptor.GetProperties(typeof(GameSettings));
        // 可自訂屬性順序、屬性描述、編輯器
        return props.Sort(new[] { "GameName", "MaxPlayers", "Difficulty", "AllowSpectators" });
    }
    
    // 5. 支援標準值清單 (下拉選單)
    public override bool GetStandardValuesSupported(ITypeDescriptorContext context) => true;
    
    public override StandardValuesCollection GetStandardValues(ITypeDescriptorContext context)
    {
        // 針對 Difficulty 屬性提供標準值
        return new StandardValuesCollection(Enum.GetValues(typeof(DifficultyLevel)));
    }
    
    public override bool GetStandardValuesExclusive(ITypeDescriptorContext context) => true; // 僅允許清單值
}

3. 套用至屬性


public class GameControl : WebControl
{
    private GameSettings _gameSettings = new GameSettings();
    
    [Category("Game Settings")]
    [Description("遊戲設定")]
    [TypeConverter(typeof(GameSettingsConverter))]
    [DesignerSerializationVisibility(DesignerSerializationVisibility.Content)]
    [PersistenceMode(PersistenceMode.InnerProperty)]
    public GameSettings GameSettings
    {
        get => _gameSettings;
        set => _gameSettings = value ?? new GameSettings();
    }
}

4. 頁面宣告式使用



    

進階:CollectionConverter 與集合屬性編輯器

自訂集合類別


public class PlayerCollection : CollectionBase
{
    public Player this[int index] => (Player)List[index];
    public void Add(Player player) => List.Add(player);
    public void Remove(Player player) => List.Remove(player);
}

public class Player
{
    public string Name { get; set; }
    public int Score { get; set; }
    public TeamColor Team { get; set; }
}

public enum TeamColor { Red, Blue, Green, Yellow }

集合轉換器


public class PlayerCollectionConverter : TypeConverter
{
    public override bool CanConvertFrom(ITypeDescriptorContext context, Type sourceType)
    {
        return sourceType == typeof(string) || base.CanConvertFrom(context, sourceType);
    }
    
    public override object ConvertFrom(ITypeDescriptorContext context, CultureInfo culture, object value)
    {
        if (value is string str)
        {
            // 格式: "Name1,Score1,Team1;Name2,Score2,Team2"
            PlayerCollection collection = new PlayerCollection();
            string[] players = str.Split(';');
            foreach (string p in players)
            {
                string[] parts = p.Split(',');
                if (parts.Length == 3)
                {
                    collection.Add(new Player 
                    { 
                        Name = parts[0], 
                        Score = int.Parse(parts[1]), 
                        Team = (TeamColor)Enum.Parse(typeof(TeamColor), parts[2]) 
                    });
                }
            }
            return collection;
        }
        return base.ConvertFrom(context, culture, value);
    }
    
    public override object ConvertTo(ITypeDescriptorContext context, CultureInfo culture, object value, Type destinationType)
    {
        if (destinationType == typeof(string) && value is PlayerCollection collection)
        {
            return string.Join(";", collection.Cast().Select(p => $"{p.Name},{p.Score},{p.Team}"));
        }
        return base.ConvertTo(context, culture, value, destinationType);
    }
}

屬性編輯器 (UITypeEditor) 進階

模態對話框編輯器


public class GameSettingsEditor : UITypeEditor
{
    public override UITypeEditorEditStyle GetEditStyle(ITypeDescriptorContext context)
    {
        return UITypeEditorEditStyle.Modal; // 模態對話框
    }
    
    public override object EditValue(ITypeDescriptorContext context, IServiceProvider provider, object value)
    {
        IWindowsFormsEditorService editorService = (IWindowsFormsEditorService)provider.GetService(typeof(IWindowsFormsEditorService));
        if (editorService != null)
        {
            GameSettings settings = (GameSettings)value ?? new GameSettings();
            using (GameSettingsDialog dialog = new GameSettingsDialog(settings))
            {
                if (editorService.ShowDialog(dialog) == DialogResult.OK)
                {
                    return dialog.Settings;
                }
            }
        }
        return value;
    }
}

// 套用編輯器
[Editor(typeof(GameSettingsEditor), typeof(UITypeEditor))]
[TypeConverter(typeof(GameSettingsConverter))]
public GameSettings GameSettings { get; set; }

下拉式編輯器


public class ColorPickerEditor : UITypeEditor
{
    public override UITypeEditorEditStyle GetEditStyle(ITypeDescriptorContext context)
    {
        return UITypeEditorEditStyle.DropDown; // 下拉面板
    }
    
    public override object EditValue(ITypeDescriptorContext context, IServiceProvider provider, object value)
    {
        IWindowsFormsEditorService editorService = (IWindowsFormsEditorService)provider.GetService(typeof(IWindowsFormsEditorService));
        if (editorService != null)
        {
            ColorPickerControl picker = new ColorPickerControl((Color)value);
            editorService.DropDownControl(picker);
            return picker.SelectedColor;
        }
        return value;
    }
}

設計時序列化控制

屬性用途典型值
DesignerSerializationVisibility控制屬性如何序列化至 InitializeComponentVisible (預設), Hidden, Content (複雜物件/集合)
PersistenceMode控制屬性如何持久化至 .aspx 標記Attribute (簡單型別), InnerProperty (複雜物件), EncodedInnerDefaultProperty
DefaultValue指定預設值,設計器不產生程式碼/標記型別預設值或自訂值
Browsable是否在屬性視窗顯示true (預設), false

總結

TypeConverter 是屬性型別與設計時編輯器的橋樑:

  1. 雙向轉換:ConvertFrom (字串→物件)、ConvertTo (物件→字串/其他型別)
  2. 屬性展開:GetPropertiesSupported/GetProperties 啟用子屬性網格編輯
  3. 標準值:GetStandardValues* 提供下拉選單選項
  4. UITypeEditor:模態對話框/下拉面板提供豐富編輯體驗
  5. 序列化控制:DesignerSerializationVisibility/PersistenceMode/DefaultValue 精確控制程式碼/標記生成

實作完整的 TypeConverter,讓自訂複雜屬性擁有與內建型別同等的設計時體驗,是專業控件開發的重要指標。

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