如何:实现类型转换器

类型转换器可用于在数据类型之间转换值,并通过提供文本到值的转换或待选值的下拉列表来帮助在设计时配置属性。 如果配置正确,通过使用 InstanceDescriptorSystem.Reflection 对象来给设计器序列化系统提供生成在运行时初始化属性的代码所需的信息,类型转换器可以生成属性配置代码。

值翻译的类型转换器

类型转换器可用于字符串到值的转换,或用于在设计时和运行时进行数据类型之间的双向翻译。 在宿主(如窗体设计器中的属性浏览器)中,类型转换器允许以文本形式向用户表示属性值,并且可以将用户输入的文本转换为相应数据类型的值。

大多数本机数据类型(Int32String、枚举类型和其他类型)具有默认的类型转换器,提供从字符串到值的转换并执行验证检查。 默认的类型转换器位于 System.ComponentModel 命名空间中,名为 类型转换器名称Converter。 当默认功能无法满足需要时,可以扩展类型转换器;当定义的自定义类型没有关联的类型转换器时,可以实现自定义类型转换器。

提示

TypeConverterAttribute 特性通常应用于属性或数据成员,以将其与类型转换器关联。 如果将 TypeConverterAttribute 应用于类型,则不必将其再次应用于该类型的属性或数据成员。

类型转换器的实现不依赖于任何用户界面功能。 因此,可在 Windows 窗体和 Web 窗体中应用同一个类型转换器。

实现能够将字符串翻译成点的简单类型转换器

  1. 定义一个从 TypeConverter 派生的类。

  2. 重写 CanConvertFrom 方法,指定转换器可从中转换的类型。 此方法是重载方法。

  3. 重写实现转换的 ConvertFrom 方法。 此方法是重载方法。

  4. 重写 CanConvertTo 方法,指定转换器可转换为哪种类型。 转换为字符串类型不需要重写此方法。 此方法是重载方法。

  5. 重写实现转换的 ConvertTo 方法。 此方法是重载方法。

  6. 重写执行验证的 IsValid 方法。 此方法是重载方法。

下面的代码示例实现了一个类型转换器,该转换器可以将 String 类型转换为 Point 类型,将 Point 转换为 String。 此示例中不重写 CanConvertToIsValid 方法。

Option Explicit 
Option Strict

Imports System
Imports System.ComponentModel
Imports System.Globalization
Imports System.Drawing

Public Class PointConverter
   Inherits TypeConverter
   
   ' Overrides the CanConvertFrom method of TypeConverter.
   ' The ITypeDescriptorContext interface provides the context for the
   ' conversion. Typically, this interface is used at design time to 
   ' provide information about the design-time container.
   Public Overrides Overloads Function CanConvertFrom(context As ITypeDescriptorContext, sourceType As Type) As Boolean
      If sourceType Is GetType(String) Then
         Return True
      End If
      Return MyBase.CanConvertFrom(context, sourceType)
   End Function
   
   ' Overrides the ConvertFrom method of TypeConverter.
   Public Overrides Overloads Function ConvertFrom(context As ITypeDescriptorContext, culture As CultureInfo, value As Object) As Object
      If TypeOf value Is String Then
         Dim v As String() = CStr(value).Split(New Char() {","c})
         Return New Point(Integer.Parse(v(0)), Integer.Parse(v(1)))
      End If
      Return MyBase.ConvertFrom(context, culture, value)
   End Function
   
   ' Overrides the ConvertTo method of TypeConverter.
   Public Overrides Overloads Function ConvertTo(context As ITypeDescriptorContext, culture As CultureInfo, value As Object, destinationType As Type) As Object
      If destinationType Is GetType(String) Then
         Return CType(value, Point).X & "," & CType(value, Point).Y
      End If
      Return MyBase.ConvertTo(context, culture, value, destinationType)
   End Function
End Class
using System;
using System.ComponentModel;
using System.Globalization;
using System.Drawing;

