ElektroStudios
ElektroStudios

Reputation: 20464

Range validation in the Form Designer's PropertyGrid of a shadowed property

I inherited the System.Windows.Forms.Form class, then I shadowed the Form.Opacity property.

In my scenario, the acceptable opacity value must be between 0% and 99%, and for this I just tried to auto-fix the provided value with a simple conditional, however, I still can set the opacity to 100% in the designer's property grid:

enter image description here

This is the code I'm using:

VB.NET:

Public Class HUDForm : Inherits Form

    <Browsable(True)>
    <Category("Window Style")>
    <Description("The opacity level of this HUDForm.")>
    <EditorBrowsable(EditorBrowsableState.Always)>
    Public Shadows Property Opacity As Double
        Get
            Return MyBase.Opacity
        End Get
        Set(ByVal value As Double)
            If (value >= 1.0R) Then
                value = 0.99R
            End If
            MyBase.Opacity = value
        End Set
    End Property

End Class

C# (online translation):

public class HUDForm : Form {

    [Browsable(true)]
    [Category("Window Style")]
    [Description("The opacity level of this HUDForm.")]
    [EditorBrowsable(EditorBrowsableState.Always)]
    public new double Opacity {
        get { return base.Opacity; }
        set {
            if (value >= 1.0) {
                value = 0.99;
            }
            base.Opacity = value;
        }
    }

}

Upvotes: 0

Views: 1438

Answers (1)

TnTinMn
TnTinMn

Reputation: 11801

The Form's designer is the controlling factor as it is handling the validation of the Opacity property as well as several other properties. You will need to create a custom DocumentDesigner Class and set the DesignerAttribute on your HUDForm Class. For the official word on extending design time support, see: Extending Design-Time Support.

The class System.Windows.Forms.Design.FormDocumentDesigner is the specified designer for the Form class. This designer is inherited when your class inherits from the Form class. FormDocumentDesigner ultimately inherits from ComponentDesigner Class. A designer may provide alternate property implementations that are in effect during design. It does so by modifying the PropertyDescriptors that are sent to the VS designer's propertygrid. This modification is done in the PreFilterProperties method. In this case, alternate property definitions are provide for the Opacity, Menu, IsMdiContainer, Size, ShowInTaskBar, WindowState, AutoSize, AcceptButton, and and CancelButton properties.

As you can not inherit from System.Windows.Forms.Design.FormDocumentDesigner, you would need to re-implement this class with your desired function.

Here is a very abbreviated custom DocumentDesigner that will provide the specific functionality you want only for demonstration of the technique. You will need to re-implement the reset of the function provided by the System.Windows.Forms.Design.FormDocumentDesigner class.

Imports System.ComponentModel

Public Class myDocDesigner
  Inherits System.Windows.Forms.Design.DocumentDesigner

    Private Property Opacity As Double
        Get
                Return CDbl(MyBase.ShadowProperties.Item("Opacity"))
        End Get
        Set(ByVal value As Double)
        If (value >= 0.99R) Then
                value = 0.99R
        End If

                MyBase.ShadowProperties.Item("Opacity") = value
        End Set
    End Property

    Protected Overrides Sub PreFilterProperties(ByVal properties As IDictionary)
        Dim descriptor As PropertyDescriptor
        MyBase.PreFilterProperties(properties)
        Dim strArray As String() = New String() {"Opacity"}
        Dim attributes As Attribute() = {}
        Dim i As Integer
        For i = 0 To strArray.Length - 1
                descriptor = DirectCast(properties.Item(strArray(i)), PropertyDescriptor)
                If (Not descriptor Is Nothing) Then
                    properties.Item(strArray(i)) = TypeDescriptor.CreateProperty(GetType(myDocDesigner), descriptor, attributes)
                End If
        Next i
    End Sub
End Class

You would decorate your custom form like this:

Imports System.ComponentModel
Imports System.ComponentModel.Design

<Designer(GetType(myDocDesigner), GetType(IRootDesigner))>
Public Class HUDForm : Inherits Form

End Class

You will need a project reference to System.Design.dll.


Alternate Solution

After thinking this over a bit more, I realized that the OP's constraint is more restrictive of the value than that coded in FormDocumentDesigner, yet still satisfies those constraints. The default Opacity property has multiple checks on its allowed range. The simplest one to override is the TypeConverter applied to the property. The base implementation uses the OpacityConverter Class and the following TypeConverter is derived from that implementation.

Imports System.Globalization
Imports System.ComponentModel

Public Class OpacityConverter2
     Inherits TypeConverter
     Public Overrides Function CanConvertFrom(ByVal context As ITypeDescriptorContext, ByVal sourceType As Type) As Boolean
          Return ((sourceType Is GetType(String)) OrElse MyBase.CanConvertFrom(context, sourceType))
     End Function

     Public Overrides Function ConvertFrom(ByVal context As ITypeDescriptorContext, ByVal culture As CultureInfo, ByVal value As Object) As Object
          If Not TypeOf value Is String Then
                Return MyBase.ConvertFrom(context, culture, value)
          End If
          Dim currentValueAsString As String = CStr(value).Replace("%"c, " "c).Trim
          Dim parsedValue As Double = Double.Parse(currentValueAsString, CultureInfo.CurrentCulture)
          If (((CStr(value).IndexOf("%") > 0) AndAlso (parsedValue >= 0)) AndAlso (parsedValue <= 1)) Then
                currentValueAsString = (parsedValue / 100).ToString(CultureInfo.CurrentCulture)
          End If
          Dim returnValue As Double = 0
          Try
                returnValue = CDbl(TypeDescriptor.GetConverter(GetType(Double)).ConvertFrom(context, culture, currentValueAsString))
                If (returnValue > 1) Then
                     returnValue = (returnValue / 100)
                End If
          Catch exception As FormatException
                Throw New FormatException("Valid Range - 0 to 99%", exception)
          End Try
          If ((returnValue >= 0) AndAlso (returnValue <= 0.99)) Then
                Return returnValue
          Else
              Throw New FormatException("Valid Range - 0 to 99%")
          End If
     End Function

     Public Overrides Function ConvertTo(ByVal context As ITypeDescriptorContext, ByVal culture As CultureInfo, ByVal value As Object, ByVal destinationType As Type) As Object
          If (destinationType Is Nothing) Then
                Throw New ArgumentNullException("destinationType")
          End If
          If (destinationType Is GetType(String)) Then
                Dim num As Double = CDbl(value)
                Dim num2 As Integer = CInt((num * 100))
                Return (num2.ToString(CultureInfo.CurrentCulture) & "%")
          End If
          Return MyBase.ConvertTo(context, culture, value, destinationType)
     End Function

End Class

Usage:

Imports System.ComponentModel
Imports System.ComponentModel.Design

Public Class HUDForm : Inherits Form

    <TypeConverter(GetType(OpacityConverter2))>
     <Browsable(True)>
     <Category("Window Style")>
     <Description("The opacity level of this HUDForm.")>
     <EditorBrowsable(EditorBrowsableState.Always)>
     Public Shadows Property Opacity As Double
          Get
                Return MyBase.Opacity
          End Get
          Set(ByVal value As Double)
                If (value >= 1.0R) Then
                     value = 0.99R
                End If
                MyBase.Opacity = value
          End Set
     End Property
End Class

Upvotes: 2

Related Questions