我不知道我做错了什么,但是我创建了一个自定义控件,其属性具有使用继承ExpandableObjectConverter
的类型转换器的子属性。
似乎我已经正确设置了所有内容,但是当我尝试更改父属性的任何子属性时,设计器中的显示不会更改,直到我单击另一个属性(例如,如果我更改了对象使用我的自定义Color1
类作为其颜色的Gradient
属性,在我点击该属性或对象之前,颜色在设计器中不会改变。)
代码包含在下面。
IndicatorBar
控件类:
Imports System.ComponentModel
Public Class IndicatorBar
Private _Percentage As Double
Private ReadOnly _BackGradient, _BarGradient As Gradient
Private _Side As SourceSide
...
<Description("Expand to set the colors of the background gradient."), Category("Appearance"), DesignerSerializationVisibility(DesignerSerializationVisibility.Content)>
Public ReadOnly Property BackGradient As Gradient
Get
Return _BackGradient
End Get
End Property
<Description("Expand to set the colors of the bar gradient."), Category("Appearance"), DesignerSerializationVisibility(DesignerSerializationVisibility.Content)>
Public ReadOnly Property BarGradient As Gradient
Get
Return _BarGradient
End Get
End Property
...
Private Sub IndicatorBar_Paint(sender As Object, e As PaintEventArgs) Handles Me.Paint
Dim backRect As New Rectangle(0, 0, Width, Height)
Dim maskRect As RectangleF
Dim bar As New Rectangle(0, 0, Width, Height)
Select Case Side
Case SourceSide.Left
maskRect = New RectangleF(0, 0, Width * Percentage, Height)
Case SourceSide.Top
maskRect = New RectangleF(0, 0, Width, Height * Percentage)
Case SourceSide.Right
maskRect = New RectangleF(Width * (1.0 - Percentage), 0, Width * Percentage, Height)
Case SourceSide.Bottom
maskRect = New RectangleF(0, Height * (1.0 - Percentage), Width, Height * Percentage)
End Select
Using backGrad As New Drawing2D.LinearGradientBrush(backRect, BackGradient.Color1, BackGradient.Color2, BackGradient.Angle)
e.Graphics.FillRectangle(backGrad, backRect)
End Using
e.Graphics.SetClip(maskRect)
Using barGrad As New Drawing2D.LinearGradientBrush(bar, BarGradient.Color1, BarGradient.Color2, BarGradient.Angle)
e.Graphics.FillRectangle(barGrad, bar)
End Using
e.Graphics.ResetClip()
End Sub
End Class
Gradient
类:
Imports System.ComponentModel
<TypeConverter(GetType(GradientConverter))>
Public Class Gradient
Private _Angle As UShort = 0
Private _Color1, _Color2 As Color
Public Sub New()
Color1 = SystemColors.ControlLight
Color2 = SystemColors.ControlLightLight
End Sub
Public Sub New(ByVal c1 As Color, ByVal c2 As Color)
Color1 = c1
Color2 = c2
End Sub
Public Sub New(ByVal c1 As Color, ByVal c2 As Color, ByVal ang As UShort)
Color1 = c1
Color2 = c2
Angle = ang
End Sub
<Browsable(True), NotifyParentProperty(True), RefreshProperties(RefreshProperties.Repaint), EditorBrowsable(EditorBrowsableState.Always)>
Public Property Color1 As Color
Get
Return _Color1
End Get
Set(value As Color)
_Color1 = value
End Set
End Property
<Browsable(True), NotifyParentProperty(True), RefreshProperties(RefreshProperties.Repaint), EditorBrowsable(EditorBrowsableState.Always)>
Public Property Color2 As Color
Get
Return _Color2
End Get
Set(value As Color)
_Color2 = value
End Set
End Property
<Browsable(True), NotifyParentProperty(True), EditorBrowsable(EditorBrowsableState.Always), DefaultValue(0)>
Public Property Angle As UShort
Get
Return _Angle
End Get
Set(value As UShort)
_Angle = value Mod 360
End Set
End Property
End Class
GradientConverter
类:
Public Class GradientConverter
Inherits ExpandableObjectConverter
Public Overrides Function ConvertTo(context As ITypeDescriptorContext, culture As Globalization.CultureInfo, value As Object, destinationType As Type) As Object
If destinationType Is GetType(String) Then
Return ""
End If
Return MyBase.ConvertTo(context, culture, value, destinationType)
End Function
End Class
答案 0 :(得分:2)
首先,启用Angle
代码中有8个左右的隐式转换。我也改变了LinearGradientBrush
类型。 UShort
需要一个,但你有Gradient
。
由于IndicartorBar.BarGradient.ColorX
属性本身是一个Type(类),因此您需要在子元素更改时通知父级。换句话说,在IndicatorBar
中 - 您希望通知将 2级冒泡到执行绘画的NotifyParentProperty
。
我怀疑你试图对RefreshProperties
和Color1
做些什么。问题是对Gradient
的更改只会通知INotifyPropertyChanged
。此外,属性不会自动与他们装饰的类或属性进行交互。大多数情况下,它们都是对Designer或Serializer等其他东西的指导。
解决方案是实施Public Class Gradient
Implements INotifyPropertyChanged
...
' VS will add this when you press ENTER on the Implements line
Public Event PropertyChanged(sender As Object,
e As PropertyChangedEventArgs) Implements INotifyPropertyChanged.PropertyChanged
...
Public Property Color2 As Color
Get
Return _Color2
End Get
Set(value As Color)
If value <> _Color2 Then
_Color2 = value
RaiseEvent PropertyChanged(Me,
New PropertyChangedEventArgs("Gradient"))
End If
End Set
End Property
,这很简单,并且不会更改代码:
Color1
修改Color2
和IndicatorBar
设置器以触发事件。然后在Public Class IndicatorBar
...
' I have no idea why these were ReadOnly
Private WithEvents _BackGradient, _BarGradient As Gradient
...
Private Sub _BackGradient_PropertyChanged(sender As Object,
e As PropertyChangedEventArgs) Handles BackGradient.PropertyChanged,
_BarGradient.PropertyChanged
Me.Invalidate()
End Sub
我们需要订阅该事件并回复:
Color#
现在,当颜色选择器下拉菜单关闭时Gradient
上的任何UserControl
发生更改时,Invalidate()
将会收到通知,并且应该立即重新绘制控件{ {1}}。
要为Angle
添加它,只需按上述方法在该setter中引发事件即可。在测试更改之前,还要确保清理并重建项目。
另一个问题是你的TypeConverter
没有做任何事情。它继承自ExpandableObjectConverter
,因此子属性崩溃。但是在折叠时,TypeConverter应该提供与Font
或Location
一样的摘要。
如果子属性,您的ConvertTo
应该提供该摘要。
Public Class GradientConverter
Inherits ExpandableObjectConverter
' when the designer asks if we can convert to string,
' reply "Yes, I can!"
Public Overrides Function CanConvertTo(context As ITypeDescriptorContext,
destinationType As Type) As Boolean
If destinationType Is GetType(String) Then
Return True
End If
Return MyBase.CanConvertTo(context, destinationType)
End Function
Public Overrides Function ConvertTo(context As ITypeDescriptorContext,
culture As Globalization.CultureInfo,
value As Object,
destinationType As Type) As Object
If destinationType Is GetType(String) Then
' cast value to our Type
Dim grad As Gradient = CType(value, Gradient)
' return the prop summary
Return String.Format("{0}, {1}, {2}", grad.Angle.ToString,
grad.Color1.ToString,
grad.Color2.ToString)
End If
Return MyBase.ConvertTo(context, culture, value, destinationType)
End Function
End Class
现在,您的2个Gradient
属性将汇总3个子道具,并在您更改时更新:
注意:
如果您想允许用户编辑3个子属性的字符串形式(例如,让他们键入&#34;蓝色&#34;通过其中一种颜色而不用扩展或打开{ {1}}属性,您必须实施Gradient
/ CanConvertFrom
才能将文本转换为有效的属性值。
在这种情况下,请注意如何装饰或分离各个值(例如,在文本中使用2个逗号)。您的ConvertFrom
代码必须解析格式化的字符串以提取这些值(同时请记住,用户可能已删除或更改了分隔符)。