Grouping Extender Provider properties in a Tree View manner

Hello All,

With regular properties, you can apply a TypeConverter to group related controls together in a TreeView manner (eg they display in the PropertyGrid with a plus sign that you can click to expand the list). This helps to organize related properties together regardless of property names.

I would like to do the same thing with Extender Provider properties but have not been able to figure out how to do so. I am assuming (I know, I know) that this is because Extender Provider properties are not really implemented as properties but as paired GetPropertyName/SetPropertyName methods.

A couple klugy work-a-rounds are possible: 1) Apply a Category attribute so that they are grouped together when viewed by Category or 2) use a common prefix to force them to be grouped together in the Property Grid. Neither of these options is desirable for obvious reasons.

Am I missing anything that would resolve this problem for me Thanks in advance!



Answer this question

Grouping Extender Provider properties in a Tree View manner

  • Kryor

    Big SmileMany, many thanks! I have not yet had a chance to digest your example, I just pasted it into a test solution and that is exactly what I am trying to do. You have helped me take this to the next level.

    If you would have any interest in beta testing my extended error provider, drop me an email at j2associates_NO_Spam@yahoo.com (minus the spam blocker).

    Thanks again and have a great day!

  • LFDIII

    Hello Mick,

    I'm not sure I follow you. As an example, let's say I wanted to write an Extender Provider to add a boolean IsRequired property. The implementation of an Extender Provider requires that you use GetIsRequired and SetIsRequired as methods instead of properties. I'm not seeing how a separate class could be pulled into the Extender Provider to have it automatically be added to different controls (eg like a ToolTip). Thanks again for your response!

  • rzddr

    Maybe I've got the wrong end of the stick, but I was under the impression you want to supply several properties through one extender and have them grouped together in much the same way as say a Font property.

    So if you create a class that contains all your properties you can then simply return an instance of that class in your extended property, although you'll need a TypeConverter for the class in order to Serialize it. So you would end up with something like:

    Public Class MyClass
      ...
      public Property Prop1() As String
       ... 
      End Property

      Public Property Prop2() As String
        ...
      End Property

    End Class

    Public Class MyExtender
        Implements IExtenderProvider
        ...
        Public Sub SetMyGroupedProperty(target As Control, value As MyClass)
           ...
        End Sub

       Public Function GetMyGroupedProperty(target As Control) As MyClass
         ...
       End Function
       ...
    End Class

  • chanmy8

    That was a deliberate feature that I added to the class showing how to type a comma delimited value to change the property.

    If you don't want it then simply remove the CanConvertFrom() and ConvertFrom() methods and return String.Empty in the ConvertTo() method if the destinationType is a string.

  • Kevin Kwan

    Create a class with all the relevant properties and then create the ExtenderProvider to extend a property of that class rather than several different properties. You'll need to create a custom TypeConverter so that the property is serialized.
  • Michael Hansen

    Hello Mick,

    You are close. Actually, I want to be able to group all of my extended properties in one place in a TreeView node style. That way they are all grouped together in one place in the PropertyGrid instead of being scattered throughout the list.

    As an example, let's say I have a property IsRequired with an associated RequiredText property which allows me to specify the message which will be displayed by the ErrorProvider.

    I can group them together in Category mode by applying the Category attribute to them, but what I really want is for them to be displayed under a Required node which would open up when clicked to display IsRequired and RequiredText, similarly to the way Location and Size work on a form. When you click Size, it opens up to display a Height and Width properties.

    In your example above, where do you instantiate my class When I tried something similar, the properties did not show up in the various controls.

    Thanks again for your responses!

  • Juan Jose Obregon

    The above code works beautifully.

    One additional thing is happening that I can't figure out. False is displayed next to the node + sign. Other times when I have used custom TypeConverters with a node view, I have overloaded or overwritten the ToString property to return String.Empty. That does not appear to work in this case, nor does reversing the property order so the string property is first. Any ideas and/or suggestions would be greatly appreciated.

  • NeilMaclean

    Sorry for the slow reply, but I've been busy.

    Any way, the following is a basic VB example showing my approach to this solution. The key to getting this working is in supplying a TypeConverter so that VS knows how to serialize the custom class.

    Note that there is no error checking in this example.



    Imports System.ComponentModel
    Imports System.ComponentModel.Design.Serialization
    Imports System.Reflection

    <ProvideProperty("Required", GetType(Button))> _
    Public Class TestExtender
        Inherits System.ComponentModel.Component
        Implements IExtenderProvider

    #Region " Component Designer generated code "
        'Standard Code cut for posting
    #End Region

        Public Function CanExtend(ByVal extendee As Object) As Boolean Implements System.ComponentModel.IExtenderProvider.CanExtend
            Return TypeOf extendee Is Button
        End Function

        Private RequiredProps As New Hashtable

        <DesignerSerializationVisibility(DesignerSerializationVisibility.Content)> _
        Public Sub SetRequired(ByVal target As Button, ByVal value As Requirements)
            If RequiredProps.Contains(target) Then
                RequiredProps.Remove(target)
            End If
            RequiredProps.Add(target, value)
        End Sub

        Public Function GetRequired(ByVal target As Button) As Requirements
            If RequiredProps.Contains(target) Then
                Return CType(RequiredProps(target), Requirements)
            End If
            RequiredProps.Add(target, New Requirements(False, String.Empty))
        End Function

    End Class

    <TypeConverter(GetType(RequirementsConverter))> _
    Public Class Requirements

        Public Sub New(ByVal Reqd As Boolean, ByVal ReqdTxt As String)
            MyBase.new()
            m_IsRequired = Reqd
            m_RequiredText = ReqdTxt
        End Sub

        Private m_IsRequired As Boolean
        Private m_RequiredText As String

        Public Property IsRequired() As Boolean
            Get
                Return m_IsRequired
            End Get
            Set(ByVal Value As Boolean)
                m_IsRequired = Value
            End Set
        End Property

        Public Property RequiredText() As String
            Get
                Return m_RequiredText
            End Get
            Set(ByVal Value As String)
                m_RequiredText = Value
            End Set
        End Property

    End Class

    Public Class RequirementsConverter
        Inherits ExpandableObjectConverter

        Public Overloads Overrides Function CanConvertTo(ByVal context As System.ComponentModel.ITypeDescriptorContext, ByVal destinationType As System.Type) As Boolean
            Return destinationType Is GetType(InstanceDescriptor) OrElse _
                   destinationType Is GetType(String)
        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
            Dim req As Requirements = CType(value, Requirements)
            If destinationType Is GetType(String) Then
                Dim returnString As String = req.IsRequired.ToString
                If req.RequiredText.Length > 0 Then
                    returnString += "," & req.RequiredText
                End If
                Return returnString
            ElseIf destinationType Is GetType(InstanceDescriptor) Then
                Dim ci As ConstructorInfo = GetType(Requirements).GetConstructor(New Type() {GetType(Boolean), GetType(String)})
                Return New InstanceDescriptor(ci, New Object() {req.IsRequired, req.RequiredText})
            End If
            Return MyBase.ConvertTo(context, culture, value, destinationType)
        End Function

        Public Overloads Overrides Function CanConvertFrom(ByVal context As System.ComponentModel.ITypeDescriptorContext, ByVal sourceType As System.Type) As Boolean
            Return sourceType Is GetType(String)
        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
            Dim str() As String = CType(value, String).Split(","c)
            Return New Requirements(str(0) = Boolean.TrueString, str(1))
        End Function

    End Class

     


  • Grouping Extender Provider properties in a Tree View manner