Search code examples
.netvb.netpropertiespropertygrid

PropertyGrid Not Updating List of String


I'm having a little trouble with a PropertyGrid.

I have set the selected object to a class containing various properties and 90% of them work correctly but for any where the property is a List(Of String) it doesn't appear to call the Set part of the property.

The class I have is as follows (shortened version):

Public Class VideosProperties
    Public Property EpisodeColorOdd As Color
        Get
            Return Videos_EpisodeColorOdd
        End Get
        Set(value As Color)
            Videos_EpisodeColorOdd = value
        End Set
    End Property

    <Editor("System.Windows.Forms.Design.StringCollectionEditor, System.Design, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a",
    "System.Drawing.Design.UITypeEditor,System.Drawing, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a")>
    Public Property Extensions As List(Of String)
        Get
            Return Videos_Extensions
        End Get
        Set(value As List(Of String))
            Videos_Extensions = value
        End Set
    End Property
End Class

I have set the SelectedObject to be this class using the following code:

Dim _Properties As Object

_Properties = New VideosProperties

PropertyGrid1.SelectedObject = _Properties

I have Initialized the List using the following code:

Extensions = New List(Of String)

When I run my code, the PropertyGrid shows the EpisodeColorOdd property and this works correctly, but the Extensions property doesn't.

When you edit the Extensions property using the PropertyGrid, it doesn't call the Set(value as List(Of String)) part of the property.

I've had a look around and can't seem to find a solution (I'm probably not looking hard enough).

Can anyone help?

EDIT

Plutonix - Your code doesn't work. I have already copied and pasted into a new project and I get errors.
I get an error at

<Editor("System.Windows.Forms...etc")> 

telling me "Type 'Editor' is not defined", so I Imported "System.ComponentModel" and now get an error saying "Overload resolution failed because no accessible 'New' accepts this number of arguments." at the same point.

If I remove the that part it doesn't work correctly.

I run the code, the form opens, I click on the Foods property and an Editor opens, I click Add and get the error "Constructor on type 'System.String' not found." which I've had before, and I fixed using

<Editor("System.Windows.Forms.Design.StringCollectionEditor, System.Design, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a", "System.Drawing.Design.UITypeEditor,System.Drawing, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a")> 

instead of

<Editor("System.Windows.Forms...etc")>

Now though, when I click on the property, it brings up a different editor and doesn't actually access the Set part of the property. So, I'm back to square one.


Solution

  • I cant be sure but from the code shown, you dont initialize the collection. Both the PropertyGrid and StringCollectionEditor are intended to edit existing properties values. By not initializing the List - which itself is an object - that property container does not exist and neither of them will create it for you.

    Your code shows the getter/setter boilerplate, but nothing about the backing fields.

    Also, since VideosProperties is a Type, you should use it to declare your variable as that Type rather than As Object. By doing that, you are casting the var to the lowest possible form; your properties are only available via late binding which requires Option Strict off.

    Public Class Animal
        ' auto implement props available since VS2010:
        Public Property Name As String
        Public Property Species As String
        Public Property Weight As Double
        Public Property Coloring As Color
    
        ' backing field for the List:
        Private mFoods As List(Of String)
    
        ' this of course would be the same as your code...
        ' no need to repeat
        <Editor("System.Windows.Forms...etc")>
        Public Property Foods As List(Of String)
            Get
                Return mFoods
            End Get
            Set(value As List(Of String))
                ' see notes
            End Set
        End Property
    
        Public Sub New(n As String, s As String)
            Name = n
            Species = s
            ' initialize the List instance:
            mFoods = New List(Of String)
        End Sub
    End Class
    

    The key is that in the constructor (Sub New) you create the list instance to hold the food items, video extensions or whatever. Using it:

    ' class/form level variable declared as Animal NOT Object
    Private A As Animal
    ...
    A = New Animal("Ziggy", "Feline")
    A.Weight = 12.4             ' not possible if A is As Object
    
    pGrid.SelectedObject = A
    

    After the edit, A.Foods will contain whatever was entered via the StringCollectionEditor. If A is declared As Object, the code line to set the Weight will prevent the code from compiling (with Option Strict on). This is because System.Object does not have a Weight property. A As Animal allows the compiler to see and use the properties defined. I suspect you are not using Option Strict.

    If your object has a valid List instance when you send your object to the PropertyGrid and a valid Editor is specified, you'll see this:

    enter image description here

    The PropGrid will display "Collection" indicating it knows what it is, and the "..." indicates the PropGrid has a valid editor associated with it. Proof that it retains the collection:

    lbFoods.Items.AddRange(A.Foods.ToArray)
    ' results in:
    

    enter image description hereenter image description here


    Note that I have nothing in the setter for Foods, yet it works. Collection Editors do not usually pass collection items back en masse via the setter. Instead, they use the Add and/or AddRange method of the collection.

    So, I can omit the setter which also prevents some piece of code from setting the List back to Nothing. Of course, code can change the string items to something else or re order them which also might be bad.

    To avoid this, you might want to use a Collection Class which implements List(of T) or Collection(Of T). This way your code can provide access to items, limit what can be done, and even validate items added.

    See Guidelines for Collections for more tips and information.