Nitroxis's picture

Vector / Color classes and the .net PropertyGrid Control

Hi.

I'm currently programming an editor for a project made with OpenTK. The editor is using the .net PropertyGrid-Control to edit an object which contains vector and color properties such as Position, Velocity, etc. But unfortunately I can't edit or expand Vector2, Vector3 and Color4 properties. Is there a way to make them editable? Maybe with TypeConverters or something?

Thanks in advance.


Comments

Comment viewing options

Select your preferred way to display the comments and click "Save settings" to activate your changes.
c2woody's picture

You'll need a TypeConverter (think you have to add this to the OpenTK sources and cannot extend this using precompiled code), but can't tell if there's an easier way to edit such structs with a property view.

Check for example
http://msdn.microsoft.com/en-us/library/Aa302334
(has a small section about a TypeConverter for a custom struct) and similar, one reference mentioned VG.net as good example for property view enabled components.

the Fiddler's picture

Patches gladly accepted.

Nitroxis's picture

Ok, I made a generic converter for the vector classes. They are now expandable in the PropertyGrid. But the problem is, that I only see the properties like Length, Xy, etc. and not the fields X, Y, Z. Applying the Browsable-Attribute to them doesn't help. Is it impossible to browse Fields in PropertyGrids?

c2woody's picture

Don't know if this is helpful for you, but there are some examples about XNA Vector extensions like
http://paul.kinlan.me/xna-vector2-type-converter

Nitroxis's picture

Yes, that's helpful. Thanks.
Edit: I didn't fix the problem :-(

FrankHileman's picture

We found we had to use properties instead of fields. That article is the correct one for learning TypeConverters.

Nitroxis's picture

I tried to override the function GetProperties of ExpandableObjectConverter. This function is returning a PropertyDescriptorCollection. So I made a class that inherits the PropertyDescriptor. In the PropertyDescriptor I can override things like SetValue and GetValue. GetValue works so far, but the SetValue method isn't working, which calls FieldInfo.SetValue. No exceptions, no errors. It's simply not setting the value.

My code so far:

Imports System.Reflection
 
Public Class Vector3TypeConverter
    Inherits ExpandableObjectConverter
 
 
    Public Overrides Function CanConvertTo(ByVal context As System.ComponentModel.ITypeDescriptorContext, ByVal destinationType As System.Type) As Boolean
        If destinationType Is GetType(Vector3) Then
            Return True
        End If
 
        Return MyBase.CanConvertTo(context, destinationType)
    End Function
    Public 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(String) AndAlso value.GetType() Is GetType(Vector3) Then
            Return value.ToString()
        End If
 
        Return MyBase.ConvertTo(context, culture, value, destinationType)
    End Function
 
    Public 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 Overrides Function ConvertFrom(ByVal context As System.ComponentModel.ITypeDescriptorContext, ByVal culture As System.Globalization.CultureInfo, ByVal value As Object) As Object
        Return MyBase.ConvertFrom(context, culture, value)
    End Function
 
    Public Overrides Function CreateInstance(ByVal context As System.ComponentModel.ITypeDescriptorContext, ByVal propertyValues As System.Collections.IDictionary) As Object
        Return New Vector3(propertyValues("X"), propertyValues("Y"), propertyValues("Z"))
    End Function
 
    Public Overrides Function GetProperties(ByVal Context As System.ComponentModel.ITypeDescriptorContext, ByVal Value As Object, ByVal Attributes() As System.Attribute) As System.ComponentModel.PropertyDescriptorCollection
        Dim Properties As New List(Of PropertyDescriptor)
        Dim ValueType As Type = Value.GetType()
        Dim ValueFields() As FieldInfo = ValueType.GetFields()
 
        For Each Field As FieldInfo In ValueFields
            If Field.Name = "X" Or Field.Name = "Y" Or Field.Name = "Z" Then
                Dim Descriptor As New FieldPropertyDescriptor(GetType(Vector3), Field)
                Properties.Add(Descriptor)
            End If
        Next
 
        Return New PropertyDescriptorCollection(Properties.ToArray())
    End Function
    Public Overrides Function GetPropertiesSupported(ByVal context As System.ComponentModel.ITypeDescriptorContext) As Boolean
        Return True
    End Function
End Class
Public Class FieldPropertyDescriptor
    Inherits PropertyDescriptor
 
    Private MyFieldInfo As FieldInfo
    Private MyCompontentType As Type
    Private MyDefaultValue As Object
 
    Public ReadOnly Property FieldInfo As FieldInfo
        Get
            Return Me.MyFieldInfo
        End Get
    End Property
    Public ReadOnly Property DefaultValue As Object
        Get
            Return Me.MyDefaultValue
        End Get
    End Property
 
    Public Sub New(ByVal ComponentType As Type, ByVal FieldInfo As FieldInfo)
        MyBase.New(FieldInfo.Name, New Attribute() {New DescriptionAttribute("LOOOOOL")})
        Me.MyFieldInfo = FieldInfo
        Me.MyDefaultValue = Nothing
        Me.MyCompontentType = ComponentType
    End Sub
    Public Sub New(ByVal ComponentType As Type, ByVal FieldInfo As FieldInfo, ByVal DefaultValue As Object)
        Me.New(ComponentType, FieldInfo)
        Me.MyDefaultValue = DefaultValue
    End Sub
 
    Public Overrides Function CanResetValue(ByVal component As Object) As Boolean
        Return True
    End Function
 
    Public Overrides ReadOnly Property ComponentType As System.Type
        Get
            Return Me.MyCompontentType
        End Get
    End Property
 
    Public Overrides Function GetValue(ByVal component As Object) As Object
        Return Me.MyFieldInfo.GetValue(component)
    End Function
 
    Public Overrides ReadOnly Property IsReadOnly As Boolean
        Get
            Return False
        End Get
    End Property
 
    Public Overrides ReadOnly Property PropertyType As System.Type
        Get
            Return Me.MyFieldInfo.FieldType
        End Get
    End Property
 
    Public Overrides Sub ResetValue(ByVal component As Object)
        Me.MyFieldInfo.SetValue(component, Me.DefaultValue)
    End Sub
 
    Public Overrides Sub SetValue(ByVal component As Object, ByVal value As Object)
        Me.MyFieldInfo.SetValue(component, CSng(value))
    End Sub
 
    Public Overrides Function ShouldSerializeValue(ByVal component As Object) As Boolean
        Return False
    End Function
End Class

Edit: Fixed it now. SetValue won't set values in structures.

Nitroxis's picture

Okay, I made a patch which contains TypeConverters for Vector2, Vector3, Vector4 and Color4 classes: http://pastebin.com/NAW47k3F
Edit: Converters.cs is using the Microsoft.VisualBasic namespace. I think that could be removed. :-S

FrankHileman's picture

I recommend to cache the return values of the GetProperties methods in static fields, and return those instead. This works because the properties don't actually change per instance.

vicviper's picture

There's yet another nifty trick:

Sometimes when debugging, you need to know the inside values of an object; the usual way is to hover it, so a menu pops up and you can browse its properties.

But this is slow, there's a better way:

[System.Diagnostics.DebuggerDisplay("x:{X} y:{Y} z:{Z} len:{Math.Sqrt(X*X+Y*Y+Z*Z)}")]  // I added the length of the vector to show what can be done, of course, it could grab it from vector3 own length property
public struct Vector3
{
public float X,Y,Z;
}

With this, you just need to hover over a vector3 object, and you immediately see all the vector values without the need to browse inside the properties.

V