public class PointConverter : TypeConverter {
   // Overrides the CanConvertFrom method of TypeConverter.
   // The ITypeDescriptorContext interface provides the context for the
   // conversion. Typically, this interface is used at design time to 
   // provide information about the design-time container.
   public override bool CanConvertFrom(ITypeDescriptorContext context, 
      Type sourceType) {
      
      if (sourceType == typeof(string)) {
         return true;
      }
      return base.CanConvertFrom(context, sourceType);
   }
   // Overrides the ConvertFrom method of TypeConverter.
   public override object ConvertFrom(ITypeDescriptorContext context, 
      CultureInfo culture, object value) {
      if (value is string) {
         string[] v = ((string)value).Split(new char[] {','});
         return new Point(int.Parse(v[0]), int.Parse(v[1]));
      }
      return base.ConvertFrom(context, culture, value);
   }
   // Overrides the ConvertTo method of TypeConverter.
   public override object ConvertTo(ITypeDescriptorContext context, 
      CultureInfo culture, object value, Type destinationType) {  
      if (destinationType == typeof(string)) {
         return ((Point)value).X + "," + ((Point)value).Y;
      }
      return base.ConvertTo(context, culture, value, destinationType);
   }
}

向“属性”窗口提供标准值列表的类型转换器

类型转换器可以为“属性”窗口控件中的类型提供一个值列表。 如果类型转换器为类型提供了一组标准值,“属性”窗口控件中关联类型的属性的值输入字段就会显示一个向下键,单击该键可显示值的列表,以便设置属性的值。

当在设计时环境属性浏览器中选中了与该类型转换器关联的类型属性时,值输入字段将包含一个按钮,该按钮显示属性类型的标准值下拉列表,可从中选择标准值。

实现在属性浏览器中提供标准值下拉列表的简单类型转换器

  1. 定义一个从 TypeConverter 派生的类。

  2. 重写 GetStandardValuesSupported 方法并返回 true。

  3. 重写 GetStandardValues 方法并返回包含属性类型标准值的 TypeConverter.StandardValuesCollection。 属性的标准值必须与属性自身的类型一致。

  4. 重写 CanConvertFrom 方法并为类型字符串的 sourceType 参数值返回 true。

  5. 重写 ConvertFrom 方法并基于 value 参数返回相应的属性值。

  6. 将指示类型转换器类型的 TypeConverterAttribute 应用于要为其提供一组标准值的类型。

下面的示例演示了一个类型转换器,该转换器为其关联的类型属性的“属性”窗口控件提供了一组标准值。 示例类型转换器支持与其关联的 integer 类型的属性。 若要在 Visual Studio 中使用该示例,请将代码编译为类库,并向**“工具箱”**中添加 IntStandardValuesControl 组件。 在设计模式中将 IntStandardValuesControl 的实例添加到窗体,并在选中了控件的情况下在“属性”窗口中滚动到 TestInt 属性。 选中属性的值输入字段,这时会出现一个向下键,单击该键可显示标准值的下拉列表。 输入整数值将会把该值添加到标准值列表,并将属性设置为指定值。

using System;
using System.ComponentModel;
using System.Collections;
using System.Drawing;
using System.Windows.Forms;

namespace StandardValuesTest
{  
    public class StandardValuesIntConverter : System.ComponentModel.TypeConverter
    {
        private ArrayList values;
        public StandardValuesIntConverter()
        {
            // Initializes the standard values list with defaults.
            values = new ArrayList(new int[] { 1, 2, 3, 4, 5 });
        }

        // Indicates this converter provides a list of standard values.
        public override bool GetStandardValuesSupported(System.ComponentModel.ITypeDescriptorContext context)
        {
            return true;
        }

