Benutzer mit den meisten Antworten
ExpandableObjectConverter mit DefaultValue und Changed Notifikation

Frage
-
Hallo,
tja erstmal Hallo. Prinzipiell bin ich erst mal neu hier in diesem Forum. Ich hoffe das ist nicht etwa unpassend aber ich habe zum ExpandableObjectConverter gleich zwei Fragen.
1.) Wie kann ich dieser Lösung ein DefaultValue mitgeben?
2.) Warum erfolgt keine Changed-Notifikation wenn einzelne Werte innerhalt der Klasse geändert werden?
Ich hoffe der Codeblock ist nicht zu ausführlich, aber zur Erklärung wohl unerläßlich.
Die Klasse "Edges" wird in einer anderen Klasse als Eigenschaft verwendet. Folgende Problemstellung habe ich nun. Ich kann der Eigenschaft Radius keinen Defaultwert mitteilen. Also im PropertyGrid wird der Wert immer Fett dargestellt und zweitens erfolgt bei Änderung kein Event in der ParentKlasse. Ich hoffe ich habe mich verständlich ausgedrückt. Ein Beispiel-Code sagt mehr als tausend Wort.
Public Class MeineKlasse ' Auf die Implementierung von INotifyPropertyChanged wurde hier nur in der Darstellung verzichtet - Im Original ist das vorhanden Private _Radius As Edges = New Edges() <Category("Layout")> _ Public Property Radius() As Edges Get Return _Radius End Get Set(ByVal value As Edges) _Radius = value OnPropertyChanged("Radius") End Set End Property End Class <TypeConverter(GetType(EdgesConverter)), Serializable()> _ Public Class Edges Implements System.ComponentModel.INotifyPropertyChanged Public Sub New() End Sub #Region "Properties" Private _TopLeft As Integer <DescriptionAttribute("Gibt den Radius der linken oberen Ecke an."), _ NotifyParentProperty(True), DefaultValue(0)> _ Public Property TopLeft() As Integer Get Return _TopLeft End Get Set(ByVal value As Integer) _TopLeft = value OnPropertyChanged("TopLeft") End Set End Property Private _TopRight As Integer <DescriptionAttribute("Gibt den Radius der rechten oberen Ecke an."), _ NotifyParentProperty(True), DefaultValue(0)> _ Public Property TopRight() As Integer Get Return _TopRight End Get Set(ByVal value As Integer) _TopRight = value OnPropertyChanged("TopRight") End Set End Property Private _BottomLeft As Integer <DescriptionAttribute("Gibt den Radius der linken unteren Ecke an."), NotifyParentProperty(True), DefaultValue(0)> _ Public Property BottomLeft() As Integer Get Return _BottomLeft End Get Set(ByVal value As Integer) _BottomLeft = value OnPropertyChanged("BottomLeft") End Set End Property Private _BottomRight As Integer <DescriptionAttribute("Gibt den Radius der rechten unteren Ecke an."), NotifyParentProperty(True), DefaultValue(0)> _ Public Property BottomRight() As Integer Get Return _BottomRight End Get Set(ByVal value As Integer) _BottomRight = value OnPropertyChanged("BottomRight") End Set End Property #End Region #Region "Events / Eventhandler" Protected Overridable Sub OnPropertyChanged(ByVal propertyName As String) RaiseEvent PropertyChanged(Me, New PropertyChangedEventArgs(propertyName)) End Sub <NonSerialized()> _ Public Event PropertyChanged(ByVal sender As Object, ByVal e As System.ComponentModel.PropertyChangedEventArgs) _ Implements System.ComponentModel.INotifyPropertyChanged.PropertyChanged #End Region End Class Public Class EdgesConverter Inherits ExpandableObjectConverter Public Overloads Overrides Function CanConvertTo(ByVal context As System.ComponentModel.ITypeDescriptorContext, _ ByVal destinationType As System.Type) As Boolean If (destinationType Is GetType(Edges)) Then Return True End If Return MyBase.CanConvertFrom(context, destinationType) End Function 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 End If Return MyBase.CanConvertFrom(context, sourceType) End Function Public Overloads Overrides Function ConvertFrom(ByVal context As System.ComponentModel.ITypeDescriptorContext, _ ByVal culture As System.Globalization.CultureInfo, _ ByVal value As Object) As Object If TypeOf value Is String Then 'Try Dim s As String = CType(value, String) Dim RadiusParts() As String s = s.Replace(";", " ") s = s.Replace(" ", " ") RadiusParts = Split(s, " ") Dim _Edges As Edges = New Edges() If Not IsNothing(RadiusParts) Then If RadiusParts.Length = 1 Then ' Nur ein Wert gilt dann für alle _Edges.TopLeft = RadiusParts(0) _Edges.TopRight = RadiusParts(0) _Edges.BottomLeft = RadiusParts(0) _Edges.BottomRight = RadiusParts(0) Else If Not IsNothing(RadiusParts(0)) Then _Edges.TopLeft = RadiusParts(0) If Not IsNothing(RadiusParts(1)) Then _Edges.TopRight = RadiusParts(1) If Not IsNothing(RadiusParts(2)) Then _Edges.BottomLeft = RadiusParts(2) If Not IsNothing(RadiusParts(3)) Then _Edges.BottomRight = RadiusParts(3) End If End If End If End If End If End If Return _Edges 'Catch ex As Exception ' Throw New ArgumentException(value & " kann nicht zu Edges konvertiert werden.") 'End Try End If Return MyBase.ConvertFrom(context, culture, value) End Function Public Overloads Overrides Function ConvertTo(ByVal context As System.ComponentModel.ITypeDescriptorContext, _ ByVal culture As System.Globalization.CultureInfo, _ ByVal value As Object, ByVal _ destinationType As System.Type) As Object If (destinationType Is GetType(System.String) AndAlso TypeOf value Is Edges) Then Dim _Edges As Edges = CType(value, Edges) If _Edges.TopLeft = _Edges.TopRight And _Edges.TopRight = _Edges.BottomLeft And _Edges.BottomLeft = _Edges.BottomRight Then ' Nur ein Wert der gilt dann für alle Return _Edges.TopLeft.ToString Else Return _Edges.TopLeft & "; " & _Edges.TopRight & "; " & _ _Edges.BottomLeft & "; " & _Edges.BottomRight End If End If Return MyBase.ConvertTo(context, culture, value, destinationType) End Function End Class
Antworten
-
Hallo,
In einfachen Fällen kann man über das DefaultValue Attribut einen Wert mitgeben.
Für komplexere Fällen unterstützt der Designer weitere Methoden,
zum einen Reset<EigenschaftsName> für das Zurücksetzen
und zum anderen ShouldSerialize<Eigenschaftsname>, um zu bestimmen,
ob eine Eigenschaft serialisiert werden muß, weil sie vom Standard abweicht
(und wird auch vom Eigenschaftseditor ausgewertet)Eine mögliche Implementation in einem Steuerlement wäre:
' Vermutlich sinnfreie Werte nur zur Demo Private _radius As Edges = New Edges(1, 2, 3, 4) <Category("Layout")> _ <RefreshProperties(RefreshProperties.Repaint)> _ Public Property Radius() As Edges Get Return _Radius End Get Set(ByVal value As Edges) _radius = value RaiseEvent RadiusChanged(Me, EventArgs.Empty) End Set End Property Private Sub ResetRadius() Me.Radius = New Edges(1, 2, 3, 4) End Sub Private Function ShouldSerializeRadius() As Boolean 'Return Not Me.Radius.IsEmpty Return Not (Me.Radius.TopLeft = 1 AndAlso Me.Radius.TopRight = 2 AndAlso Me.Radius.BottomLeft = 3 AndAlso Me.Radius.BottomRight = 4) End Function <Category("PropertyChanged")> _ Public Event RadiusChanged As EventHandler
Das vermutlich sinnvfrei (1, 2, 3, 4) habe ich nur zur Verdeutlichung verwendet.
Verwendet man Standardwerte wie Leer sollte man sich zur Vereinfachung eine Eigenschaft dafür erstellen, siehe IsEmpty (unten).
Auch sollte man für Windows Forms explizite Ereignisse in der Form <EigenschaftsName>Changed verwenden,
anstatt INotifyPropertyChanged, damit die Ereignisse später im Eigenschaftsfenster auftauchen.Das von Dir verwendete NotifyParentProperty Attribut wirkt nur auf eine Eigenschaft
gleichen Namens im übergeordneten Element hat.Ich empfehle die Lektüre von Erweitern der Entwurfszeitunterstützung wo einiges mehr erläutert wird.
Ein TypeConverter sollte für die Entwicklungszeit das Erzeugen eines InstanceDescriptor unterstützen,
und unabhängig von der eingestellten Kultur funktonieren - schon englisch, deutsch haben unterschiedliche Listentrennzeichen.
Ich habe Deinen Konverter mal umgeschrieben (auf Basis einer meiner Konverter und nicht intensiv getestet):Option Explicit On Option Strict On Option Infer On Imports System.ComponentModel Imports System.ComponentModel.Design.Serialization Imports System.Globalization <TypeConverter(GetType(EdgesConverter)), Serializable()> _ Public Class Edges Implements System.ComponentModel.INotifyPropertyChanged Public Sub New() End Sub ''' <summary>Konstruktor für gleiche Werte.</summary> ''' <remarks>auch vom EdgesConverter verwendet.</remarks> Public Sub New(ByVal all As Integer) Me.TopLeft = all Me.TopRight = all Me.BottomLeft = all Me.BottomRight = all End Sub ''' <summary>Konstruktor für unterschiedliche Werte.</summary> ''' <remarks>auch vom EdgesConverter verwendet.</remarks> Public Sub New(ByVal topLeft As Integer, ByVal topRight As Integer, ByVal bottomLeft As Integer, ByVal bottomRight As Integer) Me.TopLeft = topLeft Me.TopRight = topRight Me.BottomLeft = bottomLeft Me.BottomRight = bottomRight End Sub ''' <summary> ''' Hilfsfunktion für einfache Abfragen ''' </summary> <Browsable(False)> _ Public ReadOnly Property IsEmpty As Boolean Get Return (Me._TopLeft = 0 AndAlso Me._TopRight = 0 AndAlso Me._BottomLeft = 0 AndAlso Me._BottomRight = 0) End Get End Property #Region "Properties" Private _TopLeft As Integer <DescriptionAttribute("Gibt den Radius der linken oberen Ecke an."), _ NotifyParentProperty(True), DefaultValue(0)> _ Public Property TopLeft() As Integer Get Return _TopLeft End Get Set(ByVal value As Integer) _TopLeft = value OnPropertyChanged("TopLeft") End Set End Property Private _TopRight As Integer <DescriptionAttribute("Gibt den Radius der rechten oberen Ecke an."), _ NotifyParentProperty(True), DefaultValue(0)> _ Public Property TopRight() As Integer Get Return _TopRight End Get Set(ByVal value As Integer) _TopRight = value OnPropertyChanged("TopRight") End Set End Property Private _BottomLeft As Integer <DescriptionAttribute("Gibt den Radius der linken unteren Ecke an."), NotifyParentProperty(True), DefaultValue(0)> _ Public Property BottomLeft() As Integer Get Return _BottomLeft End Get Set(ByVal value As Integer) _BottomLeft = value OnPropertyChanged("BottomLeft") End Set End Property Private _BottomRight As Integer <DescriptionAttribute("Gibt den Radius der rechten unteren Ecke an."), NotifyParentProperty(True), DefaultValue(0)> _ Public Property BottomRight() As Integer Get Return _BottomRight End Get Set(ByVal value As Integer) _BottomRight = value OnPropertyChanged("BottomRight") End Set End Property #End Region #Region "Events / Eventhandler" Protected Overridable Sub OnPropertyChanged(ByVal propertyName As String) RaiseEvent PropertyChanged(Me, New PropertyChangedEventArgs(propertyName)) End Sub <NonSerialized()> _ Public Event PropertyChanged(ByVal sender As Object, ByVal e As System.ComponentModel.PropertyChangedEventArgs) _ Implements System.ComponentModel.INotifyPropertyChanged.PropertyChanged #End Region End Class Public Class EdgesConverter Inherits TypeConverter Public Overrides Function CanConvertFrom(ByVal context As ITypeDescriptorContext, ByVal sourceType As Type) As Boolean Return sourceType Is GetType(String) OrElse MyBase.CanConvertFrom(context, sourceType) End Function Public Overrides Function CanConvertTo(ByVal context As ITypeDescriptorContext, ByVal destinationType As Type) As Boolean Return destinationType Is GetType(InstanceDescriptor) OrElse MyBase.CanConvertTo(context, destinationType) End Function Public Overrides Function ConvertFrom( _ ByVal context As ITypeDescriptorContext, _ ByVal culture As CultureInfo, _ ByVal value As Object) As Object ' Kein String dann Standardbehandlung Dim stringValue As String = TryCast(value, String) If stringValue IsNot Nothing Then stringValue = stringValue.Trim() End If If String.IsNullOrEmpty(stringValue) Then Return MyBase.ConvertFrom(context, culture, value) End If If culture Is Nothing Then ' aktuelle Kultur als Vorgabe culture = CultureInfo.CurrentCulture End If ' Trennzeichen lt. Cultur Dim separatorChar As Char = culture.TextInfo.ListSeparator.Chars(0) ' Für Konvertierung von Integer Dim integerConverter As TypeConverter = TypeDescriptor.GetConverter(GetType(Integer)) Dim stringArray = stringValue.Split(New Char() {separatorChar}) Dim integerValues As Integer() = New Integer(stringArray.Length - 1) {} For index = 0 To integerValues.Length - 1 integerValues(index) = CInt(integerConverter.ConvertFromString(context, culture, stringArray(index))) Next If integerValues.Length = 1 Then Return New Edges(integerValues(0)) ElseIf integerValues.Length = 2 Then ' Top und Bottom gleich Return New Edges(integerValues(0), integerValues(0), integerValues(1), integerValues(1)) ElseIf integerValues.Length = 4 Then Return New Edges(integerValues(0), integerValues(1), integerValues(2), integerValues(3)) Else Throw New ArgumentException("Ungültige Angabe") End If End Function Public Overrides Function ConvertTo( _ ByVal context As ITypeDescriptorContext, _ ByVal culture As CultureInfo, _ ByVal value As Object, _ ByVal destinationType As Type) As Object If destinationType Is Nothing Then Throw New ArgumentNullException("destinationType") End If If TypeOf value Is Edges Then Dim edgeValue = DirectCast(value, Edges) Dim isAll = (edgeValue.TopLeft = edgeValue.TopRight _ AndAlso edgeValue.TopLeft = edgeValue.BottomLeft _ AndAlso edgeValue.TopLeft = edgeValue.BottomRight) Dim isDual = (edgeValue.TopLeft = edgeValue.TopRight _ AndAlso edgeValue.BottomLeft = edgeValue.BottomRight) If destinationType Is GetType(String) Then If (culture Is Nothing) Then culture = CultureInfo.CurrentCulture End If Dim separator As String = culture.TextInfo.ListSeparator(0) & " " Dim integerConverter As TypeConverter = TypeDescriptor.GetConverter(GetType(Integer)) Dim stringArray As String() = New String() { _ integerConverter.ConvertToString(context, culture, edgeValue.TopLeft), _ integerConverter.ConvertToString(context, culture, edgeValue.TopRight), _ integerConverter.ConvertToString(context, culture, edgeValue.BottomLeft), _ integerConverter.ConvertToString(context, culture, edgeValue.BottomRight)} If isAll Then Return stringArray(0) ElseIf isDual Then Return String.Join(separator, stringArray(0), stringArray(2)) Else Return String.Join(separator, stringArray) End If ElseIf destinationType Is GetType(InstanceDescriptor) Then If isAll Then Return New InstanceDescriptor(GetType(Edges).GetConstructor( _ New Type() {GetType(Integer)}), New Object() {edgeValue.TopLeft}) Else Return New InstanceDescriptor(GetType(Edges).GetConstructor( _ New Type() {GetType(Integer), GetType(Integer), GetType(Integer), GetType(Integer)}), _ New Object() {edgeValue.TopLeft, edgeValue.TopRight, edgeValue.BottomLeft, edgeValue.BottomRight}) End If End If End If Return MyBase.ConvertTo(context, culture, value, destinationType) End Function Public Overrides Function GetCreateInstanceSupported(ByVal context As ITypeDescriptorContext) As Boolean Return True End Function Public Overrides Function CreateInstance(ByVal context As ITypeDescriptorContext, _ ByVal propertyValues As IDictionary) As Object If context Is Nothing Then Throw New ArgumentNullException("context") End If If propertyValues Is Nothing Then Throw New ArgumentNullException("propertyValues") End If Dim value As Edges = DirectCast(context.PropertyDescriptor.GetValue(context.Instance), Edges) Return New Edges( _ CInt(propertyValues.Item("TopLeft")), _ CInt(propertyValues.Item("TopRight")), _ CInt(propertyValues.Item("BottomLeft")), _ CInt(propertyValues.Item("BottomRight"))) End Function Public Overrides Function GetPropertiesSupported(ByVal context As ITypeDescriptorContext) As Boolean Return True End Function Public Overrides Function GetProperties(ByVal context As ITypeDescriptorContext, _ ByVal value As Object, ByVal attributes As Attribute()) As PropertyDescriptorCollection Return TypeDescriptor.GetProperties(GetType(Edges), attributes).Sort( _ New String() {"TopLeft", "TopRight", "BottomLeft", "BottomRight"}) End Function End Class
Darin u. a. gezeigt wie man 1, 2 oder 4 Werte anzeigen/konvertieren
und den Designer bei der Anzeige von Eigenschaften beeinflussen kann.Gruß Elmar
- Als Antwort markiert Robert BreitenhoferModerator Montag, 20. September 2010 15:33
-
Hallo Community
hier nun das Endergebnis für Alle. Eine Menge Text um 4 Werte zu verwalten :-)
Imports System.ComponentModel Imports System.ComponentModel.Design.Serialization Imports System.Globalization <TypeConverter(GetType(EdgesConverter)), Serializable()> _ Public Class Edges Implements System.ComponentModel.INotifyPropertyChanged #Region "Konstruktor" ''' <summary>Default Kostruktor</summary> ''' <remarks></remarks> Public Sub New() End Sub ''' <summary>Konstruktor für gleiche Werte.</summary> ''' <remarks>auch vom EdgesConverter verwendet.</remarks> Public Sub New(ByVal all As Integer) Me.TopLeft = all Me.TopRight = all Me.BottomLeft = all Me.BottomRight = all End Sub ''' <summary>Konstruktor für unterschiedliche Werte.</summary> ''' <remarks>auch vom EdgesConverter verwendet.</remarks> Public Sub New(ByVal topLeft As Integer, ByVal topRight As Integer, ByVal bottomLeft As Integer, ByVal bottomRight As Integer) Me.TopLeft = topLeft Me.TopRight = topRight Me.BottomLeft = bottomLeft Me.BottomRight = bottomRight End Sub ''' <summary> ''' Hilfsfunktion für einfache Abfragen ''' </summary> <Browsable(False)> _ Public ReadOnly Property IsEmpty As Boolean Get Return (Me._TopLeft = 0 AndAlso Me._TopRight = 0 AndAlso Me._BottomLeft = 0 AndAlso Me._BottomRight = 0) End Get End Property #End Region #Region "Properties" Private _TopLeft As Integer <DescriptionAttribute("Gibt den Radius der linken oberen Ecke an."), _ DefaultValue(0)> _ Public Property TopLeft() As Integer Get Return _TopLeft End Get Set(ByVal value As Integer) _TopLeft = value OnPropertyChanged("TopLeft") End Set End Property Private _TopRight As Integer <DescriptionAttribute("Gibt den Radius der rechten oberen Ecke an."), _ DefaultValue(0)> _ Public Property TopRight() As Integer Get Return _TopRight End Get Set(ByVal value As Integer) _TopRight = value OnPropertyChanged("TopRight") End Set End Property Private _BottomLeft As Integer <DescriptionAttribute("Gibt den Radius der linken unteren Ecke an."), DefaultValue(0)> _ Public Property BottomLeft() As Integer Get Return _BottomLeft End Get Set(ByVal value As Integer) _BottomLeft = value OnPropertyChanged("BottomLeft") End Set End Property Private _BottomRight As Integer <DescriptionAttribute("Gibt den Radius der rechten unteren Ecke an."), DefaultValue(0)> _ Public Property BottomRight() As Integer Get Return _BottomRight End Get Set(ByVal value As Integer) _BottomRight = value OnPropertyChanged("BottomRight") End Set End Property #End Region #Region "Events / Eventhandler" Protected Overridable Sub OnPropertyChanged(ByVal propertyName As String) RaiseEvent PropertyChanged(Me, New PropertyChangedEventArgs(propertyName)) End Sub <NonSerialized()> _ Public Event PropertyChanged(ByVal sender As Object, ByVal e As System.ComponentModel.PropertyChangedEventArgs) _ Implements System.ComponentModel.INotifyPropertyChanged.PropertyChanged #End Region #Region "Methoden" Public Overrides Function Equals(ByVal other As Object) As Boolean Return TypeOf other Is Edges AndAlso Me = CType(other, Edges) End Function Public Overrides Function GetHashCode() As Integer Return (Me._TopLeft Xor Me._TopRight) << 16 Or (Me._BottomLeft Xor Me._BottomRight) End Function Public Shared Operator =(ByVal left As Edges, ByVal right As Edges) As Boolean Return (left._TopLeft = right._TopLeft AndAlso _ left._TopRight = right._TopRight AndAlso _ left._BottomLeft = right._BottomLeft AndAlso _ left._BottomRight = right._BottomRight) End Operator Public Shared Operator <>(ByVal left As Edges, ByVal right As Edges) As Boolean Return Not (left = right) End Operator #End Region End Class Public Class EdgesConverter Inherits TypeConverter #Region "Methoden" ''' <summary> ''' CanConvertFrom ''' </summary> ''' <param name="context"></param> ''' <param name="sourceType"></param> ''' <returns></returns> ''' <remarks></remarks> Public Overrides Function CanConvertFrom(ByVal context As ITypeDescriptorContext, ByVal sourceType As Type) As Boolean Return sourceType Is GetType(String) OrElse MyBase.CanConvertFrom(context, sourceType) End Function ''' <summary> ''' CanConvertTo ''' </summary> ''' <param name="context"></param> ''' <param name="destinationType"></param> ''' <returns></returns> ''' <remarks></remarks> Public Overrides Function CanConvertTo(ByVal context As ITypeDescriptorContext, _ ByVal destinationType As Type) As Boolean Return destinationType Is GetType(InstanceDescriptor) OrElse MyBase.CanConvertTo(context, destinationType) End Function ''' <summary> ''' ConvertFrom ''' </summary> ''' <param name="context"></param> ''' <param name="culture"></param> ''' <param name="value"></param> ''' <returns></returns> ''' <remarks></remarks> Public Overrides Function ConvertFrom(ByVal context As ITypeDescriptorContext, _ ByVal culture As CultureInfo, _ ByVal value As Object) As Object ' Kein String dann Standardbehandlung Dim stringValue As String = TryCast(value, String) If stringValue IsNot Nothing Then stringValue = stringValue.Trim() End If If String.IsNullOrEmpty(stringValue) Then Return MyBase.ConvertFrom(context, culture, value) End If If culture Is Nothing Then ' aktuelle Kultur als Vorgabe culture = CultureInfo.CurrentCulture End If ' Trennzeichen lt. Cultur Dim separatorChar As Char = culture.TextInfo.ListSeparator.Chars(0) ' Für Konvertierung von Integer Dim integerConverter As TypeConverter = TypeDescriptor.GetConverter(GetType(Integer)) Dim stringArray As String() = stringValue.Split(New Char() {separatorChar}) Dim integerValues As Integer() = New Integer(stringArray.Length - 1) {} For index As Integer = 0 To integerValues.Length - 1 integerValues(index) = CInt(integerConverter.ConvertFromString(context, culture, stringArray(index))) Next If integerValues.Length = 1 Then Return New Edges(integerValues(0)) ElseIf integerValues.Length = 2 Then ' Top und Bottom gleich Return New Edges(integerValues(0), integerValues(0), integerValues(1), integerValues(1)) ElseIf integerValues.Length = 4 Then Return New Edges(integerValues(0), integerValues(1), integerValues(2), integerValues(3)) Else Throw New ArgumentException("Ungültige Angabe") End If End Function ''' <summary> ''' ConvertTo ''' </summary> ''' <param name="context"></param> ''' <param name="culture"></param> ''' <param name="value"></param> ''' <param name="destinationType"></param> ''' <returns></returns> ''' <remarks></remarks> Public Overrides Function ConvertTo( _ ByVal context As ITypeDescriptorContext, _ ByVal culture As CultureInfo, _ ByVal value As Object, _ ByVal destinationType As Type) As Object If destinationType Is Nothing Then Throw New ArgumentNullException("destinationType") End If If TypeOf value Is Edges Then Dim edgeValue As Edges = DirectCast(value, Edges) Dim isAll As Boolean = (edgeValue.TopLeft = edgeValue.TopRight _ AndAlso edgeValue.TopLeft = edgeValue.BottomLeft _ AndAlso edgeValue.TopLeft = edgeValue.BottomRight) Dim isDual As Boolean = (edgeValue.TopLeft = edgeValue.TopRight _ AndAlso edgeValue.BottomLeft = edgeValue.BottomRight) If destinationType Is GetType(String) Then If (culture Is Nothing) Then culture = CultureInfo.CurrentCulture End If Dim separator As String = culture.TextInfo.ListSeparator(0) & " " Dim integerConverter As TypeConverter = TypeDescriptor.GetConverter(GetType(Integer)) Dim stringArray As String() = New String() { _ integerConverter.ConvertToString(context, culture, edgeValue.TopLeft), _ integerConverter.ConvertToString(context, culture, edgeValue.TopRight), _ integerConverter.ConvertToString(context, culture, edgeValue.BottomLeft), _ integerConverter.ConvertToString(context, culture, edgeValue.BottomRight)} If isAll Then Return stringArray(0) ElseIf isDual Then Return String.Join(separator, {stringArray(0), stringArray(2)}) Else Return String.Join(separator, stringArray) End If ElseIf destinationType Is GetType(InstanceDescriptor) Then If isAll Then Return New InstanceDescriptor(GetType(Edges).GetConstructor( _ New Type() {GetType(Integer)}), New Object() {edgeValue.TopLeft}) Else Return New InstanceDescriptor(GetType(Edges).GetConstructor( _ New Type() {GetType(Integer), GetType(Integer), GetType(Integer), GetType(Integer)}), _ New Object() {edgeValue.TopLeft, edgeValue.TopRight, edgeValue.BottomLeft, edgeValue.BottomRight}) End If End If End If Return MyBase.ConvertTo(context, culture, value, destinationType) End Function ''' <summary> ''' GetCreateInstanceSupported ''' </summary> ''' <param name="context"></param> ''' <returns></returns> ''' <remarks></remarks> Public Overrides Function GetCreateInstanceSupported(ByVal context As ITypeDescriptorContext) As Boolean Return True End Function ''' <summary> ''' CreateInstance ''' </summary> ''' <param name="context"></param> ''' <param name="propertyValues"></param> ''' <returns></returns> ''' <remarks></remarks> Public Overrides Function CreateInstance(ByVal context As ITypeDescriptorContext, _ ByVal propertyValues As IDictionary) As Object If context Is Nothing Then Throw New ArgumentNullException("context") End If If propertyValues Is Nothing Then Throw New ArgumentNullException("propertyValues") End If Dim value As Edges = DirectCast(context.PropertyDescriptor.GetValue(context.Instance), Edges) Return New Edges( _ CInt(propertyValues.Item("TopLeft")), _ CInt(propertyValues.Item("TopRight")), _ CInt(propertyValues.Item("BottomLeft")), _ CInt(propertyValues.Item("BottomRight"))) End Function ''' <summary> ''' GetPropertiesSupported ''' </summary> ''' <param name="context"></param> ''' <returns></returns> ''' <remarks></remarks> Public Overrides Function GetPropertiesSupported(ByVal context As ITypeDescriptorContext) As Boolean Return True End Function ''' <summary> ''' GetProperties ''' </summary> ''' <param name="context"></param> ''' <param name="value"></param> ''' <param name="attributes"></param> ''' <returns></returns> ''' <remarks></remarks> Public Overrides Function GetProperties(ByVal context As ITypeDescriptorContext, _ ByVal value As Object, ByVal attributes As Attribute()) As PropertyDescriptorCollection Return TypeDescriptor.GetProperties(GetType(Edges), attributes).Sort( _ New String() {"TopLeft", "TopRight", "BottomLeft", "BottomRight"}) End Function #End Region End Class
Gruß Frank
- Als Antwort markiert NachtAktiv Sonntag, 19. September 2010 21:31
Alle Antworten
-
Hallo,
In einfachen Fällen kann man über das DefaultValue Attribut einen Wert mitgeben.
Für komplexere Fällen unterstützt der Designer weitere Methoden,
zum einen Reset<EigenschaftsName> für das Zurücksetzen
und zum anderen ShouldSerialize<Eigenschaftsname>, um zu bestimmen,
ob eine Eigenschaft serialisiert werden muß, weil sie vom Standard abweicht
(und wird auch vom Eigenschaftseditor ausgewertet)Eine mögliche Implementation in einem Steuerlement wäre:
' Vermutlich sinnfreie Werte nur zur Demo Private _radius As Edges = New Edges(1, 2, 3, 4) <Category("Layout")> _ <RefreshProperties(RefreshProperties.Repaint)> _ Public Property Radius() As Edges Get Return _Radius End Get Set(ByVal value As Edges) _radius = value RaiseEvent RadiusChanged(Me, EventArgs.Empty) End Set End Property Private Sub ResetRadius() Me.Radius = New Edges(1, 2, 3, 4) End Sub Private Function ShouldSerializeRadius() As Boolean 'Return Not Me.Radius.IsEmpty Return Not (Me.Radius.TopLeft = 1 AndAlso Me.Radius.TopRight = 2 AndAlso Me.Radius.BottomLeft = 3 AndAlso Me.Radius.BottomRight = 4) End Function <Category("PropertyChanged")> _ Public Event RadiusChanged As EventHandler
Das vermutlich sinnvfrei (1, 2, 3, 4) habe ich nur zur Verdeutlichung verwendet.
Verwendet man Standardwerte wie Leer sollte man sich zur Vereinfachung eine Eigenschaft dafür erstellen, siehe IsEmpty (unten).
Auch sollte man für Windows Forms explizite Ereignisse in der Form <EigenschaftsName>Changed verwenden,
anstatt INotifyPropertyChanged, damit die Ereignisse später im Eigenschaftsfenster auftauchen.Das von Dir verwendete NotifyParentProperty Attribut wirkt nur auf eine Eigenschaft
gleichen Namens im übergeordneten Element hat.Ich empfehle die Lektüre von Erweitern der Entwurfszeitunterstützung wo einiges mehr erläutert wird.
Ein TypeConverter sollte für die Entwicklungszeit das Erzeugen eines InstanceDescriptor unterstützen,
und unabhängig von der eingestellten Kultur funktonieren - schon englisch, deutsch haben unterschiedliche Listentrennzeichen.
Ich habe Deinen Konverter mal umgeschrieben (auf Basis einer meiner Konverter und nicht intensiv getestet):Option Explicit On Option Strict On Option Infer On Imports System.ComponentModel Imports System.ComponentModel.Design.Serialization Imports System.Globalization <TypeConverter(GetType(EdgesConverter)), Serializable()> _ Public Class Edges Implements System.ComponentModel.INotifyPropertyChanged Public Sub New() End Sub ''' <summary>Konstruktor für gleiche Werte.</summary> ''' <remarks>auch vom EdgesConverter verwendet.</remarks> Public Sub New(ByVal all As Integer) Me.TopLeft = all Me.TopRight = all Me.BottomLeft = all Me.BottomRight = all End Sub ''' <summary>Konstruktor für unterschiedliche Werte.</summary> ''' <remarks>auch vom EdgesConverter verwendet.</remarks> Public Sub New(ByVal topLeft As Integer, ByVal topRight As Integer, ByVal bottomLeft As Integer, ByVal bottomRight As Integer) Me.TopLeft = topLeft Me.TopRight = topRight Me.BottomLeft = bottomLeft Me.BottomRight = bottomRight End Sub ''' <summary> ''' Hilfsfunktion für einfache Abfragen ''' </summary> <Browsable(False)> _ Public ReadOnly Property IsEmpty As Boolean Get Return (Me._TopLeft = 0 AndAlso Me._TopRight = 0 AndAlso Me._BottomLeft = 0 AndAlso Me._BottomRight = 0) End Get End Property #Region "Properties" Private _TopLeft As Integer <DescriptionAttribute("Gibt den Radius der linken oberen Ecke an."), _ NotifyParentProperty(True), DefaultValue(0)> _ Public Property TopLeft() As Integer Get Return _TopLeft End Get Set(ByVal value As Integer) _TopLeft = value OnPropertyChanged("TopLeft") End Set End Property Private _TopRight As Integer <DescriptionAttribute("Gibt den Radius der rechten oberen Ecke an."), _ NotifyParentProperty(True), DefaultValue(0)> _ Public Property TopRight() As Integer Get Return _TopRight End Get Set(ByVal value As Integer) _TopRight = value OnPropertyChanged("TopRight") End Set End Property Private _BottomLeft As Integer <DescriptionAttribute("Gibt den Radius der linken unteren Ecke an."), NotifyParentProperty(True), DefaultValue(0)> _ Public Property BottomLeft() As Integer Get Return _BottomLeft End Get Set(ByVal value As Integer) _BottomLeft = value OnPropertyChanged("BottomLeft") End Set End Property Private _BottomRight As Integer <DescriptionAttribute("Gibt den Radius der rechten unteren Ecke an."), NotifyParentProperty(True), DefaultValue(0)> _ Public Property BottomRight() As Integer Get Return _BottomRight End Get Set(ByVal value As Integer) _BottomRight = value OnPropertyChanged("BottomRight") End Set End Property #End Region #Region "Events / Eventhandler" Protected Overridable Sub OnPropertyChanged(ByVal propertyName As String) RaiseEvent PropertyChanged(Me, New PropertyChangedEventArgs(propertyName)) End Sub <NonSerialized()> _ Public Event PropertyChanged(ByVal sender As Object, ByVal e As System.ComponentModel.PropertyChangedEventArgs) _ Implements System.ComponentModel.INotifyPropertyChanged.PropertyChanged #End Region End Class Public Class EdgesConverter Inherits TypeConverter Public Overrides Function CanConvertFrom(ByVal context As ITypeDescriptorContext, ByVal sourceType As Type) As Boolean Return sourceType Is GetType(String) OrElse MyBase.CanConvertFrom(context, sourceType) End Function Public Overrides Function CanConvertTo(ByVal context As ITypeDescriptorContext, ByVal destinationType As Type) As Boolean Return destinationType Is GetType(InstanceDescriptor) OrElse MyBase.CanConvertTo(context, destinationType) End Function Public Overrides Function ConvertFrom( _ ByVal context As ITypeDescriptorContext, _ ByVal culture As CultureInfo, _ ByVal value As Object) As Object ' Kein String dann Standardbehandlung Dim stringValue As String = TryCast(value, String) If stringValue IsNot Nothing Then stringValue = stringValue.Trim() End If If String.IsNullOrEmpty(stringValue) Then Return MyBase.ConvertFrom(context, culture, value) End If If culture Is Nothing Then ' aktuelle Kultur als Vorgabe culture = CultureInfo.CurrentCulture End If ' Trennzeichen lt. Cultur Dim separatorChar As Char = culture.TextInfo.ListSeparator.Chars(0) ' Für Konvertierung von Integer Dim integerConverter As TypeConverter = TypeDescriptor.GetConverter(GetType(Integer)) Dim stringArray = stringValue.Split(New Char() {separatorChar}) Dim integerValues As Integer() = New Integer(stringArray.Length - 1) {} For index = 0 To integerValues.Length - 1 integerValues(index) = CInt(integerConverter.ConvertFromString(context, culture, stringArray(index))) Next If integerValues.Length = 1 Then Return New Edges(integerValues(0)) ElseIf integerValues.Length = 2 Then ' Top und Bottom gleich Return New Edges(integerValues(0), integerValues(0), integerValues(1), integerValues(1)) ElseIf integerValues.Length = 4 Then Return New Edges(integerValues(0), integerValues(1), integerValues(2), integerValues(3)) Else Throw New ArgumentException("Ungültige Angabe") End If End Function Public Overrides Function ConvertTo( _ ByVal context As ITypeDescriptorContext, _ ByVal culture As CultureInfo, _ ByVal value As Object, _ ByVal destinationType As Type) As Object If destinationType Is Nothing Then Throw New ArgumentNullException("destinationType") End If If TypeOf value Is Edges Then Dim edgeValue = DirectCast(value, Edges) Dim isAll = (edgeValue.TopLeft = edgeValue.TopRight _ AndAlso edgeValue.TopLeft = edgeValue.BottomLeft _ AndAlso edgeValue.TopLeft = edgeValue.BottomRight) Dim isDual = (edgeValue.TopLeft = edgeValue.TopRight _ AndAlso edgeValue.BottomLeft = edgeValue.BottomRight) If destinationType Is GetType(String) Then If (culture Is Nothing) Then culture = CultureInfo.CurrentCulture End If Dim separator As String = culture.TextInfo.ListSeparator(0) & " " Dim integerConverter As TypeConverter = TypeDescriptor.GetConverter(GetType(Integer)) Dim stringArray As String() = New String() { _ integerConverter.ConvertToString(context, culture, edgeValue.TopLeft), _ integerConverter.ConvertToString(context, culture, edgeValue.TopRight), _ integerConverter.ConvertToString(context, culture, edgeValue.BottomLeft), _ integerConverter.ConvertToString(context, culture, edgeValue.BottomRight)} If isAll Then Return stringArray(0) ElseIf isDual Then Return String.Join(separator, stringArray(0), stringArray(2)) Else Return String.Join(separator, stringArray) End If ElseIf destinationType Is GetType(InstanceDescriptor) Then If isAll Then Return New InstanceDescriptor(GetType(Edges).GetConstructor( _ New Type() {GetType(Integer)}), New Object() {edgeValue.TopLeft}) Else Return New InstanceDescriptor(GetType(Edges).GetConstructor( _ New Type() {GetType(Integer), GetType(Integer), GetType(Integer), GetType(Integer)}), _ New Object() {edgeValue.TopLeft, edgeValue.TopRight, edgeValue.BottomLeft, edgeValue.BottomRight}) End If End If End If Return MyBase.ConvertTo(context, culture, value, destinationType) End Function Public Overrides Function GetCreateInstanceSupported(ByVal context As ITypeDescriptorContext) As Boolean Return True End Function Public Overrides Function CreateInstance(ByVal context As ITypeDescriptorContext, _ ByVal propertyValues As IDictionary) As Object If context Is Nothing Then Throw New ArgumentNullException("context") End If If propertyValues Is Nothing Then Throw New ArgumentNullException("propertyValues") End If Dim value As Edges = DirectCast(context.PropertyDescriptor.GetValue(context.Instance), Edges) Return New Edges( _ CInt(propertyValues.Item("TopLeft")), _ CInt(propertyValues.Item("TopRight")), _ CInt(propertyValues.Item("BottomLeft")), _ CInt(propertyValues.Item("BottomRight"))) End Function Public Overrides Function GetPropertiesSupported(ByVal context As ITypeDescriptorContext) As Boolean Return True End Function Public Overrides Function GetProperties(ByVal context As ITypeDescriptorContext, _ ByVal value As Object, ByVal attributes As Attribute()) As PropertyDescriptorCollection Return TypeDescriptor.GetProperties(GetType(Edges), attributes).Sort( _ New String() {"TopLeft", "TopRight", "BottomLeft", "BottomRight"}) End Function End Class
Darin u. a. gezeigt wie man 1, 2 oder 4 Werte anzeigen/konvertieren
und den Designer bei der Anzeige von Eigenschaften beeinflussen kann.Gruß Elmar
- Als Antwort markiert Robert BreitenhoferModerator Montag, 20. September 2010 15:33
-
Hallo Elmar,
ich danke Dir erstmal recht herzlich für die Mühe die Du Dir mit meinem CodeSchnipsel gemacht hast und natürlich auch die sehr ausführlichen Erklärungen. Die Lösung mit ShouldSerializeRadius() wende ich auch im Augenblick an. Allerdings dachte ich, das es doch noch einen anderen Weg für DefaultValue() geben müßte. Denn beispielsweise folgendes Beispiel kommt hier ohne die Definition von ShouldSerializeXXXX aus.
Gruß FrankPrivate _Font As New System.Drawing.Font("Microsoft Sans Serif", 8.25) <Localizable(True), Category("Text"), DefaultValue(GetType(Font), "Microsoft Sans Serif, 8.25pt"), XmlIgnore()> _ Public Property Font() As Font Get Return _Font End Get Set(ByVal Value As Font) _Font = Value MyBase.OnPropertyChanged("Font") End Set End Property
-
Hallo Frank,
kann man zwar machen, hier wäre das:
da der Wert vom TypeConverter mit InvariantCulture ausgewertet wird -<DefaultValue(GetType(Edges), "1, 2, 3, 4")> _
Listentrenner ist dort ein Komma.Meiner Erfahrung nach klappt es jedoch mit Anzeige und Serialisierung nicht mmer zuverlässig.
Gerade noch kurz für VS2010 getestet und es scheint sich nichts geändert zu haben:
Verzichtet man auf die zusätzlichen Methoden und verwendet nur DefaultValue,
wird der Wert fett angezeigt.
Führt man ein Reset (via Eigenschaftsfenster) aus, ist der Wert kurzzeitig "normal",
nach dem nächsten Laden aber wieder fett.Auch das Nicht-Serialisieren des Standardwertes klappt dabei nicht immer zuverlässig,
was dann den Code (in InitalizeComponent etc) aufbläht.Deswegen verwende ich durchweg die zwei Methoden mehr, wenn es mehr als
ein einfacher Wert ist.Gruß Elmar
-
Hallo Elmar,
die ChangedNotification funktioniert jetzt völlig einwandfrei. Allerdings grübel ich noch an dem DefaultValue Problem. Könnte es sein, das ich zum diesem Zweck die Function Equals überschreiben müßte. Ich denke mal für die Auswertung von DefaultValue erzeugt das System doch sicherlich eine weitere Instanz von Edges und vergleicht diese dann mit der vorhandenen Instanz. Somit sind diese ja dann immer unterschiedlich. Wenn ich jetzt Equals() überschreibe wie kann ich dann die Wandlungsroutinen im TypeConverter nutzen. Sorry wenn ich vieleicht nerve, aber Du besitzt so ein enormes Wissen und das nutze ich natürlich gern. Falls Du die Pappen dick hast einfach nicht mehr darauf Antworten. Ansonsten nochmals Dank.
Gruß Frank
-
Hallo Frank,
kann man schon, nur ist das Frage der Abwägung, wo man die zusätzlichen Zeilen Code schreibt,
braucht man den Typ nur einige Male, macht es eher mehr Arbeit.
Zudem missfallen mir persönlich Attribute mit String-Angaben, da sich leichter Fehler einschleichen.Macht man daraus ein Structure (vermeidet damit Nothing Prüfungen)
und packt Equals, GetHashCode und Vergleiche hinzu, wird man es los:<TypeConverter(GetType(EdgesConverter)), Serializable()> _ Public Structure Edges ' ... Rest wie oben Public Overrides Function Equals(ByVal other As Object) As Boolean Return TypeOf other Is Edges AndAlso Me = CType(other, Edges) End Function Public Overrides Function GetHashCode() As Integer Return (Me._TopLeft Xor Me._TopRight) << 16 Or (Me._BottomLeft Xor Me._BottomRight) End Function Public Shared Operator =(ByVal left As Edges, ByVal right As Edges) As Boolean Return (left._TopLeft = right._TopLeft AndAlso _ left._TopRight = right._TopRight AndAlso _ left._BottomLeft = right._BottomLeft AndAlso _ left._BottomRight = right._BottomRight) End Operator Public Shared Operator <>(ByVal left As Edges, ByVal right As Edges) As Boolean Return Not (left = right) End Operator End Structure
Allerdings habe ich den Designer (getestet in einem Panel) trotzdem dabei "erwischt",
dass er die Anzeige mal fett darstellt (bisher war es nach Neuladen weg).Umgekehrt: Will man Entwicklungszeit sparen, könnte man z. B. die Padding Struktur verwenden,
die erfüllt einen ähnlichen Zweck (und kann sogar ein, zwei Dinge menr).
Zumindest sofern die Verwendung für Radius so zu gestalten ist,
das die anderen Namen keine geistigen Bocksprünge erfordern.Gruß Elmar
- Bearbeitet Elmar BoyeEditor Sonntag, 19. September 2010 19:50 Code Formatierung
-
Hallo Elmar,
ich danke Dir wirklich recht herzlich. Jetzt spielt alles so wie ich mir das gewünscht habe. Ich weis das Beispiel war ein bischen banal, aber es ging mir hier eher um Verständnisfragen in Verbindung mit dem ExpandableObjectConverter bzw. dem Einsatz von TypeConvertern. Ich stimme Dir zu, das drei Zeilen ShouldSerializeXXX das bestimmt schnell gelöst hätten. Ich kann nur sagen "Klassen sollten für die Ewigkeit definiert werden, und wenn nicht dann aber flexibel anpassbar sein". :-)
Und mit diesem Satz möchte ich mich gern verabschieden.
Gruß Frank
- Bearbeitet NachtAktiv Sonntag, 19. September 2010 21:08
-
Hallo Community
hier nun das Endergebnis für Alle. Eine Menge Text um 4 Werte zu verwalten :-)
Imports System.ComponentModel Imports System.ComponentModel.Design.Serialization Imports System.Globalization <TypeConverter(GetType(EdgesConverter)), Serializable()> _ Public Class Edges Implements System.ComponentModel.INotifyPropertyChanged #Region "Konstruktor" ''' <summary>Default Kostruktor</summary> ''' <remarks></remarks> Public Sub New() End Sub ''' <summary>Konstruktor für gleiche Werte.</summary> ''' <remarks>auch vom EdgesConverter verwendet.</remarks> Public Sub New(ByVal all As Integer) Me.TopLeft = all Me.TopRight = all Me.BottomLeft = all Me.BottomRight = all End Sub ''' <summary>Konstruktor für unterschiedliche Werte.</summary> ''' <remarks>auch vom EdgesConverter verwendet.</remarks> Public Sub New(ByVal topLeft As Integer, ByVal topRight As Integer, ByVal bottomLeft As Integer, ByVal bottomRight As Integer) Me.TopLeft = topLeft Me.TopRight = topRight Me.BottomLeft = bottomLeft Me.BottomRight = bottomRight End Sub ''' <summary> ''' Hilfsfunktion für einfache Abfragen ''' </summary> <Browsable(False)> _ Public ReadOnly Property IsEmpty As Boolean Get Return (Me._TopLeft = 0 AndAlso Me._TopRight = 0 AndAlso Me._BottomLeft = 0 AndAlso Me._BottomRight = 0) End Get End Property #End Region #Region "Properties" Private _TopLeft As Integer <DescriptionAttribute("Gibt den Radius der linken oberen Ecke an."), _ DefaultValue(0)> _ Public Property TopLeft() As Integer Get Return _TopLeft End Get Set(ByVal value As Integer) _TopLeft = value OnPropertyChanged("TopLeft") End Set End Property Private _TopRight As Integer <DescriptionAttribute("Gibt den Radius der rechten oberen Ecke an."), _ DefaultValue(0)> _ Public Property TopRight() As Integer Get Return _TopRight End Get Set(ByVal value As Integer) _TopRight = value OnPropertyChanged("TopRight") End Set End Property Private _BottomLeft As Integer <DescriptionAttribute("Gibt den Radius der linken unteren Ecke an."), DefaultValue(0)> _ Public Property BottomLeft() As Integer Get Return _BottomLeft End Get Set(ByVal value As Integer) _BottomLeft = value OnPropertyChanged("BottomLeft") End Set End Property Private _BottomRight As Integer <DescriptionAttribute("Gibt den Radius der rechten unteren Ecke an."), DefaultValue(0)> _ Public Property BottomRight() As Integer Get Return _BottomRight End Get Set(ByVal value As Integer) _BottomRight = value OnPropertyChanged("BottomRight") End Set End Property #End Region #Region "Events / Eventhandler" Protected Overridable Sub OnPropertyChanged(ByVal propertyName As String) RaiseEvent PropertyChanged(Me, New PropertyChangedEventArgs(propertyName)) End Sub <NonSerialized()> _ Public Event PropertyChanged(ByVal sender As Object, ByVal e As System.ComponentModel.PropertyChangedEventArgs) _ Implements System.ComponentModel.INotifyPropertyChanged.PropertyChanged #End Region #Region "Methoden" Public Overrides Function Equals(ByVal other As Object) As Boolean Return TypeOf other Is Edges AndAlso Me = CType(other, Edges) End Function Public Overrides Function GetHashCode() As Integer Return (Me._TopLeft Xor Me._TopRight) << 16 Or (Me._BottomLeft Xor Me._BottomRight) End Function Public Shared Operator =(ByVal left As Edges, ByVal right As Edges) As Boolean Return (left._TopLeft = right._TopLeft AndAlso _ left._TopRight = right._TopRight AndAlso _ left._BottomLeft = right._BottomLeft AndAlso _ left._BottomRight = right._BottomRight) End Operator Public Shared Operator <>(ByVal left As Edges, ByVal right As Edges) As Boolean Return Not (left = right) End Operator #End Region End Class Public Class EdgesConverter Inherits TypeConverter #Region "Methoden" ''' <summary> ''' CanConvertFrom ''' </summary> ''' <param name="context"></param> ''' <param name="sourceType"></param> ''' <returns></returns> ''' <remarks></remarks> Public Overrides Function CanConvertFrom(ByVal context As ITypeDescriptorContext, ByVal sourceType As Type) As Boolean Return sourceType Is GetType(String) OrElse MyBase.CanConvertFrom(context, sourceType) End Function ''' <summary> ''' CanConvertTo ''' </summary> ''' <param name="context"></param> ''' <param name="destinationType"></param> ''' <returns></returns> ''' <remarks></remarks> Public Overrides Function CanConvertTo(ByVal context As ITypeDescriptorContext, _ ByVal destinationType As Type) As Boolean Return destinationType Is GetType(InstanceDescriptor) OrElse MyBase.CanConvertTo(context, destinationType) End Function ''' <summary> ''' ConvertFrom ''' </summary> ''' <param name="context"></param> ''' <param name="culture"></param> ''' <param name="value"></param> ''' <returns></returns> ''' <remarks></remarks> Public Overrides Function ConvertFrom(ByVal context As ITypeDescriptorContext, _ ByVal culture As CultureInfo, _ ByVal value As Object) As Object ' Kein String dann Standardbehandlung Dim stringValue As String = TryCast(value, String) If stringValue IsNot Nothing Then stringValue = stringValue.Trim() End If If String.IsNullOrEmpty(stringValue) Then Return MyBase.ConvertFrom(context, culture, value) End If If culture Is Nothing Then ' aktuelle Kultur als Vorgabe culture = CultureInfo.CurrentCulture End If ' Trennzeichen lt. Cultur Dim separatorChar As Char = culture.TextInfo.ListSeparator.Chars(0) ' Für Konvertierung von Integer Dim integerConverter As TypeConverter = TypeDescriptor.GetConverter(GetType(Integer)) Dim stringArray As String() = stringValue.Split(New Char() {separatorChar}) Dim integerValues As Integer() = New Integer(stringArray.Length - 1) {} For index As Integer = 0 To integerValues.Length - 1 integerValues(index) = CInt(integerConverter.ConvertFromString(context, culture, stringArray(index))) Next If integerValues.Length = 1 Then Return New Edges(integerValues(0)) ElseIf integerValues.Length = 2 Then ' Top und Bottom gleich Return New Edges(integerValues(0), integerValues(0), integerValues(1), integerValues(1)) ElseIf integerValues.Length = 4 Then Return New Edges(integerValues(0), integerValues(1), integerValues(2), integerValues(3)) Else Throw New ArgumentException("Ungültige Angabe") End If End Function ''' <summary> ''' ConvertTo ''' </summary> ''' <param name="context"></param> ''' <param name="culture"></param> ''' <param name="value"></param> ''' <param name="destinationType"></param> ''' <returns></returns> ''' <remarks></remarks> Public Overrides Function ConvertTo( _ ByVal context As ITypeDescriptorContext, _ ByVal culture As CultureInfo, _ ByVal value As Object, _ ByVal destinationType As Type) As Object If destinationType Is Nothing Then Throw New ArgumentNullException("destinationType") End If If TypeOf value Is Edges Then Dim edgeValue As Edges = DirectCast(value, Edges) Dim isAll As Boolean = (edgeValue.TopLeft = edgeValue.TopRight _ AndAlso edgeValue.TopLeft = edgeValue.BottomLeft _ AndAlso edgeValue.TopLeft = edgeValue.BottomRight) Dim isDual As Boolean = (edgeValue.TopLeft = edgeValue.TopRight _ AndAlso edgeValue.BottomLeft = edgeValue.BottomRight) If destinationType Is GetType(String) Then If (culture Is Nothing) Then culture = CultureInfo.CurrentCulture End If Dim separator As String = culture.TextInfo.ListSeparator(0) & " " Dim integerConverter As TypeConverter = TypeDescriptor.GetConverter(GetType(Integer)) Dim stringArray As String() = New String() { _ integerConverter.ConvertToString(context, culture, edgeValue.TopLeft), _ integerConverter.ConvertToString(context, culture, edgeValue.TopRight), _ integerConverter.ConvertToString(context, culture, edgeValue.BottomLeft), _ integerConverter.ConvertToString(context, culture, edgeValue.BottomRight)} If isAll Then Return stringArray(0) ElseIf isDual Then Return String.Join(separator, {stringArray(0), stringArray(2)}) Else Return String.Join(separator, stringArray) End If ElseIf destinationType Is GetType(InstanceDescriptor) Then If isAll Then Return New InstanceDescriptor(GetType(Edges).GetConstructor( _ New Type() {GetType(Integer)}), New Object() {edgeValue.TopLeft}) Else Return New InstanceDescriptor(GetType(Edges).GetConstructor( _ New Type() {GetType(Integer), GetType(Integer), GetType(Integer), GetType(Integer)}), _ New Object() {edgeValue.TopLeft, edgeValue.TopRight, edgeValue.BottomLeft, edgeValue.BottomRight}) End If End If End If Return MyBase.ConvertTo(context, culture, value, destinationType) End Function ''' <summary> ''' GetCreateInstanceSupported ''' </summary> ''' <param name="context"></param> ''' <returns></returns> ''' <remarks></remarks> Public Overrides Function GetCreateInstanceSupported(ByVal context As ITypeDescriptorContext) As Boolean Return True End Function ''' <summary> ''' CreateInstance ''' </summary> ''' <param name="context"></param> ''' <param name="propertyValues"></param> ''' <returns></returns> ''' <remarks></remarks> Public Overrides Function CreateInstance(ByVal context As ITypeDescriptorContext, _ ByVal propertyValues As IDictionary) As Object If context Is Nothing Then Throw New ArgumentNullException("context") End If If propertyValues Is Nothing Then Throw New ArgumentNullException("propertyValues") End If Dim value As Edges = DirectCast(context.PropertyDescriptor.GetValue(context.Instance), Edges) Return New Edges( _ CInt(propertyValues.Item("TopLeft")), _ CInt(propertyValues.Item("TopRight")), _ CInt(propertyValues.Item("BottomLeft")), _ CInt(propertyValues.Item("BottomRight"))) End Function ''' <summary> ''' GetPropertiesSupported ''' </summary> ''' <param name="context"></param> ''' <returns></returns> ''' <remarks></remarks> Public Overrides Function GetPropertiesSupported(ByVal context As ITypeDescriptorContext) As Boolean Return True End Function ''' <summary> ''' GetProperties ''' </summary> ''' <param name="context"></param> ''' <param name="value"></param> ''' <param name="attributes"></param> ''' <returns></returns> ''' <remarks></remarks> Public Overrides Function GetProperties(ByVal context As ITypeDescriptorContext, _ ByVal value As Object, ByVal attributes As Attribute()) As PropertyDescriptorCollection Return TypeDescriptor.GetProperties(GetType(Edges), attributes).Sort( _ New String() {"TopLeft", "TopRight", "BottomLeft", "BottomRight"}) End Function #End Region End Class
Gruß Frank
- Als Antwort markiert NachtAktiv Sonntag, 19. September 2010 21:31
-
Hallo Frank,
die letzten Erweiterungen setzen voraus, dass es sich um einen Werttyp (Structure vs Class ) handelt,
sonst muß Equals, = und <> anders implementiert werden, da auch Nothing möglich ist.Gruß Elmar
-
Danke Elmar,
Dann haben wir doch aus dem Würfel eine Kugel gefeilt.
Gruß Frank
Public Shared Operator =(ByVal left As Edges, ByVal right As Edges) As Boolean If (left Is Nothing) And (right Is Nothing) Then Return True Else If (left Is Nothing) Or (right Is Nothing) Then Return False Else Return (left._TopLeft = right._TopLeft AndAlso _ left._TopRight = right._TopRight AndAlso _ left._BottomLeft = right._BottomLeft AndAlso _ left._BottomRight = right._BottomRight) End If End If End Operator