Gewusst wie: Implementieren eines Typkonverters
Ein Typkonverter kann zum Konvertieren von Werten zwischen Datentypen und zur Unterstützung der Eigenschaftenkonfiguration zur Entwurfszeit verwendet werden, indem die Konvertierung von Text in Werte oder eine Dropdownliste, in der Werte ausgewählt werden können, bereitgestellt werden. Bei ordnungsgemäßer Konfiguration kann ein Typkonverter mit einem InstanceDescriptor-Objekt und einem System.Reflection-Objekt Eigenschaftenkonfigurationscode erzeugen und so die erforderlichen Informationen bereitstellen, mit denen das Designerserialisierungssystem Code zum Initialisieren der Eigenschaft zur Laufzeit generieren kann.
Typkonverter für das Übersetzen von Werten
Typkonverter können zum Konvertieren von Zeichenfolgen in Werte oder für das Übersetzen aus bzw. in unterstützte Datentypen zur Entwurfszeit und zur Laufzeit verwendet werden. In einem Host, z. B. einem Eigenschaftenbrowser in einem Formular-Designer, können mit Typkonvertern Eigenschaftswerte in Textform für den Benutzer dargestellt werden, und vom Benutzer eingegebener Text kann in einen Wert des entsprechenden Datentyps konvertiert werden.
Die meisten systemeigenen Datentypen (Int32, String, Enumerationstypen und andere) verfügen über Standardtypkonverter, die Zeichenfolgen in Werte konvertieren und Validierungen durchführen. Die Standardtypkonverter befinden sich im System.ComponentModel-Namespace und werden mit TypeConverterNameConverter bezeichnet. Sie können einen Typkonverter erweitern, wenn die Standardfunktionen nicht Ihren Bedürfnissen entsprechen, oder einen benutzerdefinierten Typkonverter implementieren, wenn Sie einen benutzerdefinierten Typ definiert haben, dem kein Typkonverter zugeordnet ist.
Hinweis
Ein TypeConverterAttribute-Attribut wird normalerweise auf eine Eigenschaft oder einen Datenmember angewendet, um es einem Typkonverter zuzuordnen.Wenn ein TypeConverterAttribute auf einen Typ angewendet wird, muss es nicht erneut auf Eigenschaften oder Datenmember dieses Typs angewendet werden.
Die Implementierung eines Typkonverters ist von den Funktionen der Benutzeroberfläche unabhängig. Daher kann der gleiche Typkonverter sowohl unter Windows Forms als auch unter Web Forms angewendet werden.
So implementieren Sie einen einfachen Typkonverter, der einen "String"-Typ in einen "Point"-Typ konvertiert
Definieren Sie eine Klasse, die von TypeConverter abgeleitet ist.
Überschreiben Sie die CanConvertFrom-Methode, die angibt, aus welchem Typ der Konverter konvertieren kann. Diese Methode wird überladen.
Überschreiben Sie die ConvertFrom-Methode, die die Konvertierung implementiert. Diese Methode wird überladen.
Überschreiben Sie die CanConvertTo-Methode, die angibt, in welchen Typ der Konverter konvertieren kann. Es ist nicht erforderlich, diese Methode für die Konvertierung in einen Zeichenfolgentyp zu überschreiben. Diese Methode wird überladen.
Überschreiben Sie die ConvertTo-Methode, die die Konvertierung implementiert. Diese Methode wird überladen.
Überschreiben Sie die IsValid(ITypeDescriptorContext, Object)-Methode, die die Validierung ausführt. Diese Methode wird überladen.
Im folgenden Codebeispiel wird ein Typkonverter implementiert, der einen String-Typ in einen Point-Typ und einen Point-Typ in einen String-Typ konvertiert. Die CanConvertTo-Methode und die IsValid(ITypeDescriptorContext, Object)-Methode werden in diesem Beispiel nicht überschrieben.
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);
}
}
Typkonverter, die eine Liste von Standardwerten für ein Eigenschaftenfenster bereitstellen
Ein Typkonverter kann eine Liste von Werten für einen Typ in einem Eigenschaftenfenster-Steuerelement bereitstellen. Wenn ein Typkonverter eine Gruppe von Standardwerten für einen Typ bereitstellt, zeigt das Werteingabefeld für eine Eigenschaft des zugeordneten Typs in einem Eigenschaftenfenster-Steuerelement einen Pfeil nach unten an. Durch Klicken auf diesen Pfeil wird eine Liste mit Werten anzeigt, die zum Festlegen des Werts der Eigenschaft verwendet werden kann.
Wenn eine Eigenschaft des Typs, dem dieser Typkonverter zugeordnet ist, in einem Eigenschaftenbrowser der Entwurfszeitumgebung ausgewählt wird, enthält das Werteingabefeld eine Schaltfläche, die eine Dropdownliste der Standardwerte für den Eigenschaftentyp angezeigt, in der Sie auswählen können.
So implementieren Sie einen einfachen Typkonverter, der eine Dropdownliste mit Standardwerten in einem Eigenschaftenbrowser bereitstellt
Definieren Sie eine Klasse, die von TypeConverter abgeleitet ist.
Überschreiben Sie die GetStandardValuesSupported-Methode, und geben Sie true zurück.
Überschreiben Sie die GetStandardValues-Methode, und geben Sie StandardValuesCollection mit den Standardwerten für den Eigenschaftentyp zurück. Die Standardwerte für eine Eigenschaft müssen vom selben Typ wie die Eigenschaft selbst sein.
Überschreiben Sie die CanConvertFrom-Methode, und geben Sie true für einen sourceType-Parameterwert vom Typ string zurück.
Überschreiben Sie die ConvertFrom-Methode, und geben Sie den entsprechenden Wert für die auf dem value-Parameter basierende Eigenschaft zurück.
Wenden Sie ein TypeConverterAttribute an, das gegenüber dem Typ, für den Sie eine Gruppe von Standardwerten bereitstellen, den Typ des Typkonverters angibt.
Im folgenden Beispiel wird ein Typkonverter veranschaulicht, der eine Liste mit Standardwerten für den Typ, dem er zugeordnet ist, für ein Eigenschaftenfenster-Steuerelement bereitstellt. Der Beispieltypkonverter ist dem Typ Integer zugeordnet und unterstützt daher Eigenschaften mit diesem Typ. Sie können das Beispiel in Visual Studio verwenden, wenn Sie den Code in eine Klassenbibliothek kompilieren und der Toolbox die IntStandardValuesControl-Komponente hinzufügen. Fügen Sie einem Formular im Entwurfsmodus eine Instanz von IntStandardValuesControl hinzu, und führen Sie im Eigenschaftenfenster einen Bildlauf zur TestInt-Eigenschaft durch, während das Steuerelement ausgewählt ist. Wenn das Werteingabefeld für die Eigenschaft aktiviert ist, wird ein Pfeil nach unten angezeigt. Durch Klicken auf den Pfeil wird eine Dropdownliste mit Standardwerten anzeigt. Ein eingegebener Ganzzahlwert wird der Liste der Standardwerte hinzugefügt, und für die Eigenschaft wird der angegebene Wert festgelegt.
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
Typkonverter, die zur Laufzeit Code für die Initialisierung einer Eigenschaft generieren
.NET Framework ermöglicht das Generieren von dynamischem Code für die Initialisierung von Eigenschaften zur Entwurfszeit. Dieser Code initialisiert eine Eigenschaft zur Laufzeit.
Entwickler können einen Typkonverter erstellen, der auf Konstruktoren basierenden Initialisierungscode generiert. Diese Typkonverter können Konstruktorcode mithilfe von zur Entwurfszeit festgelegten Werten dynamisch generieren, um Eigenschaften eines Typs zur Laufzeit zu konfigurieren. Der Typkonverter implementiert die Logik, mit der der Typ und die Werte eines Konstruktors für die Eigenschaft konfiguriert werden.
Wenn es erforderlich ist, neben einem Konstruktor Code zu generieren, um eine Eigenschaft zu initialisieren, kann dieser Code dynamisch generiert werden, indem ein benutzerdefinierter CodeDomSerializer implementiert und ein DesignerSerializerAttribute angewendet wird, das dem Typ CodeDomSerializer für einen Typ zuordnet. Diese Herangehensweise wird i. d. R. nur für Szenarien verwendet, bei denen die dynamisch gesteuerte oder benutzerdefinierte Codegenerierung für die Komponenteninitialisierung wichtig ist. Weitere Informationen zu dieser Herangehensweise finden Sie in der Dokumentation zu CodeDomSerializer.
Zum Erstellen einer auf Konstruktoren basierenden, benutzerdefinierten Eigenschafteninitialisierung müssen Sie dem Typ der zu initialisierenden Eigenschaft einen Typkonverter zuordnen. Der Typkonverter muss in der Lage sein, in InstanceDescriptor zu konvertieren.
So implementieren Sie einen Typkonverter, der auf Konstruktoren basierenden Code zur Initialisierung von Eigenschaften erzeugt
Definieren Sie eine Klasse, die von TypeConverter abgeleitet ist.
Überschreiben Sie die CanConvertTo-Methode. Geben Sie true zurück, wenn der destinationType-Parameter dem InstanceDescriptor-Typ entspricht.
Überschreiben Sie die ConvertTo-Methode. Wenn der destinationType-Parameter dem InstanceDescriptor-Typ entspricht, erstellen Sie einen InstanceDescriptor, der den Konstruktor und die Konstruktorargumente darstellt, für die Code erstellt werden soll, und geben diesen Deskriptor zurück. Zum Erstellen von InstanceDescriptor, der den geeigneten Konstruktor und die geeigneten Parameter darstellt, rufen Sie ConstructorInfo von Type der Eigenschaft ab, die Sie initialisieren. Rufen Sie dazu die GetConstructor-Methode oder die GetConstructors-Methode mit der entsprechenden Methodensignatur des gesuchten Konstruktors auf. Erstellen Sie dann einen neuen InstanceDescriptor, und übergeben Sie ConstructorInfo für den Typ, der den zu verwendenden Konstruktortyp darstellt, zusammen mit einem Array von Parameterobjekten, die mit der Konstruktorsignatur übereinstimmen.
Im folgenden Beispiel wird ein Typkonverter implementiert, der auf Konstruktoren basierenden Code zur Initialisierung von Eigenschaften vom Typ Point generiert.
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);
}
Kompilieren des Codes
- Wenn Sie einen benutzerdefinierten TypeConverter entwickeln, ist es ratsam, die Buildnummer so zu konfigurieren, dass sie mit jedem Build erhöht wird. Dadurch wird verhindert, dass in der Entwicklungsumgebung ältere, zwischengespeicherte Versionen von TypeConverter erstellt werden.