        // Returns a StandardValuesCollection of standard value objects.
        public override System.ComponentModel.TypeConverter.StandardValuesCollection GetStandardValues(System.ComponentModel.ITypeDescriptorContext context)
        {        
            // Passes the local integer array.
            StandardValuesCollection svc = 
                new StandardValuesCollection(values);       
            return svc;
        }

        // Returns true for a sourceType of string to indicate that 
        // conversions from string to integer are supported. (The 
        // GetStandardValues method requires a string to native type 
        // conversion because the items in the drop-down list are 
        // translated to string.)
        public override bool CanConvertFrom(System.ComponentModel.ITypeDescriptorContext context, System.Type sourceType)
        {
            if( sourceType == typeof(string) )
                return true;
            else 
                return base.CanConvertFrom(context, sourceType);
        }

        // If the type of the value to convert is string, parses the string 
        // and returns the integer to set the value of the property to. 
        // This example first extends the integer array that supplies the 
        // standard values collection if the user-entered value is not 
        // already in the array.
        public override object ConvertFrom(System.ComponentModel.ITypeDescriptorContext context, System.Globalization.CultureInfo culture, object value)
        {
            if( value.GetType() == typeof(string) )
            {
                // Parses the string to get the integer to set to the property.
                int newVal = int.Parse((string)value);
            
                // Tests whether new integer is already in the list.
                if( !values.Contains(newVal) )
                {
                    // If the integer is not in list, adds it in order.
                    values.Add(newVal);
                    values.Sort();
                }                                
                // Returns the integer value to assign to the property.
                return newVal;
            }
            else
                return base.ConvertFrom(context, culture, value);
        }
    }

    // Provides a test control with an integer property associated with 
    // the StandardValuesIntConverter type converter.
    public class IntStandardValuesControl : System.Windows.Forms.UserControl
    {
        [TypeConverter(typeof(StandardValuesIntConverter))]
        public int TestInt
        {
            get
            {
                return this.integer_field;
            }
            set
            {
                if(value.GetType() == typeof(int))
                    this.integer_field = value;
            }
        }
        private int integer_field = 0;
      
        public IntStandardValuesControl()
        {
            this.BackColor = Color.White;
            this.Size = new Size(472, 80);
        }

        // OnPaint override displays instructions for the example.
        protected override void OnPaint(System.Windows.Forms.PaintEventArgs e)
        {
            if(this.DesignMode)
            {
                e.Graphics.DrawString("TypeConverter.GetStandardValues Example Control", new Font(FontFamily.GenericMonospace, 10), new SolidBrush(Color.Blue), 5, 5);
                e.Graphics.DrawString("The type converter for the TestInt property of this", new Font(FontFamily.GenericMonospace, 10), new SolidBrush(Color.Black), 5, 20);
                e.Graphics.DrawString("component provides a list of standard values to the", new Font(FontFamily.GenericMonospace, 10), new SolidBrush(Color.Black), 5, 30);
                e.Graphics.DrawString("Properties window. Setting a value through a property", new Font(FontFamily.GenericMonospace, 10), new SolidBrush(Color.Black), 5, 40);
                e.Graphics.DrawString("grid adds it to the list of standard values.", new Font(FontFamily.GenericMonospace, 10), new SolidBrush(Color.Black), 5, 50);             
            }
            else
            {
                e.Graphics.DrawString("TypeConverter.GetStandardValues Example Control", new Font(FontFamily.GenericMonospace, 10), new SolidBrush(Color.Blue), 5, 5);         
                e.Graphics.DrawString("This control was intended for use in design mode.", new Font(FontFamily.GenericMonospace, 10), new SolidBrush(Color.Black), 5, 20);       
            }
        }
    }
}
Imports System
Imports System.ComponentModel
Imports System.ComponentModel.Design
Imports System.Collections
Imports System.Drawing
Imports System.Windows.Forms

