在设计时访问窗体上的控件
如何在设计时为属性编辑器发现窗体上的所有控件
引言
这个技巧的重点在于,如何在 Visual Studio 中使用 Visual Basic 从属性编辑器(UITypeEditor
)访问设计时窗体。
关于下载的说明
- 不要尝试运行该应用程序,它什么也不做
- 重点是,在 IDE 中,当窗体设计器打开(
Simple Demo
->ExampleForm
)时,您可以选择名为ExampleComponent1
的组件,然后在属性编辑器中,TargetControls
属性是一个集合,单击省略号会显示UITypeEditor
- 您可以注意到,选中的(或未选中的)控件会在 IDE 会话之间持久保存,这意味着当您构建应用程序时,该集合将在运行时可见
我正在为 .NET component
创建一个 Visual Studio 属性编辑器(UITypeEditor
),该编辑器保存设计时附加到的 form
上 controls
的一个子集。最初,我很难弄清楚如何做到这一点,但像往常一样,一旦你发现了答案,它就变得非常容易。
下图显示了一个使用自定义 UITypeEditor
的演示应用程序。有一个自定义组件,具有一个名为 TargetControls
的属性,该属性使用该自定义 UITypeEditor
。实际的属性编辑器本身是左下方的窗体(红色边框),正如您所看到的,它包含顶部显示的窗体上的 控件
列表。该图像来自 Visual Studio 内部,不在调试模式下,所以您在顶部看到的是窗体设计器。
背景
在运行时创建一个 Collection(Of Control)
很容易,其中 Collection
中的 Controls
是 Form
上的 Controls
。在设计时执行此操作要困难得多 - 这就是我需要做的,所以我创建了一个 UITypeEditor
,它可以使用设计时窗体上的一个或多个 Controls
来填充 Component
或 Control
属性。我使用了 Creating Property Editors in DesignTime for VS.Net Easily (UITypeEditor Helper) 这篇文章,作者是 S.Serpooshan,以提供基类。
在这里,我将仅重现用于检索窗体上控件列表的代码部分,因为完整的类(希望如此)将是文章而不是技巧的主题。
Using the Code
在 UITypeEditor
中,有一个名为 EditValue
的方法,其工作是显示自定义控件(下拉列表或模态窗体)并根据用户与控件的交互方式更新属性。我已经删除了所有加载和显示自定义控件的周围代码以及管理更新属性的代码,以便将重点放在技巧的有趣部分。所有其他内容都可以从 S. Serpooshan 的文章中获取。
myControl
是对自定义属性编辑器控件的引用,在这种情况下,该控件是一个包含 CheckedListBox
的窗体(其名称为 AvailableControls
)。
第一个 For Each
循环将项目添加到该 CheckedListBox
中,通过 EditValue
函数的 context
参数访问对设计时窗体的引用。弄清楚这个参数为您提供了一种引用设计时窗体的方法是关键,从那里开始非常容易。
第二个 For Each
循环只是确定在显示它之前,在 CheckedListBox
中要选中哪些项目,即,在用户有机会编辑它之前,保存在属性的当前值中的项目。
Public Overrides Function EditValue(ByVal context As ITypeDescriptorContext, _
ByVal provider As IServiceProvider, ByVal value As Object) As Object
...
...
'Load the AvailableControls checked list with all form controls visible through the Designer
For Each c As Component In context.Container.Components
'Cycle through the components owned by the form in the designer
If TypeOf c Is Control Then
'if the component is a control then add it to the CheckedListBox
myControl.AvailableControls.Items.Add(c)
End If
Next
If value IsNot Nothing Then
'If the property currently has anything in its collection
'then check those items in the checked list box
'create a temporary Collection that holds the current controls held by the property
Dim tCollection As Collection(Of Control) = CType(value, Collection(Of Control))
Dim found As Integer
For Each c As Control In tCollection
'cycle through the current controls held by the property
found = myControl.AvailableControls.Items.IndexOf(c)
If Not found = -1 Then
'if the control has been found in the CheckedListBox
'then set its checked state to true
myControl.AvailableControls.SetItemChecked(found, True)
End If
Next
End If
...
...
End Function
要使用此代码,您需要创建一个自定义组件,该组件具有类型为 Collection(Of Control)
的 public
属性。然后,您需要将其 EditorAttribute
设置为 UITypeEditor
的类名。
Public Class ControlCollectionUITypeEditor
Inherits UITypeEditor
...
...
End Class
Public Class ExampleComponent
Inherits Component
Private _TargetControls As New Collection(Of Control)
'EditorAttribute tells the Property Grid to use this editor to change the property
'DesignerSerializationVisibility stops the compiler trying to
'serialize a Collection object (since it can't be serialized)
<EditorAttribute(GetType(ControlCollectionUITypeEditor), _
GetType(System.Drawing.Design.UITypeEditor))>
<DesignerSerializationVisibility(DesignerSerializationVisibility.Content)>
Public Property TargetControls As Collection(Of Control)
Get
Return _TargetControls
End Get
Set(ByVal value As Collection(Of Control))
'Validity checking, event raising etc. deliberately left out for simplicity
_TargetControls = value
End Set
End Property
End Class
关注点
我曾经在 MSDN Visual Basic General 论坛上与几个人讨论过这个问题,其主要内容是“你做不到”。
只要你足够努力,你就可以!
历史
- 版本 1:原始技巧
- 版本 2:根据 2013 年 10 月 29 日的第 29 个问题的答案添加了下载