방법: 형식 변환기 구현
형식 변환기를 사용하여 데이터 형식 사이에 값을 변환하고 텍스트 대 값 변환 또는 폼 선택을 위한 드롭다운 목록이 지원되므로 디자인 타임에 더 쉽게 속성을 구성할 수 있습니다. 형식 변환기를 제대로 구성하면 InstanceDescriptor 및 System.Reflection 개체를 통해 속성 구성 코드를 생성하여 런타임에 속성을 초기화하는 코드를 생성하는 데 필요한 정보를 디자이너 serialization 시스템에 제공할 수 있습니다.
값 변환을 위한 형식 변환기
형식 변환기는 디자인 타임 및 런타임에 문자열 대 값 변환 또는 지원되는 데이터 형식 사이의 변환을 하는데 사용할 수 있습니다. 폼 디자이너의 속성 브라우저와 같은 호스트에서 형식 변환기를 사용하여 속성 값을 사용자에게 텍스트로 보여줄 수 있으며, 사용자가 입력한 텍스트를 적절한 데이터 형식의 값으로 변환할 수 있습니다.
대부분의 네이티브 데이터 형식(Int32, String, 열거형 등)에는 문자열에서 값으로 변환하고 유효성 검사를 수행하는 기본 형식 변환기가 있습니다. 기본 형식 변환기는 System.ComponentModel 네임스페이스에 있으며 TypeConverterNameConverter으로 명명됩니다. 기본 기능이 사용자의 목적에 적합하지 않은 경우 형식 변환기를 확장하거나, 연결된 형식 변환기가 없는 사용자 지정 형식을 정의하는 경우 사용자 지정 형식 변환기를 구현할 수 있습니다.
참고
일반적으로 TypeConverterAttribute 특성은 속성 또는 데이터 멤버에 적용되어 이를 형식 변환기와 연결합니다.TypeConverterAttribute가 형식에 적용되면 해당 형식의 속성 또는 데이터 멤버에 다시 적용할 필요가 없습니다.
형식 변환기의 구현은 사용자 인터페이스 기능에 독립적입니다. 따라서 같은 형식 변환기는 Windows Forms 및 Web Forms 모두에 적용될 수 있습니다.
문자열을 Point로 변환할 수 있는 간단한 형식 변환기르 구현하려면
TypeConverter에서 파생되는 클래스를 정의합니다.
변환기를 통해 변환 가능한 원본 형식을 지정하는 CanConvertFrom 메서드를 재정의합니다. 이 메서드는 오버로드됩니다.
변환을 구현하는 ConvertFrom 메서드를 재정의합니다. 이 메서드는 오버로드됩니다.
변환기를 통해 변환 가능한 대상 형식을 지정하는 CanConvertTo 메서드를 재정의합니다. 문자열 형식으로 변환하기 위해 이 메서드를 재정의할 필요는 없습니다. 이 메서드는 오버로드됩니다.
변환을 구현하는 ConvertTo 메서드를 재정의합니다. 이 메서드는 오버로드됩니다.
유효성 검사를 수행하는 IsValid(ITypeDescriptorContext, Object) 메서드를 재정의합니다. 이 메서드는 오버로드됩니다.
다음 코드 예제에서는 String 형식을 Point 형식으로 변환하고 Point 형식을 String 형식으로 변환하는 형식 변환기를 구현합니다. 이 예제에서 CanConvertTo 및 IsValid(ITypeDescriptorContext, Object) 메서드는 재정의되지 않습니다.
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);
}
}
속성 창에 표준 값 목록을 제공하는 형식 변환기
형식 변환기는 속성 창 컨트롤에서 형식에 대한 값 목록을 제공할 수 있습니다. 형식 변환기에서 형식에 대한 표준 값 집합을 제공하는 경우, 속성 창 컨트롤에서 연결된 형식의 속성에 대한 값 입력 필드에는 클릭할 때 속성 값을 설정할 수 있도록 값 목록을 보여 주는 아래쪽 화살표가 나타납니다.
형식 변환기가 연결되는 형식의 속성이 디자인 타임 환경 속성 브라우저에서 선택될 때 값 입력 필드에 선택할 수 있는 속성 형식에 대한 표준 값 목록의 드롭다운 목록이 표시되는 단추가 포함됩니다.
속성 브라우저에 표준 값의 드롭다운 목록을 제공하는 간단한 형식 변환기를 구현하려면
TypeConverter에서 파생되는 클래스를 정의합니다.
GetStandardValuesSupported 메서드를 재정의하고 true를 반환합니다.
GetStandardValues 메서드를 재정의하고 속성 형식에 대한 표준 값이 포함된 StandardValuesCollection을 반환합니다. 속성의 표준 값은 해당 속성과 형식이 같아야 합니다.
CanConvertFrom 메서드를 재정의하고 형식 문자열의 sourceType 매개 변수 값에 대해 true를 반환합니다.
ConvertFrom 메서드를 재정의하고 value 매개 변수를 기준으로 속성에 적절한 값을 반환합니다.
형식 변환기의 형식을 나타내는 TypeConverterAttribute를 표준 값 집합을 제공하는 형식에 적용합니다.
다음 예제에서는 연결되는 형식의 속성에 대한 속성 창 컨트롤에 표준 값 목록을 제공하는 형식 변환기를 보여 줍니다. 이 예제 형식 변환기에서는 연결된 정수 형식의 속성을 지원합니다. 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로 변환할 수 있어야 합니다.
생성자 기반 속성 초기화 코드를 생성하는 형식 변환기를 구현하려면
TypeConverter에서 파생되는 클래스를 정의합니다.
CanConvertTo 메서드를 재정의합니다. destinationType 매개 변수가 InstanceDescriptor 형식과 같으면 true를 반환합니다.
ConvertTo 메서드를 재정의합니다. destinationType 매개 변수가 InstanceDescriptor 형식과 같으면 생성자 및 생성자 인수를 나타내는 InstanceDescriptor를 구성하고 반환하여 코드를 생성합니다. 적절한 생성자와 매개 변수를 나타내는 InstanceDescriptor를 만들려면 검색 중인 생성자의 해당 메서드 시그니처를 통해 GetConstructor 또는 GetConstructors 메서드를 호출하여 초기화하는 속성의 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가 디자인 환경에서 만들어지지 못하도록 할 수 있습니다.