views:

631

answers:

1

I have an App where I'd like to be able to edit any type (font, colour, point etc.) at run time and use any of the .Net default type editors. (e.g., font/ colour picker).

Rather than re-invent the wheel, I decided to use the property grid control.

If I pass an object of, say font, to the grid, it lists all the fields separately, with no option to open the font picker.

Therefore, I created this generic wrapper class:

Private Class Wrapper(Of T)
    Private _Value As T
    Public Property Value() As T
        Get
            Return Me._Value
        End Get
        Set(ByVal value As T)
            Me._Value = value
        End Set
    End Property

    Public Sub New(ByVal Value As T)
        Me._Value = Value
    End Sub
End Class

Instead of passing a font object to the grid, I pass an instance of the wrapper. The property grid then behaves as I would like.

This works, but the problem is, the object could be of any type and I can't code something like -

Dim MyWrapper = New Wrapper(of T)(myObject).

Basically, the information I have is the type's assembly qualified name and a string representation of the object. I then use a type converter to create the object :

Dim ID As String = "System.Drawing.Font, System.Drawing, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a"
Dim PropertyValue As String = "Arial, 12px, style=Bold, Strikeout"
Dim T As Type = System.Type.GetType(ID)
Dim tc As TypeConverter = TypeDescriptor.GetConverter(T)
Dim o As Object = tc.ConvertFromString(PropertyValue)

If I pass the object to the property grid, it works but it doesn't work if I pass an instance of the wrapper.

I've solved the problem by using reflection.Emit to create a non generic wrapper of the required type on the fly, but I suspect this is over kill.

Any ideas?

ETA:

I had a problem with what do to if I was using the Grid to edit a property, say a Font, that was not already defined.

If i define:

Dim f as Font = Nothing

, and pass that to the wrapper, the property grid displays as expected with (none) and a button with ... to select the font.

My problem was how to do the equivalent of Dim myObject As 'Type' = Nothing, at run-time.

I couldn't find a way to do this, but luckily with the wrapper and my type, it wasn't a problem. I changed Pradeep's code (look at the answers) to :

Dim genericType As Type = GetType(Wrapper(Of ))
Dim specificType As Type = genericType.MakeGenericType(T)
Dim ci As ConstructorInfo = specificType.GetConstructor(New Type() {T})
Dim wrappedObject As Object = ci.Invoke(New Object() {Nothing})
Me.PropertyGrid1.SelectedObject = wrappedObject

Problem solved!

+1  A: 

I think this should work. I've tested it in C# and used a converter to get code in VB.net

This is the code in C#

Type generic = typeof(Wrapper<>);
Type specific = generic.MakeGenericType(o.GetType());
ConstructorInfo ci = specific.GetConstructor(new Type[] { o.GetType() });
object o1 = ci.Invoke(new object[] { o });
propertyGrid1.SelectedObject = o1;

VB.NET

Dim generic As Type =  Type.GetType(Wrapper<>) 
Dim specific As Type =  generic.MakeGenericType(o.GetType()) 
Dim ci As ConstructorInfo =  specific.GetConstructor(New Type() {o.GetType() })
Dim o1 As Object =  ci.Invoke(New Object(){  o })
propertyGrid1.SelectedObject = o1
chikak
Thanks mate, this is spot on. I knew I'd used something like this before but I always forget you can refer to a generic type without the sub type.My 28 lines of emit code is now replaced with 4 :-)Just a slight correction, the first line was converted wrongly, it should read: Dim generic As Type = GetType(Wrapper(Of ))
Jules