更改自定义子项 属性 不会更新父项 class 的控件
Changing custom child property doesn't update parent class's control
我不知道我做错了什么,但我创建了一个带有 属性 的自定义控件,该控件使用继承 ExpandableObjectConverter
.[=21 的类型转换器具有子属性=]
似乎我已经正确设置了所有内容,但是当我尝试更改父项的任何子属性时 属性,设计器中的显示不会改变,直到我单击另一个 属性(例如,如果我更改使用我的自定义 Gradient
class 颜色的对象的 Color1
属性,则设计器中的颜色将不会更改,直到我单击那个 属性 或对象)。
代码包含在下面。
IndicatorBar
控件class:
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
class:
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
class:
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
首先,打开 Option Strict
您的代码中有 8 个左右的隐式转换。我还会更改 Angle
类型。 LinearGradientBrush
需要一个单曲,但你有 UShort
.
第 1 部分
由于Gradient
属性是本身一个类型(class),所以需要在子元素改变时通知父元素.换句话说,在 IndicartorBar.BarGradient.ColorX
中 - 您希望通知冒泡 2 级 给 IndicatorBar
执行绘画的人。
我怀疑你试图用 NotifyParentProperty
和 RefreshProperties
做什么。问题是对 Color1
的更改只会通知 Gradient
。此外,属性不会自动与它们修饰的 Class 或 属性 交互。大多数情况下,它们是对设计器或序列化器等其他东西的指令。
解决方案是实现 INotifyPropertyChanged
,这很简单并且不会对代码进行太多更改:
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
setter 以触发事件。然后在 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()
.
要为 Angle
添加它,只需如上所述在 setter 中引发事件。还要确保在测试更改之前清理并重建项目。
第 2 部分
另一个问题是您的 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 个子属性的字符串形式(例如,让他们在其中一种颜色 上键入“蓝色”而不 扩展或打开 Gradient
属性,您必须实施 CanConvertFrom
/ ConvertFrom
才能将文本转换为有效的 属性 值。
在这种情况下,请注意如何装饰或分隔各个值(例如,在文本中使用 2 个逗号)。您的 ConvertFrom
代码将必须解析格式化字符串以提取这些值(还要记住用户可能已删除或更改分隔符)。
我不知道我做错了什么,但我创建了一个带有 属性 的自定义控件,该控件使用继承 ExpandableObjectConverter
.[=21 的类型转换器具有子属性=]
似乎我已经正确设置了所有内容,但是当我尝试更改父项的任何子属性时 属性,设计器中的显示不会改变,直到我单击另一个 属性(例如,如果我更改使用我的自定义 Gradient
class 颜色的对象的 Color1
属性,则设计器中的颜色将不会更改,直到我单击那个 属性 或对象)。
代码包含在下面。
IndicatorBar
控件class:
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
class:
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
class:
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
首先,打开 Option Strict
您的代码中有 8 个左右的隐式转换。我还会更改 Angle
类型。 LinearGradientBrush
需要一个单曲,但你有 UShort
.
第 1 部分
由于Gradient
属性是本身一个类型(class),所以需要在子元素改变时通知父元素.换句话说,在 IndicartorBar.BarGradient.ColorX
中 - 您希望通知冒泡 2 级 给 IndicatorBar
执行绘画的人。
我怀疑你试图用 NotifyParentProperty
和 RefreshProperties
做什么。问题是对 Color1
的更改只会通知 Gradient
。此外,属性不会自动与它们修饰的 Class 或 属性 交互。大多数情况下,它们是对设计器或序列化器等其他东西的指令。
解决方案是实现 INotifyPropertyChanged
,这很简单并且不会对代码进行太多更改:
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
setter 以触发事件。然后在 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()
.
要为 Angle
添加它,只需如上所述在 setter 中引发事件。还要确保在测试更改之前清理并重建项目。
第 2 部分
另一个问题是您的 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 个子属性的字符串形式(例如,让他们在其中一种颜色 上键入“蓝色”而不 扩展或打开 Gradient
属性,您必须实施 CanConvertFrom
/ ConvertFrom
才能将文本转换为有效的 属性 值。
在这种情况下,请注意如何装饰或分隔各个值(例如,在文本中使用 2 个逗号)。您的 ConvertFrom
代码将必须解析格式化字符串以提取这些值(还要记住用户可能已删除或更改分隔符)。