更改自定义子属性不会更新父类的控件

时间:2015-05-16 09:28:28

标签: .net vb.net user-controls typeconverter

我不知道我做错了什么,但是我创建了一个自定义控件,其属性具有使用继承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

1 个答案:

答案 0 :(得分:2)

首先,启用Angle代码中有8个左右的隐式转换。我也改变了LinearGradientBrush类型。 UShort需要一个,但你有Gradient

第1部分

由于IndicartorBar.BarGradient.ColorX属性本身是一个Type(类),因此您需要在子元素更改时通知父级。换句话说,在IndicatorBar中 - 您希望通知将 2级冒泡到执行绘画的NotifyParentProperty

我怀疑你试图对RefreshPropertiesColor1做些什么。问题是对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

修改Color2IndicatorBar设置器以触发事件。然后在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中引发事件即可。在测试更改之前,还要确保清理并重建项目。

第2部分

另一个问题是你的TypeConverter没有做任何事情。它继承自ExpandableObjectConverter,因此子属性崩溃。但是在折叠时,TypeConverter应该提供与FontLocation一样的摘要。

如果子属性,您的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个子道具,并在您更改时更新:

enter image description here

注意:
如果您想允许用户编辑3个子属性的字符串形式(例如,让他们键入&#34;蓝色&#34;通过其中一种颜色而不用扩展或打开{ {1}}属性,您必须实施Gradient / CanConvertFrom才能将文本转换为有效的属性值。

在这种情况下,请注意如何装饰或分离各个值(例如,在文本中使用2个逗号)。您的ConvertFrom代码必须解析格式化的字符串以提取这些值(同时请记住,用户可能已删除或更改了分隔符)。