RefreshProperties attribute at class level in .net, winforms + incorrectly refreshing property grid
I had a strange problem editing a class in the property grid whereby the property grid would refresh incorrectly.
I managed to reduce the problem down to a class with just two properties. I've included the code at the end to ease explanation.
It basically boils down to a class with two properties. The first of which is expandable (a font). The class itself is expandable and also implements the CreateInstance method in the type converter.
To see the problem, expand the font, edit, say 'Bold', and tab away. Two problems happen:
(1) The second property jumps up and ends up in the expanded font property.
(2) The '-' sign of the expanded font changes to a '+'.
The problem goes away by attaching ResfreshProperties(RefreshProperties.All) to the class.
That's great, but I'd like to understand how it fixed the problem. I've had a look in reflector and can't find any examples of RefreshProperties being attached at the class level.
/// Simple Class
<TypeConverter(GetType(Class1Converter)), _
RefreshProperties(RefreshProperties.All)> _
Public Class Class1
Public Sub New(ByVal font As Font, ByVal image As Image)
Me.New()
Me.Image = image
Me.Font = font
End Sub
Public Sub New()
End Sub
Private _Font As Font = New Font("Arial", 10)
Public Property Font() As Font
Get
Return _Font
End Get
Set(ByVal value As Font)
_Font = value
End Set
End Property
Private _Image As Image
Public Property Image() As Image
Get
Return _Image
End Get
开发者_JAVA百科 Set(ByVal value As Image)
_Image = value
End Set
End Property
End Class
/// Converter for the class
Public Class Class1Converter
Inherits ExpandableObjectConverter
Public Overrides Function GetCreateInstanceSupported(ByVal context As System.ComponentModel.ITypeDescriptorContext) As Boolean
Return True
End Function
Public Overrides Function CreateInstance(ByVal context As System.ComponentModel.ITypeDescriptorContext, ByVal propertyValues As System.Collections.IDictionary) As Object
Dim font As Font = TryCast(propertyValues("Font"), Font)
Dim image As Image = CType(propertyValues("Image"), Image)
Return New Class1(font, image)
End Function
End Class
/// A button to host the class
Public Class MyButton
Inherits Button
Private _C As Class1 = New Class1
Public Property C() As Class1
Get
Return _C
End Get
Set(ByVal value As Class1)
_C = value
End Set
End Property
End Class
It sounds like what's happening is a painting bug in the property grid control. Was the RefreshPropertiesAttribute applied at the property level when the bug occurred? Does the component implement INotifyPropertyChanged?
One reason the problem likely goes away is that applying RefreshPropertiesAttribute at the class level is not the way the attribute is supposed to be used. I'm guessing that by applying it at the class level you're effectively removing it. It looks like they left it set as AttributeTargets.All but clearly that's not by design.
RefreshProperties is kind of a cop-out attribute. It tells the designer that when the value of this property changes (via the property grid), go re-query every other property on this class. Obviously that's kind of wasteful, particularly if the property that changed doesn't have any impact on any other properties.
If you do have properties that cause changes in other properties, you can use the PropNameChanged event pattern. In this case, you would raise a FontChanged or ImageChanged event on the class when those properties change. The Windows Forms designer looks for events with that naming convention and uses them to invalidate the property grid.
But in the example you gave, those two properties would not invalidate each other and therefore you wouldn't need to invalidate either one in response to the other.
精彩评论