I am currently looking into using the PropertyGrid Control for the first time, but am having problems with when it calls the Get and Set methods of the property. Basically, I want to write a wrapper around a set of properties that are defined in some precompiled code to allow the propertygrid to use them. Basically, I am trying to have the propertygrid directly read and write the configuration values in this class.
The problem comes in when I try to create an expandable property. The expansion works fine, but due to the way I wrote the code, the 'set' method never gets run when one of the fields in the expanded view is modified. The set method does run, however, when I edit the field directly (without expanding it).
Now it would seem that this is a limitation of using custom expanded forms, however I also noticed that using the built in System.Drawing.Point class does not have this problem. When using the property grid with this class, expanding the property and editing an expanded form causes the set method of the property to be called immediately.
I am trying to explain this as best I can, but it is rather odd and complex to explain so feel free to ask questions.
The basic buggy behavior in the code below is that when trying to change a field in the expanded 'AngleRange' set of values, the Set method for this property does not get called, so the underlying values (_MILGeometricFinder.Models.Item(_ModelNum).Angle.Value, .NegativeDelta, .PositiveDelta) do not get changed.
On the otherhand in the ModelOrigin Property, any time you change the values of the expanded fields, the set gets called immediately and updates the proper values. As far as I can tell, this must have to do with the way that the System.Drawing.Point class is declared. It seems to have a way of informing the propertygrid control that a set has been called on one of its properties, so the propertygrid should call a set on that particular property.
Here is my code:
Main form code to link the propertygrid in:
PropertyGrid1.SelectedObject = New ModelFinderProperties(_MILGeometricFinder)
Excerpt From the PropertyList that the Propertygrid Reads:
Imports System.ComponentModel
Imports Matrox.ActiveMIL
'''
<DefaultPropertyAttribute("Title")> _
Public Class ModelFinderProperties
'''
Private _MILGeometricFinder As AxMatrox.ActiveMIL.ModelFinder.AxMModelFinder = Nothing
Private _ModelNum As Integer = 1
Public Sub New(ByRef ModelFinder As AxMatrox.ActiveMIL.ModelFinder.AxMModelFinder, Optional ByVal ModelNum As Integer = 1)
_MILGeometricFinder = ModelFinder
_ModelNum = ModelNum
End Sub
<CategoryAttribute("Training Parameters"), _
Browsable(True), _
[ReadOnly](False), _
BindableAttribute(False), _
DefaultValueAttribute("(0.0,0.0)"), _
DesignOnly(False), _
DescriptionAttribute("The (0,0) Point of a found model.")> _
Public Property ModelOrigin() As Point
Get
Return New Point(_MILGeometricFinder.Models.Item(_ModelNum).ReferenceX(), _
_MILGeometricFinder.Models.Item(_ModelNum).ReferenceY())
End Get
Set(ByVal Value As Point)
_MILGeometricFinder.Models.Item(_ModelNum).ReferenceX = Value.X
_MILGeometricFinder.Models.Item(_ModelNum).ReferenceY = Value.Y
End Set
End Property
'''
<CategoryAttribute("Search Parameters"), _
Browsable(True), _
[ReadOnly](False), _
BindableAttribute(False), _
DefaultValueAttribute("(0.0,0.0)"), _
DesignOnly(False), _
DescriptionAttribute("The starting and offset angle ranges to use when searching.")> _
Public Property AngleRange() As RangeCls
Get
With _MILGeometricFinder.Models.Item(_ModelNum).Angle
Return New RangeCls(.Value, .NegativeDelta, .PositiveDelta)
End With
End Get
Set(ByVal newValue As RangeCls)
With _MILGeometricFinder.Models.Item(_ModelNum).Angle
.Value = newValue.Reference
.NegativeDelta = newValue.NegativeDelta
.PositiveDelta = newValue.PositiveDelta
End With
End Set
End Property
End Class
My RangeCls:
Imports System.ComponentModel
<TypeConverter(GetType(RangeClsConverter))> _
Public Class RangeCls
'''
Private _Reference As Double
Private _NegativeDelta As Double
Private _PositiveDelta As Double
'''
Public Sub New(ByVal Reference As Double, ByRef NegativeDelta As Double, ByRef PositiveDelta As Double)
_Reference = Reference
_NegativeDelta = NegativeDelta
_PositiveDelta = PositiveDelta
End Sub
'''
Public Sub New()
Me.new(0, 0, 0)
End Sub
<DescriptionAttribute("Set the reference value.")> _
Public Property Reference() As Double
Get
Return _Reference
End Get
Set(ByVal Value As Double)
_Reference = Value
End Set
End Property
'''
<DescriptionAttribute("Set the positive delta.")> _
Public Property PositiveDelta() As Double
Get
Return _PositiveDelta
End Get
Set(ByVal Value As Double)
_PositiveDelta = Value
End Set
End Property
'''
<DescriptionAttribute("Set the negative delta.")> _
Public Property NegativeDelta() As Double
Get
Return _NegativeDelta
End Get
Set(ByVal Value As Double)
_NegativeDelta = Value
End Set
End Property
End Class
Friend Class RangeClsConverter : Inherits ExpandableObjectConverter
Public Overloads Overrides Function _
CanConvertTo(ByVal context As System.ComponentModel.ITypeDescriptorContext, _
ByVal destinationType As System.Type) As Boolean
If (destinationType Is GetType(RangeCls)) Then
Return True
End If
Return MyBase.CanConvertTo(context, destinationType)
End Function
Public Overloads Overrides Function _
CanConvertFrom(ByVal context As System.ComponentModel.ITypeDescriptorContext, _
ByVal sourceType As System.Type) As Boolean
If (sourceType Is GetType(String)) Then
Return True
End If
Return MyBase.CanConvertFrom(context, sourceType)
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
If TypeOf value Is String Then
Try
Dim s As String = CType(value, String)
Dim rangeParts(3) As String
Dim _RangeCls As RangeCls = New RangeCls
Dim delimiterIndex As Integer
delimiterIndex = InStr(s, "[")
rangeParts(0) = Left(s, delimiterIndex - 1)
s = Mid(s, delimiterIndex + 1)
delimiterIndex = InStr(s, ",")
rangeParts(1) = Left(s, delimiterIndex - 1)
s = Mid(s, delimiterIndex + 1)
delimiterIndex = InStr(s, "]")
rangeParts(2) = Left(s, delimiterIndex - 1)
_RangeCls.Reference = Convert.ToDouble(rangeParts(0))
_RangeCls.NegativeDelta = Convert.ToDouble(rangeParts(1))
_RangeCls.PositiveDelta = Convert.ToDouble(rangeParts(2))
Return _RangeCls
Catch ex As Exception
Throw New ArgumentException("Can not convert '" + _
value + "' to type RangeCls")
End Try
End If
Return MyBase.ConvertFrom(context, culture, value)
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
If (destinationType Is GetType(System.String) AndAlso _
TypeOf value Is RangeCls) Then
Dim _RangeCls As RangeCls = CType(value, RangeCls)
Return _RangeCls.Reference & " [" & _RangeCls.NegativeDelta & _
", " & _RangeCls.PositiveDelta & "]"
End If
Return MyBase.ConvertTo(context, culture, value, destinationType)
End Function
End Class
|