从 属性 派生 PropertyGrid 下拉列表是一个对象 class,而不仅仅是一个字符串
Derive PropertyGrid dropdown list from property that is an object class, not just a string
如何在 属性 网格中拥有作为对象(不仅仅是字符串)下拉列表的属性?我到目前为止,但后来卡住了!考虑以下代码:
Public Class mOrganisation
Public Property ID as String
Public Property Name as String
End Class
Public Class mSystem
Public Property ID as string
Public Property Name as String
Public Property Developer as mOrganisation
Public Overrides Function ToString() As String
Return Name
End Function
End Class
Public Class mGame
Public Property ID as string
Public Property Name as String
<TypeConverter(GetType(SystemConverter))>
Public Property System as mSystem
End Class
Public Class Main
Public Systems as List(of mSystem) = [...list gatehring code here]
End Class
Public Class SystemConverter
Inherits TypeConverter
Public Overrides Function GetStandardValuesSupported(ByVal context As ITypeDescriptorContext) As Boolean
Return True
End Function
Public Overrides Function GetStandardValuesExclusive(ByVal context As ITypeDescriptorContext) As Boolean
Return False
End Function
Public Overrides Function GetStandardValues(ByVal context As ITypeDescriptorContext) As TypeConverter.StandardValuesCollection
Return New StandardValuesCollection(Main.Systems)
End Function
End Class
mOrgnisation 只是为了给 mSystem Class 引入一些复杂性。现在这段代码确实会下拉值:
但是当我 select 一个值时,我得到一个 PropertyGrid 错误 "Object of type 'System.String' cannot be converted to type 'mSystem'"
这让我陷入了困境,尤其是在尝试应用 Convert From
和 Convert To
的各种排列时。但是,我找不到合适的解决方案。通过 ConvertFrom
进行的一次尝试使下拉菜单加载非常缓慢,一次加载一个项目(我认为它是为每个项目触发的)。
我会做一个自定义 UITypeEditor
但我找不到像标准下拉菜单那样获得 PropertyGrid 固有调整大小 method/handle 的方法(并尝试编写我自己的调整大小例程,但证明粘性和闪烁我认为是因为 PropGrid + 控件的交互)
什么是 best/most 优雅的实现方式?
有一些方法可以告诉系统"What to pick from object"
1) 在 ComboBox 上使用 DisplayMemberPath:
<ComboBox ItemsSource="{Binding Path=mSystem}"
DisplayMemberPath="Name"/>
2) 在 ComboBox 上设置 ItemTemplate。这类似于 #1,除了允许您定义要显示的模板:
<ComboBox ItemsSource="{Binding Path=mSystem}">
<ComboBox.ItemTemplate>
<DataTemplate>
<Border BorderBrush="Green" BorderThickness="1" Padding="5">
<TextBlock Text="{Binding Path=Name,StringFormat='Name: {0}'}" />
</Border>
</DataTemplate>
</ComboBox.ItemTemplate>
3) 将 DataTemplate 添加到 XAML 资源。这对于关联给定的 class:
很有用
<UserControl xmlns:local="CLASS_CONTEXT_HERE">
<UserControl.Resources>
<DataTemplate DataType="local:mSystem">
<TextBlock Text="{Binding Name}" />
</DataTemplate>
</UserControl.Resources>
4)如果要显示NAME和ID :
Public Overrides Function ToString() As String
Return string.Format("{0} ({1})", Name, ID)
End Function
最终放弃了 TypeConverter 并找到了解决方案。像大多数编码噩梦一样,它是将 5 行简单的代码放在正确的位置!关键是访问 PropertyGrid 内置的调整大小句柄。您可以调整下面的代码以包括构建处理自定义对象,因为 value
是任何对象。但是,为简单起见,下面的示例是一个字符串:
Public Overrides ReadOnly Property IsDropDownResizable As Boolean
Get
Return True
End Get
End Property
实现方法如下:
第 1 步:创建新的 UITypeEditor
这将 属性 连接到自定义编辑器。值包含从 属性 传递的值。它可以是任何对象类型。因此,您可以将其传递给您的自定义控件,以通知其中的任何编辑功能。
新建 class:
Imports System.ComponentModel
Imports System.Drawing.Design
Imports System.Windows.Forms.Design
Public Class TestUIEditor
Inherits UITypeEditor
' Indicate that we display a dropdown.
Public Overrides Function GetEditStyle(ByVal context As ITypeDescriptorContext) _
As UITypeEditorEditStyle
Return UITypeEditorEditStyle.DropDown
End Function
Public Overrides Function EditValue(ByVal context As ITypeDescriptorContext, ByVal provider As IServiceProvider, ByVal value As Object) As Object
' Get an IWindowsFormsEditorService object.
Dim editor_service As IWindowsFormsEditorService = CType(provider.GetService(GetType(IWindowsFormsEditorService)), IWindowsFormsEditorService)
If editor_service Is Nothing Then
Return MyBase.EditValue(context, provider, value)
End If
'Setup the editor
Dim editorService As IWindowsFormsEditorService = Nothing
If provider IsNot Nothing Then
editorService = TryCast(provider.GetService(GetType(IWindowsFormsEditorService)), IWindowsFormsEditorService)
End If
If editorService IsNot Nothing Then
Dim testUI As New TestPropGridUI(editorService)
'Populate the existing value to the dropdown control
testUI.Text = value
'Drop down the control
editorService.DropDownControl(testUI)
'Update property value once dropdown closed
value = testUI.Text
End If
Return value
End Function
Public Overrides ReadOnly Property IsDropDownResizable As Boolean
Get
'Ensures control is resizable
Return True
End Get
End Property
End Class
第 2 步:创建自定义编辑器:
在此示例中,我刚刚使用了继承 TextBox 的自定义 class。但是,您可以使用任何控件,甚至是自定义 UserControl。
Imports System.Windows.Forms.Design
Public Class TestPropGridUI
Inherits TextBox
' The editor service displaying this control.
Private m_EditorService As IWindowsFormsEditorService
Public Sub New(ByVal editor_service As IWindowsFormsEditorService)
MyBase.New()
m_EditorService = editor_service
Dock = DockStyle.Fill
End Sub
Public Sub returnPressed(sender As Object, e As KeyEventArgs) Handles Me.KeyDown
'Closes the dropdown when Enter pressed
If e.KeyCode = Keys.Enter Then
m_EditorService.CloseDropDown()
End If
End Sub
End Class
注意 m_EditorService.CloseDropDown。将其放在您希望下拉菜单自行关闭的任何位置(例如,在选择值之后)。
第 3 步:告诉您的 class 属性 使用自定义编辑器:
Public Class TestObject
Public Property ID as String
Public Property Quantity as Integer
<Editor(GetType(TestUIEditor), GetType(UITypeEditor))>
Public Property TestText As String
End Class
就是这样!
如何在 属性 网格中拥有作为对象(不仅仅是字符串)下拉列表的属性?我到目前为止,但后来卡住了!考虑以下代码:
Public Class mOrganisation
Public Property ID as String
Public Property Name as String
End Class
Public Class mSystem
Public Property ID as string
Public Property Name as String
Public Property Developer as mOrganisation
Public Overrides Function ToString() As String
Return Name
End Function
End Class
Public Class mGame
Public Property ID as string
Public Property Name as String
<TypeConverter(GetType(SystemConverter))>
Public Property System as mSystem
End Class
Public Class Main
Public Systems as List(of mSystem) = [...list gatehring code here]
End Class
Public Class SystemConverter
Inherits TypeConverter
Public Overrides Function GetStandardValuesSupported(ByVal context As ITypeDescriptorContext) As Boolean
Return True
End Function
Public Overrides Function GetStandardValuesExclusive(ByVal context As ITypeDescriptorContext) As Boolean
Return False
End Function
Public Overrides Function GetStandardValues(ByVal context As ITypeDescriptorContext) As TypeConverter.StandardValuesCollection
Return New StandardValuesCollection(Main.Systems)
End Function
End Class
mOrgnisation 只是为了给 mSystem Class 引入一些复杂性。现在这段代码确实会下拉值:
但是当我 select 一个值时,我得到一个 PropertyGrid 错误 "Object of type 'System.String' cannot be converted to type 'mSystem'"
这让我陷入了困境,尤其是在尝试应用 Convert From
和 Convert To
的各种排列时。但是,我找不到合适的解决方案。通过 ConvertFrom
进行的一次尝试使下拉菜单加载非常缓慢,一次加载一个项目(我认为它是为每个项目触发的)。
我会做一个自定义 UITypeEditor
但我找不到像标准下拉菜单那样获得 PropertyGrid 固有调整大小 method/handle 的方法(并尝试编写我自己的调整大小例程,但证明粘性和闪烁我认为是因为 PropGrid + 控件的交互)
什么是 best/most 优雅的实现方式?
有一些方法可以告诉系统"What to pick from object"
1) 在 ComboBox 上使用 DisplayMemberPath:
<ComboBox ItemsSource="{Binding Path=mSystem}"
DisplayMemberPath="Name"/>
2) 在 ComboBox 上设置 ItemTemplate。这类似于 #1,除了允许您定义要显示的模板:
<ComboBox ItemsSource="{Binding Path=mSystem}">
<ComboBox.ItemTemplate>
<DataTemplate>
<Border BorderBrush="Green" BorderThickness="1" Padding="5">
<TextBlock Text="{Binding Path=Name,StringFormat='Name: {0}'}" />
</Border>
</DataTemplate>
</ComboBox.ItemTemplate>
3) 将 DataTemplate 添加到 XAML 资源。这对于关联给定的 class:
很有用<UserControl xmlns:local="CLASS_CONTEXT_HERE">
<UserControl.Resources>
<DataTemplate DataType="local:mSystem">
<TextBlock Text="{Binding Name}" />
</DataTemplate>
</UserControl.Resources>
4)如果要显示NAME和ID :
Public Overrides Function ToString() As String
Return string.Format("{0} ({1})", Name, ID)
End Function
最终放弃了 TypeConverter 并找到了解决方案。像大多数编码噩梦一样,它是将 5 行简单的代码放在正确的位置!关键是访问 PropertyGrid 内置的调整大小句柄。您可以调整下面的代码以包括构建处理自定义对象,因为 value
是任何对象。但是,为简单起见,下面的示例是一个字符串:
Public Overrides ReadOnly Property IsDropDownResizable As Boolean
Get
Return True
End Get
End Property
实现方法如下:
第 1 步:创建新的 UITypeEditor
这将 属性 连接到自定义编辑器。值包含从 属性 传递的值。它可以是任何对象类型。因此,您可以将其传递给您的自定义控件,以通知其中的任何编辑功能。
新建 class:
Imports System.ComponentModel
Imports System.Drawing.Design
Imports System.Windows.Forms.Design
Public Class TestUIEditor
Inherits UITypeEditor
' Indicate that we display a dropdown.
Public Overrides Function GetEditStyle(ByVal context As ITypeDescriptorContext) _
As UITypeEditorEditStyle
Return UITypeEditorEditStyle.DropDown
End Function
Public Overrides Function EditValue(ByVal context As ITypeDescriptorContext, ByVal provider As IServiceProvider, ByVal value As Object) As Object
' Get an IWindowsFormsEditorService object.
Dim editor_service As IWindowsFormsEditorService = CType(provider.GetService(GetType(IWindowsFormsEditorService)), IWindowsFormsEditorService)
If editor_service Is Nothing Then
Return MyBase.EditValue(context, provider, value)
End If
'Setup the editor
Dim editorService As IWindowsFormsEditorService = Nothing
If provider IsNot Nothing Then
editorService = TryCast(provider.GetService(GetType(IWindowsFormsEditorService)), IWindowsFormsEditorService)
End If
If editorService IsNot Nothing Then
Dim testUI As New TestPropGridUI(editorService)
'Populate the existing value to the dropdown control
testUI.Text = value
'Drop down the control
editorService.DropDownControl(testUI)
'Update property value once dropdown closed
value = testUI.Text
End If
Return value
End Function
Public Overrides ReadOnly Property IsDropDownResizable As Boolean
Get
'Ensures control is resizable
Return True
End Get
End Property
End Class
第 2 步:创建自定义编辑器:
在此示例中,我刚刚使用了继承 TextBox 的自定义 class。但是,您可以使用任何控件,甚至是自定义 UserControl。
Imports System.Windows.Forms.Design
Public Class TestPropGridUI
Inherits TextBox
' The editor service displaying this control.
Private m_EditorService As IWindowsFormsEditorService
Public Sub New(ByVal editor_service As IWindowsFormsEditorService)
MyBase.New()
m_EditorService = editor_service
Dock = DockStyle.Fill
End Sub
Public Sub returnPressed(sender As Object, e As KeyEventArgs) Handles Me.KeyDown
'Closes the dropdown when Enter pressed
If e.KeyCode = Keys.Enter Then
m_EditorService.CloseDropDown()
End If
End Sub
End Class
注意 m_EditorService.CloseDropDown。将其放在您希望下拉菜单自行关闭的任何位置(例如,在选择值之后)。
第 3 步:告诉您的 class 属性 使用自定义编辑器:
Public Class TestObject
Public Property ID as String
Public Property Quantity as Integer
<Editor(GetType(TestUIEditor), GetType(UITypeEditor))>
Public Property TestText As String
End Class
就是这样!