How do I have properties which are objects (not just a string) dropdown in property grid? I got so far, but then got stuck! Consider the code below:
Public Class mOrganisation
Public Property ID as String
Public Property Name as String
End Class
Public Class mSystem
Public Property ID as string
Public Property Name as String
Public Property Developer as mOrganisation
Public Overrides Function ToString() As String
Return Name
End Function
End Class
Public Class mGame
Public Property ID as string
Public Property Name as String
<TypeConverter(GetType(SystemConverter))>
Public Property System as mSystem
End Class
Public Class Main
Public Systems as List(of mSystem) = [...list gatehring code here]
End Class
Public Class SystemConverter
Inherits TypeConverter
Public Overrides Function GetStandardValuesSupported(ByVal context As ITypeDescriptorContext) As Boolean
Return True
End Function
Public Overrides Function GetStandardValuesExclusive(ByVal context As ITypeDescriptorContext) As Boolean
Return False
End Function
Public Overrides Function GetStandardValues(ByVal context As ITypeDescriptorContext) As TypeConverter.StandardValuesCollection
Return New StandardValuesCollection(Main.Systems)
End Function
End Class
mOrgnisation is there just to introduce some complication to the mSystem Class. Now this code does drop down the values:
But when I select a value, I get a PropertyGrid error "Object of type 'System.String' cannot be converted to type 'mSystem'"
This had led me down a rabbit hole, particularly trying to apply various permutations of Convert From
and Convert To
. However, I couldn't find a decent solution. One attempt via ConvertFrom
had the drop down menu loading very slowly, one item at a time (I think it was being fired for each and every item).
I would make a custom UITypeEditor
but I can't find a way to get the PropertyGrid inherent resize method/handle like on the standard dropdown (and tried coding my own resize routine, but proved sticky and flickery I think because of the interaction of PropGrid + the control)
What is the best/most elegant way to achieve this?
Eventually ditched TypeConverter and found a solution. Like most coding nightmares, it was 5 simple lines put in exactly the right place! The key was accessing the PropertyGrid built-in resize handle. You can adjust the code below to include building in handling custom objects, as value
is any object. However, example below is a string, for simplicity:
Public Overrides ReadOnly Property IsDropDownResizable As Boolean
Get
Return True
End Get
End Property
Here's how to achieve:
Step 1: Create a new UITypeEditor
This interfaces the property to the custom editor. value contains the value passed from the property. It can be of any object type. You can therefore pass this to your custom control to inform any editing functions therein.
Create a new class:
Imports System.ComponentModel
Imports System.Drawing.Design
Imports System.Windows.Forms.Design
Public Class TestUIEditor
Inherits UITypeEditor
' Indicate that we display a dropdown.
Public Overrides Function GetEditStyle(ByVal context As ITypeDescriptorContext) _
As UITypeEditorEditStyle
Return UITypeEditorEditStyle.DropDown
End Function
Public Overrides Function EditValue(ByVal context As ITypeDescriptorContext, ByVal provider As IServiceProvider, ByVal value As Object) As Object
' Get an IWindowsFormsEditorService object.
Dim editor_service As IWindowsFormsEditorService = CType(provider.GetService(GetType(IWindowsFormsEditorService)), IWindowsFormsEditorService)
If editor_service Is Nothing Then
Return MyBase.EditValue(context, provider, value)
End If
'Setup the editor
Dim editorService As IWindowsFormsEditorService = Nothing
If provider IsNot Nothing Then
editorService = TryCast(provider.GetService(GetType(IWindowsFormsEditorService)), IWindowsFormsEditorService)
End If
If editorService IsNot Nothing Then
Dim testUI As New TestPropGridUI(editorService)
'Populate the existing value to the dropdown control
testUI.Text = value
'Drop down the control
editorService.DropDownControl(testUI)
'Update property value once dropdown closed
value = testUI.Text
End If
Return value
End Function
Public Overrides ReadOnly Property IsDropDownResizable As Boolean
Get
'Ensures control is resizable
Return True
End Get
End Property
End Class
Step 2: Create your custom editor:
In this example, I have just used a custom class that inherits TextBox . However, you can use any controls, even a custom UserControl.
Imports System.Windows.Forms.Design
Public Class TestPropGridUI
Inherits TextBox
' The editor service displaying this control.
Private m_EditorService As IWindowsFormsEditorService
Public Sub New(ByVal editor_service As IWindowsFormsEditorService)
MyBase.New()
m_EditorService = editor_service
Dock = DockStyle.Fill
End Sub
Public Sub returnPressed(sender As Object, e As KeyEventArgs) Handles Me.KeyDown
'Closes the dropdown when Enter pressed
If e.KeyCode = Keys.Enter Then
m_EditorService.CloseDropDown()
End If
End Sub
End Class
Note m_EditorService.CloseDropDown . Put this wherever you want the dropdown to close itself (e.g. after a value selection).
Step 3: Tell your class property to use the custom editor:
Public Class TestObject
Public Property ID as String
Public Property Quantity as Integer
<Editor(GetType(TestUIEditor), GetType(UITypeEditor))>
Public Property TestText As String
End Class
And that's it!