Windows Develop Bookmark and Share   
 index > Windows Forms General > Custom control property not available for binding to user settings
 

Custom control property not available for binding to user settings

I am using VB in VS2008 to create a custom GUI control. The control works fine except that two properties of the control will not appear in the list of properties that can be bound to user settings (from the Application Settings / Property binding option in the control properties panel).

The two properties that do not appear are classes defined in the control. The classes are simply a wrapper for three integer values. I have created them as a class rather than three separate properties so that I could build a designer for the class. All this works just fine.

I can find plenty of documentation on what I need to do to enable the control to persist its properties, but this is not what I am trying to achieve. I simply want these two properties to be available in the Application Settings dialog when invoked at design time from the application hosting the control. I want the host application to be able to bind these properties of the control to user settings defined in the host. I suspect the problem is with the class definition in the control because the property type is not available as a type in the Settings page of the host project properties. It seems that there's something I need to add to the class definition to confirm that it really is serializable, although the documentation implies that it should be recognised as serializable becauseall its properties are serializable.

The class is:

    <Serializable()> _
    Public Class PointerSize

        Private myPointerStart As Integer
        Private myPointerEnd As Integer
        Private myPointerWidth As Integer

        Public Sub New(ByVal X As Integer, ByVal Y As Integer, ByVal Z As Integer)
            myPointerStart = X
            myPointerEnd = Y
            myPointerWidth = Z
        End Sub

        <Category("Appearance"), Description("Start point for the pointer"), DefaultValue(40),  Browsable(True)> _
        Public Property PointerStart() As Integer
            Get
                Return myPointerStart
            End Get
            Set(ByVal Value As Integer)
                   myPointerStart = Value
            End Set
        End Property

        <Category("Appearance"), Description("End point of the indicator pointer."),  DefaultValue(70), Browsable(True)> _
        Public Property PointerEnd() As Integer
            Get
                Return myPointerEnd
            End Get
            Set(ByVal Value As Integer)
                    myPointerEnd = Value
            End Set
        End Property

        <Category("Appearance"), Description("Width of the indicator pointer."), DefaultValue(2), Browsable(True)> _
        Public Property PointerWidth() As Integer
            Get
                Return myPointerWidth
            End Get
            Set(ByVal Value As Integer)
                    myPointerWidth = Value
            End Set
        End Property
    End Class

    Friend Class PointerSizeEditor
        Inherits UITypeEditor
        Public Overloads Overrides Function GetEditStyle( _
            ByVal context As System.ComponentModel.ITypeDescriptorContext) _
                As System.Drawing.Design.UITypeEditorEditStyle
            Return UITypeEditorEditStyle.Modal
        End Function

        Public Overloads Overrides Function EditValue( _
            ByVal context As System.ComponentModel.ITypeDescriptorContext, _
            ByVal provider As System.IServiceProvider, _
                ByVal value As Object) As Object
            ' Get an IWindowsFormsEditorService.
            Dim editor_service As IWindowsFormsEditorService = _
                CType(provider.GetService( _
                    GetType(IWindowsFormsEditorService)),  _
                    IWindowsFormsEditorService)
            If editor_service Is Nothing Then Return Nothing
            ' Display the input dialog.
            Dim pointerstart As Integer
            Dim pointerend As Integer
            Dim pointerwidth As Integer
            If value Is Nothing Then
                pointerstart = 40
                pointerend = 70
                pointerwidth = 2
            Else
                Dim this As PointerSize = CType(value, PointerSize)
                pointerstart = this.PointerStart
                pointerend = this.PointerEnd
                pointerwidth = this.PointerWidth
            End If
            Dim dlg As New dlgPointerSizeEditor
            dlg.txtPointerStart.Text = pointerstart.ToString
            dlg.txtPointerEnd.Text = pointerend.ToString
            dlg.txtPointerWidth.Text = pointerwidth.ToString
            ' Display the editor form.
            If editor_service.ShowDialog(dlg) = DialogResult.OK Then
                pointerstart = CInt(Val(dlg.txtPointerStart.Text.Trim))
                pointerend = CInt(Val(dlg.txtPointerEnd.Text.Trim))
                pointerwidth = CInt(Val(dlg.txtPointerWidth.Text.Trim))
                value = New PointerSize(pointerstart, pointerend, pointerwidth)
            End If
            Return value
        End Function
    End Class



SkinBark  Sunday, September 13, 2009 11:47 PM
Hi SkinBark,

I have read your post and I know the class PointerSize is defined in another class. Have you exposed the instance of PointerSize as a public property? If you have exposed that property, it can be seen in the propertygrid of that class's instance's property list.

If I misunderstood you, please feel free to tell me.

