在设计时是否可以更改属性的值?

时间:2019-06-13 14:22:24

标签: vb.net properties attributes

我的Property上有一个User Control,它像这样隐藏在设计模式下

<Browsable(False)>
Public Property MyProperty As Object

我要根据另一个True的值将其更改为Property

类似

Private _otherProperty As Boolean
Public Property OtherProperty() As Boolean
    Get
        Return _otherProperty
    End Get
    Set(ByVal value As Boolean)
        _otherProperty = value
        If value = True Then
            'Set MyProperty Browsable Attribute True here
        Else
            'Set MyProperty Browsable Attribute False here
        End If
    End Set
End Property

这就是我的想法。

基本上,我希望Property仅在另一个Property设置为True时在设计时可用,但找不到在设计模式下更改属性值的方法。

2 个答案:

答案 0 :(得分:1)

通过PropertyDescriptors管理WinForm设计器的PropertyGrid中显示的属性。您可以通过两种不同的方式控制检查机制返回的描述符。一种相对简单(虽然乏味)的方法是让您的类实现ICustomTypeDescriptor Interface

让我们假设您的usercontrol类定义如下:

Imports System.ComponentModel
Public Class DemoUC
  Public Sub New()
    InitializeComponent()
  End Sub

  <RefreshProperties(RefreshProperties.All)>
  Public Property OtherProperty As Boolean

  <Browsable(False)>
  Public Property MyProperty As String
End Class

请注意RefreshPropertiesAttribute装饰OtherProperty。这将告诉PropertyGrid每次此属性更改时都拉动所有属性。这是必需的,以便在MyProperty为真时显示OtherProperty属性的逻辑起作用。

在另一个类文件中,添加以下实现ICustomTypeDescriptor Interface的部分类。

Imports System.ComponentModel

Partial Public Class DemoUC : Implements ICustomTypeDescriptor

  Public Function GetAttributes() As AttributeCollection Implements ICustomTypeDescriptor.GetAttributes
    Return TypeDescriptor.GetAttributes(Me, True)
  End Function

  Public Function GetClassName() As String Implements ICustomTypeDescriptor.GetClassName
    Return TypeDescriptor.GetClassName(Me, True)
  End Function

  Public Function GetComponentName() As String Implements ICustomTypeDescriptor.GetComponentName
    Return TypeDescriptor.GetComponentName(Me, True)
  End Function

  Public Function GetConverter() As TypeConverter Implements ICustomTypeDescriptor.GetConverter
    Return TypeDescriptor.GetConverter(Me, True)
  End Function

  Public Function GetDefaultEvent() As EventDescriptor Implements ICustomTypeDescriptor.GetDefaultEvent
    Return TypeDescriptor.GetDefaultEvent(Me, True)
  End Function

  Public Function GetDefaultProperty() As PropertyDescriptor Implements ICustomTypeDescriptor.GetDefaultProperty
    Return TypeDescriptor.GetDefaultProperty(Me, True)
  End Function

  Public Function GetEditor(editorBaseType As Type) As Object Implements ICustomTypeDescriptor.GetEditor
    Return TypeDescriptor.GetEditor(Me, editorBaseType, True)
  End Function

  Public Function GetEvents() As EventDescriptorCollection Implements ICustomTypeDescriptor.GetEvents
    Return TypeDescriptor.GetEvents(Me, True)
  End Function

  Public Function GetEvents(attributes() As Attribute) As EventDescriptorCollection Implements ICustomTypeDescriptor.GetEvents
    Return TypeDescriptor.GetEvents(Me, attributes, True)
  End Function

  Public Function GetProperties() As PropertyDescriptorCollection Implements ICustomTypeDescriptor.GetProperties
    Return GetProperties({})
  End Function

  Public Function GetProperties(attributes() As Attribute) As PropertyDescriptorCollection Implements ICustomTypeDescriptor.GetProperties
    Dim basePDs As New PropertyDescriptorCollection(Nothing, False)
    For Each pd As PropertyDescriptor In TypeDescriptor.GetProperties(Me, attributes, True)
      basePDs.Add(pd)
    Next
    If Me.DesignMode AndAlso Me.OtherProperty Then
      Dim pd As PropertyDescriptor = TypeDescriptor.GetProperties(Me, True).Cast(Of PropertyDescriptor).Where(Function(desc As PropertyDescriptor) desc.Name.Equals(NameOf(Me.MyProperty))).FirstOrDefault()
      If basePDs.Contains(pd) Then
        basePDs.Remove(pd)
      End If
      basePDs.Add(New BrowsableDescriptor(pd))
    End If
    Return basePDs
  End Function

  Public Function GetPropertyOwner(pd As PropertyDescriptor) As Object Implements ICustomTypeDescriptor.GetPropertyOwner
    Return Me
  End Function

  Class BrowsableDescriptor : Inherits PropertyDescriptor
    Private src As PropertyDescriptor
    Public Sub New(src As PropertyDescriptor)
      MyBase.New(src.Name, Nothing)
      Me.src = src
      Dim attribs As New List(Of Attribute)
      For Each att As Attribute In src.Attributes
        If TypeOf att Is BrowsableAttribute Then Continue For
        attribs.Add(att)
      Next
      attribs.Add(BrowsableAttribute.Yes)
      MyBase.AttributeArray = attribs.ToArray
    End Sub

    Public Overrides ReadOnly Property IsBrowsable As Boolean
      Get
        Return True
      End Get
    End Property

    Public Overrides ReadOnly Property ComponentType As Type
      Get
        Return src.ComponentType
      End Get
    End Property

    Public Overrides ReadOnly Property IsReadOnly As Boolean
      Get
        Return src.IsReadOnly
      End Get
    End Property

    Public Overrides ReadOnly Property PropertyType As Type
      Get
        Return src.PropertyType
      End Get
    End Property

    Public Overrides Sub ResetValue(component As Object)
      src.ResetValue(component)
    End Sub

    Public Overrides Sub SetValue(component As Object, value As Object)
      src.SetValue(component, value)
    End Sub

    Public Overrides Function CanResetValue(component As Object) As Boolean
      Return src.CanResetValue(component)
    End Function

    Public Overrides Function GetValue(component As Object) As Object
      Return src.GetValue(component)
    End Function

    Public Overrides Function ShouldSerializeValue(component As Object) As Boolean
      Return src.ShouldSerializeValue(component)
    End Function
  End Class

