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 |
|