Sincerely,
Kira Qian
Please remember to mark the replies as answers if they help and unmark them if they provide no help. Welcome to the All-In-One Code Framework!
Kira Qian  Tuesday, September 15, 2009 8:02 AM
In class GuiGauge I have:

        <Category("Appearance"), _
        Browsable(True), _
        Description("Encapsulates the pointer size properties."), _
        Editor(GetType(PointerSizeEditor), GetType(UITypeEditor))> _
        Public Property Pointer() As PointerSize
            Get
                Pointer = myPointer
            End Get
            Set(ByVal value As PointerSize)
                myPointer = New PointerSize(value.PointerStart, value.PointerEnd, Math.Abs(value.PointerWidth))
                PointerPen.Width = myPointer.PointerWidth)
                NotifyPropertyChanged("Pointer")
                Me.Invalidate()
            End Set
        End Property
Class GuiGauge and class PointerSize are both defined in NameSpace GuiControls. All other properties of class GuiGauge appear in the list in the "Bind Properties to Application Settings"dialog and work correctly when bound to user settings. But property Pointer (and one other defined in exactly the same way) do not appear in the list and cannot be bound to user settings. Property Pointer appears in the property grid for an instance of GuiGauge, can be edited in the UIEditor, and correctly controls the corresponding GUI elements in the control (eg, width of the pen).
SkinBark  Tuesday, September 15, 2009 10:04 PM
Did you ever get an answer to this?   I now have exactly the same problem.  I have brought about a dozen control properties together in three classes, and each class is now a property of the control.  I have created custom editors for each class.  The user now edits these properties as a group in their own editor - a big improvement over a single large list of apparently unrelated properties.   But that means I have lost the ability for the user to have these properties persisted in user settings by nominating the property (the class) in the list.  They were previously available when they were separate properties, and were persisting correctly (they are all simple value types).

The new classes are persisting properly for the custom control settings in the designer, so there is obviously no problem with serializing and deserializing, so why won't they appear in the list as available for binding?  I'm not aware of any special attribute that I used for other properties to get them to appear in the list - it just seems to be automatic for any persistable property.  So what extra do I need to do to get these new properties to also appear?  The only difference between these properties and the ones that appear automatically is they they are defined as classes within the control, so I presume it's something I have to add to the class definition - but what?

If I can't make these classes available to the user for selection as bound properties then I will have to go back to including their components individually in the control's properties.   I can sort of 'group' them by selecting the property name carefully,  but that is not nearly as neat as being able to group them into a single property as a class, and providing a custom editor for the class.
Acamar  Friday, October 02, 2009 2:03 AM
I would say that the problem here is that you haven't provided a TypeConverter .  In my opinion, the property values cannot be serialized because the designer needs constructor semantics.  To provide the semantics, one usually implement a TypeConverter for the specific type.  Basically, you need to provide an InstanceDescriptor when asked for one.

Oh, and most likely a string representation too!  The settings file will not use an InstanceDescriptor to persist the property; the InstanceDescriptor is only used in code (the designer file).  Your TypeConverter probably needs to convert to and from the string datatype.

MCP
webJose  Friday, October 02, 2009 2:16 AM
Thanks for the suggestion. 

I have added typeconverters from and to for String and InstanceDescriptor, including the associated CanConverts and IsValids.    I have also added the TypeConverter attribute for the classes.  This was all relatively straightforward, because the properties are simply a series of integers.

This has given me the string form for the property in the property list, and I have confirmed that I can edit either in the property list or in the custom editor, and the changes are properly reflected in the control.

I now have access to the class type in the Settings page of the application properties. I can create a setting for the type and enter default values, and these defaults appear at runtime after synchronizing.  I can update the settings in the application at runtime and I have confirmed that these are persisted through to user settings, so all that is significant progress. I gather that this depends on the String TypeConverter, so that seems to be working OK.