End Class

大多数实现只返回基础TypeDescriptor所提供的内容。在GetProperties函数中,实现了用可浏览的逻辑替换MyProperty属性的不可浏览的PropertyDescriptor的逻辑。

一旦这是代码被编译,DemoUC控件将在PropertyGrid中这样显示。请注意,根据MyProperty的值显示/隐藏了OtherProperty

ConditionalProperty GIF

答案 1 :(得分:0)

ControlDesigner派生的与用户控件相关联的定制设计器上的besing实现。

覆盖ControlDesigner PostFilterProperties方法,我们可以从方法引用的IDictionary属性中删除现有属性:

Dim propDescriptor = DirectCast(properties("PropertyName"), PropertyDescriptor)
properties.Remove("PropertyName");

覆盖PreFilterProperties方法,可以使用PropertyDescriptor来添加属性(如果先前已删除该属性,则添加回来):

properties.Add("PropertyName", propDescriptor)

可以在由另一个属性的值设置的条件下删除该属性,还可以验证DesignMode(或UserControl)的Control状态:

If (Not (Me.Control.Site.DesignMode) OrElse DirectCast(Me.Control, MyUserControl).SomeProperty) Then
    '(...)
End If

引起属性集合更改的属性必须用装饰,并设置为RefreshProperties.All

<RefreshProperties(RefreshProperties.All)>
Public Property MyPropertyA As Boolean = True

示例行为:

UserControlDesigner

Imports System.ComponentModel
Imports System.Diagnostics
Imports System.Windows.Forms
Imports System.Windows.Forms.Design

<Designer(GetType(MyUserControlDesigner))>
Partial Public Class MyUserControl
    Inherits UserControl

    <RefreshProperties(RefreshProperties.All)>
    Public Property MyPropertyA As Boolean = True
    Public Property MyPropertyB As Boolean
End Class


<DebuggerDisplay("MyUserControlDesigner", Name:="MyUserControlDesigner")>
Public Class MyUserControlDesigner
    Inherits ControlDesigner
    Private propDescriptor As PropertyDescriptor = Nothing

    Protected Overrides Sub PreFilterProperties(properties As System.Collections.IDictionary)
        MyBase.PreFilterProperties(properties)
        If Not Me.Control.Site.DesignMode OrElse DirectCast(Me.Control, MyUserControl).MyPropertyA Then
            If Not properties.Contains("MyPropertyB") Then
                properties.Add("MyPropertyB", propDescriptor)
            End If
        End If
    End Sub

    Protected Overrides Sub PostFilterProperties(properties As System.Collections.IDictionary)
        If Me.Control.Site.DesignMode AndAlso Not DirectCast(Me.Control, MyUserControl).MyPropertyA Then
            If properties.Contains("MyPropertyB") Then
                propDescriptor = DirectCast(properties("MyPropertyB"), PropertyDescriptor)
                properties.Remove("MyPropertyB")
            End If
        End If
        MyBase.PostFilterProperties(properties)
    End Sub
End Class