Можно ли изменить значение атрибута свойства во время разработки? - PullRequest
1 голос
/ 13 июня 2019

У меня есть 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 ]

1 голос
/ 14 июня 2019

Свойства, отображаемые в PropertyGrid конструктора WinForm, управляются через PropertyDescriptors .Вы можете управлять дескрипторами, возвращаемыми механизмами проверки, несколькими способами.Относительно простой (хотя и утомительный) способ состоит в том, чтобы ваш класс реализовал интерфейс ICustomTypeDescriptor .

Предположим, что ваш класс 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 реализуется логика, позволяющая заменить небезопасный PropertyDescriptor для свойства MyProperty на доступный для просмотра.

После компиляции кода элемент управления DemoUCотображать, как это в PropertyGrid.Обратите внимание, что MyProperty отображается / скрыт на основе значения OtherProperty.

ConditionalProperty GIF

0 голосов
/ 14 июня 2019

Реализация реализована в Custom Designer, производном от ControlDesigner , связанном с UserControl.

Переопределив метод 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
...