But design time property settings for these properties are not being retained in the designer!  And I still don't have these properties listed in the Application Settings (Property Binding) dialog.  I suspect this means that the InstanceDescriptor TypeConverter is not working. Is it possible that this was being defaulted when I didn't have a custom typeconverter - now I have one to do the string conversion it will also be used for the InstanceDescriptor conversion, and there's something wrong with it?  AFAICT all this code is identicial for all implementations, other than the types and the specifics of the string conversions.
    Public Class MarkerConverter
        Inherits TypeConverter
        ' Provides a typeconversion for the Marker class.
        ' Overrides the CanConvertFrom method of TypeConverter.
        ' The ITypeDescriptorContext interface provides the context for the
        ' conversion. Typically, this interface is used at design time to 
        ' provide information about the design-time container.
        Public Overloads Overrides Function CanConvertFrom(ByVal context As ITypeDescriptorContext, ByVal sourceType As Type) As Boolean
            If sourceType Is GetType(String) Then
                Return True
            End If
            If sourceType Is GetType(InstanceDescriptor) Then
                Return True
            End If
            Return MyBase.CanConvertFrom(context, sourceType)
        End Function

        Public Overrides Function CanConvertTo(ByVal context As System.ComponentModel.ITypeDescriptorContext, ByVal destinationType As System.Type) As Boolean
            If destinationType Is GetType(String) Then
                Return True
            End If
            If destinationType Is GetType(InstanceDescriptor) Then
                Return True
            End If
            Return MyBase.CanConvertTo(context, destinationType)
        End Function

        ' Overrides the ConvertFrom method of TypeConverter.
        Public Overloads Overrides Function ConvertFrom(ByVal context As ITypeDescriptorContext, ByVal culture As CultureInfo, ByVal value As Object) As Object
            If TypeOf value Is String Then
                Dim v As String() = CStr(value).Split(New Char() {","c})
                Return New Marker(Integer.Parse(v(0)), Integer.Parse(v(1)), Integer.Parse(v(2)))
            End If
            If TypeOf value Is InstanceDescriptor Then
                Return CType(value.invoke, Marker)
            End If
            Return MyBase.ConvertFrom(context, culture, value)
        End Function

        ' Overrides the ConvertTo method of TypeConverter.
        Public Overloads Overrides Function ConvertTo(ByVal context As ITypeDescriptorContext, ByVal culture As CultureInfo, ByVal value As Object, ByVal destinationType As Type) As Object
            If destinationType Is GetType(String) Then
                Return CType(value, Marker).MarkerLength & "," & CType(value, Marker).MarkerHeight & "," & CType(value, Marker).MarkerWidth
            End If
            If destinationType Is GetType(InstanceDescriptor) Then
                Dim ci As ConstructorInfo = GetType(Marker).GetConstructor(New Type() {GetType(Integer), GetType(Integer), GetType(Integer)})
                Dim t As Marker = CType(value, Marker)
                Return New InstanceDescriptor(ci, New Object() {t.MarkerLength, t.MarkerHeight, t.MarkerWidth})
            End If
            Return MyBase.ConvertTo(context, culture, value, destinationType)
        End Function

        Public Overrides Function IsValid(ByVal context As System.ComponentModel.ITypeDescriptorContext, ByVal value As Object) As Boolean
            Dim v As String() = CStr(value).Split(New Char() {","c})
            If v.Length = 3 Then Return True
            Return MyBase.IsValid(context, value)
        End Function
    End Class


 
Acamar  Friday, October 02, 2009 8:11 AM
Your TypeConverter seems to be OK except maybe for IsValid().  I think IsValid() is should have If TypeOf value Is String Then enclosing your string validation.

Other than that, I'd recommend that you debug the design-time behavior to see what is going on with the TypeConverter when used by Visual Studio.
MCP
webJose  Friday, October 02, 2009 12:27 PM
I think this is now complete and correct. I have changed the IsValid as per your suggestion, and for completeness I will also add the IsValid result for the InstanceDescriptor type, although this might already be defaulting to true.

The problem seems to revolve around attributes rather than the supporting methods, and I am still not clear on exactly when the supporting methods are required, but this is the combination that gives me correct persistence of property settings for the control in the designer, correct persistence in user settings for the properties of each control instance in the application, access to the string form of the control values in the properties list, a custom editor for the control property, and the control property available to bind to a user setting in Application Settings (Property Binding).

For the control property Attributes:

        <DesignerSerializationVisibility(DesignerSerializationVisibility.Visible), _
        TypeConverter(GetType(myCustomConverter)), _
        Bindable(True), _
        Editor(GetType(myCustomEditor), GetType(UITypeEditor))> _

For the property class Attributes:

    <Serializable(), _
    TypeConverter(GetType(myCustomConverter))> _

And methods for the property class:

myCustomConverter (A TypeConverter class for types String and InstanceDescriptor)
myCustomEditor (A UITypeEditor class)

It appears that the default functionality provided by the attribute setting
            DesignerSerializationVisibility(DesignerSerializationVisibility.Content
whch is adequate for a property class where all the public properties are simple value variables, disappears as soon as a custom TypeConverter is created.
  • Edited byAcamar Sunday, October 04, 2009 3:45 AMFormatting
  •  
Acamar  Sunday, October 04, 2009 3:37 AM

You can use google to search for other answers

Custom Search

More Threads

• Toolstrip Problems in COM application
• comboBox behavior different in debugger
• Weird - Applying visual styles!
• Changing Custom Control library
• Adding a Handler to Multiple Controls (including child controls)
• Encryption of user settings (user.config)
• Print Background Color
• UI not updating after WCF callback?
• ListBox text format
• When i maximize a form the contents in it are not maximized