Z-order 和集合拷贝
原著:ken spencer
翻译:tjq_tang
原文出处:MSDN Magazine Jan 2004(AdvancedBasics)
原代码下载:AdvancedBasics0401.exe
(131KB)

Windows® 窗体控件 z-order 的唯一选项吗?

Windows 窗体控件没有象以前 Visual Basic (ZOrder)那样具备处理 z-order
的属性。碰到类似的处理时,可以在控件集合中找到方法。SetChildIndex 方法允许你设置控件的 z-index 。下面这行代码出自
Figure 1中的单击事件,它改变 Label2 的
z-index 值:
Me.Controls.SetChildIndex(Me.Label2, Label2NewIndex)
为了获取某个控件当前的索引,调用 GetChildIndex方法,如
Figure 1中 ShowIndex 方法所示。

方法将控件拷贝到一个数组中?

MyArrayOfControls 数组。
Me.Controls.CopyTo(MyArrayOfControls, 0)
下面我举例来说明一下,首先,我创建叫 frmControlsToArrayMaster 的窗体,它包含一组
Label/textbox 控件和三个按钮。接着我创建第二个窗体, 叫做 frmArrayClient, 它与前面创建的窗体大小相同,但没有控件。
于是,我准备好编写一些代码。
我最初写的代码是cmdCopy_Click事件(参见Figure 2)。该事件定义MyArrayOfControls数组大小为集合中控件的数目。然后,用
CopyTo 从第一个元素位置开始将控件拷贝到这个数组。我将这些代码放在按钮的 click 事件中,以便更加容易观察到它的运行。
接着我创建 cmdNew_Clieck 事件,第一个版本的代码就像这样:
Private Sub cmdNew_Click(ByVal sender As System.Object, _
ByVal e As System.EventArgs) Handles cmdNew.Click
Dim frm As New frmArrayClient
frm.ShowDialog()
End Sub
然后,我在 frmArrayClient 中添加下面的代码:
Dim ct As Control
If MyArrayOfControls.GetUpperBound(0) 0 Then
Me.Controls.AddRange(MyArrayOfControls)
For Each ct In Me.Controls
If ct.GetType.ToString = "System.Windows.Forms.Button" Then
ct.Visible = False
End If
Next
End If
通过调用 Controls.AddRange 添加来自第一个窗体的控件。因为在这个窗体上我不想要相同命令的按钮,于是将任何按钮的Visible
属性都设置为 False。很 Cool 吧?
效果参见 Figure 3:

Figure 3 Reparented 控件
上面的窗体是 frmControlsToArrayMaster,它是有控件的。但它们在哪里呢? 实际上,frmArrayClient 中的
AddRange 被调用时,这些控件从我的数组中被加载到新的窗体上。
因为一个控件仅能有一个父控件,这些控件被重新指定为新窗体( frmArrayClient
)里的子控件。也就是说这些控件不会再出现在第一个窗体( frmControlsToArrayMaster )上。
Figure 2中的代码cmdNew_Click在进行上面的处理时是有隐痛的。它
会将控件放回到 frmControlsToArrayMaster。你可以自己运行例子程序来证明这一点,将数据放进两个控件,然后单击新按钮。现在将数据添加
到两个以上的控件并且关闭新的窗体。所有输入的数据现在都在在原来窗体上。大多数情况下这样是很酷,但如果你是想拷贝控件到新的窗体上,而不是移动它们,那会
怎么样呢?
我对这个问题不是很在行,就向在 NetEdge Software 工作的朋友 Bill Martschenko
请求帮助,他发给我一些经典的代码以解决控件拷贝(克隆)问题。
它通过 CopyTo 函数将拷贝控件到数组,创建同样属性的控件集合(或者它们的子集),作为原始的控件。接着就可添加新控件而不会影响到原来的控件。
frmNewArrayClient2 采用了 Bill 建议的方法 . Figure 4 显示了新窗体,正如你所见的,原来的窗体上的控件依然如故,但新
窗体上这些控件被复制出来,几个命令按钮除外。