Namespace StandardValuesTest

    Public Class StandardValuesIntConverter
        Inherits System.ComponentModel.TypeConverter

        Private values As ArrayList

        Public Sub New()
            ' Initializes the standard values list with defaults.
            values = New ArrayList(New Integer() {1, 2, 3, 4, 5})
        End Sub 'New

        ' Indicates this type converter provides a list of standard values.
        Public Overloads Overrides Function GetStandardValuesSupported(ByVal context As System.ComponentModel.ITypeDescriptorContext) As Boolean
            Return True
        End Function 'GetStandardValuesSupported

        ' Returns a StandardValuesCollection of standard value objects.
        Public Overloads Overrides Function GetStandardValues(ByVal context As System.ComponentModel.ITypeDescriptorContext) As System.ComponentModel.TypeConverter.StandardValuesCollection
            ' Passes the local integer array.
            Dim svc As New StandardValuesCollection(values)
            Return svc
        End Function 'GetStandardValues

        ' Returns true for a sourceType of string to indicate that 
        ' conversions from string to integer are supported. (The 
        ' GetStandardValues method requires a string to native type 
        ' conversion because the items in the drop-down list are 
        ' translated to string.)
        Public Overloads Overrides Function CanConvertFrom(ByVal context As System.ComponentModel.ITypeDescriptorContext, ByVal sourceType As System.Type) As Boolean
            If sourceType Is GetType(String) Then
                Return True
            Else
                Return MyBase.CanConvertFrom(context, sourceType)
            End If
        End Function 'CanConvertFrom

        ' If the type of the value to convert is string, parses the string 
        ' and returns the integer to set the value of the property to. 
        ' This example first extends the integer array that supplies the 
        ' standard values collection if the user-entered value is not 
        ' already in the array.
        Public Overloads Overrides Function ConvertFrom(ByVal context As System.ComponentModel.ITypeDescriptorContext, ByVal culture As System.Globalization.CultureInfo, ByVal value As Object) As Object
            If value.GetType() Is GetType(String) Then
                ' Parses the string to get the integer to set to the property.
                Dim newVal As Integer = Integer.Parse(CStr(value))

                ' Tests whether new integer is already in the list.
                If Not values.Contains(newVal) Then
                    ' If the integer is not in list, adds it in order.
                    values.Add(newVal)
                    values.Sort()
                End If
                ' Returns the integer value to assign to the property.
                Return newVal
            Else
                Return MyBase.ConvertFrom(context, culture, value)
            End If
        End Function 'ConvertFrom
    End Class 'StandardValuesIntConverter

    ' Provides a test control with an integer property associated with the 
    ' StandardValuesIntConverter type converter.
    Public Class IntStandardValuesControl
        Inherits System.Windows.Forms.UserControl

        <TypeConverter(GetType(StandardValuesIntConverter))> _
        Public Property TestInt() As Integer
            Get
                Return Me.integer_field
            End Get
            Set(ByVal Value As Integer)
                If Value.GetType() Is GetType(Integer) Then
                    Me.integer_field = Value
                End If
            End Set
        End Property
        Private integer_field As Integer = 0

        Public Sub New()
            Me.BackColor = Color.White
            Me.Size = New Size(472, 80)
        End Sub 'New

        ' OnPaint override displays instructions for the example.
        Protected Overrides Sub OnPaint(ByVal e As System.Windows.Forms.PaintEventArgs)
            If Me.DesignMode Then
                e.Graphics.DrawString("TypeConverter.GetStandardValues Example Control", New Font(FontFamily.GenericMonospace, 10), New SolidBrush(Color.Blue), 5, 5)
                e.Graphics.DrawString("The type converter for the TestInt property of this", New Font(FontFamily.GenericMonospace, 10), New SolidBrush(Color.Black), 5, 20)
                e.Graphics.DrawString("component provides a list of standard values to the", New Font(FontFamily.GenericMonospace, 10), New SolidBrush(Color.Black), 5, 30)
                e.Graphics.DrawString("Properties window. Setting a value through a property", New Font(FontFamily.GenericMonospace, 10), New SolidBrush(Color.Black), 5, 40)
                e.Graphics.DrawString("grid adds it to the list of standard values.", New Font(FontFamily.GenericMonospace, 10), New SolidBrush(Color.Black), 5, 50)
            Else
                e.Graphics.DrawString("TypeConverter.GetStandardValues Example Control", New Font(FontFamily.GenericMonospace, 10), New SolidBrush(Color.Blue), 5, 5)
                e.Graphics.DrawString("This control was intended for use in design mode.", New Font(FontFamily.GenericMonospace, 10), New SolidBrush(Color.Black), 5, 20)
            End If
        End Sub 'OnPaint
    End Class 'IntStandardValuesControl
