属性页如何工作
属性页看起来很像表单,设计它们有点类似于设计表单。然而,属性页的工作方式与表单的工作方式完全不同。
例如,当“属性页”对话框创建属性页的实例时,Initialize 事件是 PropertyPage 对象获得的第一个事件 — 就像它对表单一样。但是,与表单不同的是,PropertyPage 对象不会获得 Load 事件。PropertyPage 对象的关键事件是 SelectionChanged 事件。
本主题检查您的 PropertyPage 对象必须做的三件事:
在 SelectionChanged 事件中,获取要编辑的属性值。
每当用户编辑属性值时,设置 PropertyPage 对象的 Changed 属性。
在 ApplyChanges 事件中,将已编辑的属性值复制回选定的控件(或多个控件)。
SelectionChanged 事件
SelectionChanged 事件在显示属性页以及当前选定控件的列表发生更改时发生。
例如,在选择控件的一个实例并打开“属性页”对话框后,开发人员可能会意识到她需要更改控件的两个实例的属性。通过在按住 CTRL 键的同时单击第二个实例,她可以将第二个实例添加到选定控件的列表中。然后,您的每个属性页都会收到一个 SelectionChanged 事件。
重要 您应该始终将 SelectionChanged 事件视为第一次加载您的属性页。如您所见,更改选择从根本上改变了属性页的状态。
为单个控件编写 SelectionChanged 事件
您需要在 SelectionChanged 事件中做的最重要的事情是设置显示属性值以进行编辑的控件的值。例如,考虑 VirtualVelociraptor 控件的 General 页面(最初显示在图 10.1 中):
假设 VirtualVelociraptor 控件的 Age 属性使用以下公共 Enum
Public Enum DinoAge vvHatchling vvJuvenile vvAdultEnd Enum
属性页的 SelectionChanged 事件可能如下所示:
Private Sub PropertyPage_SelectionChanged() ' Place the value of the DinoName property for the ' first selected control in the txtDinoName text ' box for display and editing. txtDinoName = SelectedControls(0).DinoName ' Use the value of the Age property of the first ' selected control to select the appropriate ' option button in the Age frame. optAge(SelectedControls(0).Age).Value = True ' (The code above depends on the fact that the ' elements of the DinoAge Enum have the values ' 0, 1, and 2.)End Sub
提示 属性页向导将使用文本框控件和复选框(用于布尔属性)填充您的属性页,并为 SelectionChanged 事件生成默认代码。
SelectedControls 集合
SelectedControls 集合包含开发人员正在处理的容器中当前选择的所有控件。该集合可能包含您的控件的多个实例;如果属性页由控件组件中的多个控件共享,则该集合可能包含多种类型的控件。
注意 您不必担心包含您自己以外的控件(例如文本框)的集合,因为“属性页”对话框仅显示所有当前选定控件使用的页面。
目前,请忽略可能选择多个控件的可能性。上面显示的 SelectionChanged 事件中的代码所做的是获取集合中第一个控件的每个属性的值,并将其分配给属性页上的适当控件。
在单个选定控件的情况下,这会将控件的所有属性值放在用户可以编辑的字段中。
编辑属性的不同方法
您可以使用显示枚举元素的下拉列表,而不是将 Age 属性的属性值显示为一组选项按钮:
下拉列表占用的空间比选项按钮少(这一优势随着可能值的数量增加而变大),并且它显示了将在代码中使用的常量的名称。
以下代码片段显示了如何设置这样的列表。
Private Sub PropertyPage_SelectionChanged() txtDinoName = SelectedControls(0).DinoName ' Create a drop-down list containing the values and ' names of the Enum elements for the Age ' property, and select the one that corresponds ' to the current value of the Age property. cboAge.AddItem vvHatchling & " - vvHatchling" cboAge.AddItem vvJuvenile & " - vvJuvenile" cboAge.AddItem vvAdult & " - vvAdult" cboAge.ListIndex = SelectedControls(0).Age ' (The index of each Enum element in the drop-down ' list is the same as the element's value.)End Sub
提示 虽然您可以选择对属性有意义的任何可编辑表示,但请记住,每个属性占用的空间越多,您需要的选项卡就越多。最大限度地减少选项卡的数量使您的控件的属性页更易于使用。对于大多数枚举,下拉列表将最有效地利用空间。
为多个控件编写 SelectionChanged 事件
要确定是否选择了多个控件,可以测试 SelectedControls 集合的 Count 属性,看它是否大于一。
为了处理多个选定的控件实例,将控件的属性分为两组很有用:
可以为多个控件明智地设置为相同值的属性。例如,能够将多个 Label 控件的 BackColor 属性设置为相同的值是非常方便的。
为多个控件设置相同值没有意义的属性。例如,将多个 Label 控件的 Caption 属性设置为相同的值并不是特别有用。事实上,用户不小心这样做可能会很烦人。
您可以在 SelectionChanged 事件中采用的一种方法是在选择多个控件时禁用第二类属性的编辑字段。在讨论 ApplyChanges 事件时,将展示另一种技术。
共享属性页
如果您的项目中有多个控件,并且两个这样的控件共享一个属性页,请确保为读取属性值的代码提供错误捕获。如果选择的第一个控件不包括页面上显示的所有属性,则当您尝试读取该属性值时会发生错误。
通过设置 Changed = True 来启用应用按钮
为了告诉 Visual Basic 用户已经编辑了属性页上的一个或多个属性,您必须将 PropertyPage 对象的 Changed 属性设置为 True。因为无法知道用户可能决定更改哪个属性,所以您必须对页面上显示的每个属性执行此操作。
例如,要通知 PropertyPage 上一个示例中 DinoName 或 Age 属性的更改,您可以使用以下代码:
请注意,这与编码完全相同PropertyPage.Changed = True
。
通知 PropertyPage 对象值已更改会启用“属性页”对话框上的“应用”按钮,并在按下“应用”按钮、用户更改选项卡或关闭对话框时引发 ApplyChanges 事件。
注意 您可能希望跟踪哪些属性已更改,这样您就不必将它们全部写出来。
ApplyChanges 事件
PropertyPage 对象中第二个最重要的事件是 ApplyChanges 事件。在这种情况下,您将编辑的属性值复制回当前选定的控件。
ApplyChanges 事件在用户发生以下情况时发生:
单击确定按钮关闭对话框。
单击应用按钮。
在“属性页”对话框中选择另一个选项卡。
ApplyChanges 事件的以下代码假定 SelectionChanged 事件是使用 Age 属性的下拉列表编码的,如前所示。
Private Sub PropertyPage_ApplyChanges() Dim vv As VirtualVelociraptor ' Set the DinoName property of the FIRST selected ' control only. SelectedControls(0).DinoName = txtDinoName For Each vv In SelectedControls ' Transfer the value currently selected in the ' drop-down list for the DinoAge property to ' all of the selected controls. vv.DinoAge = cboAge.ListIndex ' (The code above works because the value of ' each of the three elements of the Enum is ' the same as its index number in cboAge.) Next End Sub
因为给所有虚拟 velociraptors 赋予相同的名称通常没有意义,所以 DinoName 属性仅应用于第一个选定的控件。另一方面,Age 属性应用于所有选定的控件。
注意 作为控件作者,由您决定为多个选定控件设置哪些属性有意义。
处理 ApplyChanges 中的错误
在上面显示的情况下,ApplyChanges 事件中不会出错。text 属性是一个简单的字符串,下拉列表将 Age 属性的用户输入限制为仅有效的值。
如果您的属性页允许用户输入可能被 Property Let(或 Property Set)过程拒绝的值,您应该在 ApplyChanges 事件中使用错误捕获。最简单的方案是使用 On Error Resume Next,并在每个可能引发错误的属性之后测试 Err.Number。
发生错误时:
停止处理 ApplyChanges 事件。
显示错误消息,以便用户了解出了什么问题。
将焦点设置到导致错误的属性。
将 PropertyPage 对象的 Changed 属性设置为 True。
重要* 设置Changed = True
执行两个功能。首先,它重新启用应用按钮。其次,它可以防止在用户单击“确定”时关闭“属性页”对话框。*这是防止对话框关闭的唯一方法。