Figure 4 拷贝的控件
这个窗体加载 frmNewArrayClient2 事件:
Dim ctNew As Control
Dim i As Integer
If MyArrayOfControls.GetUpperBound(0) 0 Then
For i = 0 To MyArrayOfControls.GetUpperBound(0)
ctNew = CloneControl(MyArrayOfControls(i))
If ctNew.GetType.ToString "System.Windows.Forms.Button" Then
Me.Controls.Add(ctNew)
End If
Next
End If
上面这段代码循环访问 MyArrayOfControls 中所有控件,然后只要不是按钮,便每次都创建一个新的控件并添加到窗体上。新控件是通过调用 CloneControl
函数创建的。此代码十分简单并实现了控件的拷贝。现在,让我们进一步深入代码看看它是如何工作的 (参见
Figure 5 ) 。首先, CloneControl 函数执行
下面这行代码,创建与当前控件类型相同的新控件:
Dim newControl As Control = CType(NewAs(c), Control)
从CType 调用中可以看出,它有一个 NewAs 方法调用。该函数的这个参数就是你要拷贝的控件。NewAs 的具体细节请参见
Figure 5 。
如果你检查 NewAs 的定义( Figure 5
里有两个),就会发现它们看起来就像是自己在做递归调用,其实他们仅仅是被重载。这个小诀窍使你将参数作为对象来传递,
使得第一个函数调用干净利落。然后函数便可以对参数使用一些诸如 GetType
之类的操作,这样使开发人员更容易使用这个函数,因为他们不必知道如何调用私有函数的细节。第二个 NewAs 函数调用了程序集类的 CreateInstance,该方法根据控件
的类型创建控件实例。可以用它创建你想要克隆的控件实例。
CloneProperties 函数的第二行拷贝控件的属性并且将属性映射到新控件中,参见如下代码:
CloneProperties(newControl, c, "Visible", "Size", "Font", _
"Text", "Location", "BackColor", "ForeColor", "Enabled", _
"BackgroundImage")
CloneControl以一个If语句块结束,这个语句块允许你对特定的控件类型进行专门的处理。例子程序中有一个If语句块,当控件为按钮或链接标签时拷贝特定的属性。此外,它还二次调用
CloneProperties以获取附加属性。
CloneProperties 函数实现代码也在 Figure 5 中,该函数有一个你想要设置的属性数组参数。然后,它为每一个控件创建一个 PropertyAccessor 类实例。最后,代码循环参数
名数组,将目标控件的属性设为源控件的属性。从而将源控件的属性一个一个拷贝到新控件属性中。
让我们来看一下 PropertyAccessor 类(参见 Figure 6 )。该类的构造函数带了一个参数 ---
源或目的控件的引用,该引用设置到目标变量上。目标变量可继承引用控件的属性。
有意思的是代码定义了子项属性,有缺省值。可以看到子项属性:
Default Public Property Item(ByVal propertyName As String) As Object
Get 方法返回属性值。接着创建 PropertyInfo 类的实例并设置需要的属性 :
Dim prop As PropertyInfo = Me.Target.GetType().GetProperty(propertyName)
一旦获得了属性,就可通过 GetValue 方法获得它的值。 Set 方法也是这样直接使用的。新值被忽略,你需创建 PropertyInfo
类实例:
Dim prop As PropertyInfo = Me.Target.GetType().GetProperty(propertyName)
可调用 SetValue 设置属性值:
prop.SetValue(Me.Target, value, Nothing)
这就是所有的东西了。从一个 Form 拷贝控件到另一个 Form 的代码:
这个月的二个问题初看比较困难。但 Microsoft® .NET Framework
都为它们提供了直接的方式来解决。有时候自己就得坚持不懈以找寻解决问题的方案。
对 Ken 所写有什么问题或者评论,与 basics@microsoft.com 联系。
Ken Spencer 为 32X Tech 工作( http://www.32X.com ),提供培训,软件开发,微软技术质询。
本文出自
January 2004 期刊,可通过当地
报摊获得,或者最好是 订阅