End Namespace 'StandardValuesTest

在运行时为属性初始化生成代码的类型转换器

.NET Framework 提供了在设计时生成动态属性初始化代码(此代码在运行时初始化属性)的功能。

开发人员可以构建一个产生基于构造函数的初始化代码的类型转换器。 为了在运行时配置类型属性,这些类型转换器可以使用在设计时设置的值来动态生成构造函数代码。 类型转换器实现逻辑以配置属性的类型和构造函数的值。

为了初始化属性,除了生成构造函数之外,如果您还需要生成代码,可以实现一个自定义的 CodeDomSerializer,并应用将类型的 CodeDomSerializer 与该类型关联的 DesignerSerializerAttribute,这样就可以动态地生成代码。 通常只有在对组件初始化的代码生成进行动态控制或自定义很重要的情况下,才使用这种方法。 有关该方法的更多信息,请参见 CodeDomSerializer 文档。

要生成自定义的基于构造函数的属性初始值设定项,必须将类型转换器与要初始化的属性的类型进行关联,而且该类型转换器必须能够转换到 InstanceDescriptor

实现生成基于构造函数的属性初始化代码的类型转换器

  1. 定义一个从 TypeConverter 派生的类。

  2. 重写 CanConvertTo 方法。 如果 destinationType 参数等于 InstanceDescriptor 类型,则返回 true。

  3. 重写 ConvertTo 方法。 如果 destinationType 参数等于 InstanceDescriptor 类型,构造并返回一个 InstanceDescriptor,表示要为之生成代码的构造函数和构造函数参数。 若要创建表示相应的构造函数和参数的 InstanceDescriptor,可通过调用具有您正在查找的构造函数的相应方法签名的 GetConstructorGetConstructors 方法从正在初始化的属性的 Type 获得 ConstructorInfo。 然后创建一个新的实例说明符,并为表示待用构造函数类型的类型传递 ConstructorInfo,同时传递与构造函数签名匹配的参数对象数组。

下面的示例实现了一个类型转换器,该转换器可以为类型 Point 的属性生成基于构造函数的属性初始化代码。

public class PointConverter : TypeConverter 
{
   public override bool CanConvertTo(ITypeDescriptorContext context, Type destinationType) 
   {
      if (destinationType == typeof(InstanceDescriptor)) 
         return true;
      return base.CanConvertTo(context, destinationType);
   }

public override object ConvertTo(ITypeDescriptorContext context, 
CultureInfo culture, object value, Type destinationType) 
{
      // Insert other ConvertTo operations here.
      //
      if (destinationType == typeof(InstanceDescriptor) && 
value is Point) 
   {
         Point pt = (Point)value;

      ConstructorInfo ctor = typeof(Point).GetConstructor(
new Type[] {typeof(int), typeof(int)});
      if (ctor != null) 
      {
         return new InstanceDescriptor(ctor, new object[] {pt.X, pt.Y});
}
}
   return base.ConvertTo(context, culture, value, destinationType);      
}

编译代码

  • 在开发自定义 TypeConverter 时,建议将内部版本号设置为随每次生成而递增。 这样可以防止在设计环境中创建 TypeConverter 的较旧的缓存版本。

请参见

概念

通用类型转换