控件处理焦点的方法取决于开发控件时使用的模式。本章前面的“创建 ActiveX 控件的三种方法”中讨论了建造 ActiveX 控件的模式。
重点 如果象“允许开发人员将控件放置在您的控件上”所述的那样,要描写一个作为其它控件的容器的控件,则请注意,这个主题中的材料不能应用到被开发人员放到您的控件的实例上的控件。这些被包含的控件将接收独立于您的控件以及独立于其子控件的焦点。
对于用户绘制控件,UserControl 上不会有子控件。如果不希望控件接收焦点,则可以把 UserControl 对象的 CanGetFocus 置成 False。CanGetFocus 的缺省值为 True。
如果用户绘制控件能够接收焦点,则当您的控件接收或失去焦点时, UserControl 对象会收到 GotFocus 和 LostFocus 事件。当它拥有焦点时,用户绘制控件负责绘制自己的焦点矩形,本章的“用户绘制控件”中对此进行了讨论。
这是 UserControl 的 GotFocus 和 LostFocus 事件需要为用户绘制控件实现的唯一功能。不必为控件的使用者产生 GotFocus 和 LostFocus 事件,因为如果 CanGetFocus 属性为 True,则这些事件已由容器的扩展属性提供。
注意 用户绘制控件的 UserControl 对象在收到 GotFocus 事件之前先收到 EnterFocus 事件,在 LostFocus 事件之后会收到 ExitFocus 事件。没有必要在这两个事件的事件过程中添加代码,这也是我们的建议。
如本主题后面所描述的,用户绘制的控件可响应访问键。
如果制作的控件是对单个子控件的改进,或者是若干子控件的组合,那么,不管 CanGetFocus 的值是什么,UserControl 对象都不能接收到焦点,除非这些子控件全都不能接收焦点。
如果没有子控件能接收焦点,并且 CanGetFocus 为 True,那么 UserControl 对象接收到的事件与用户绘制控件的相同。对于这些事件,唯一需要做的工作是使控件看上去得到了焦点。
如果控件至少包含一个能接收焦点的子控件,则 UserControl 对象的 CanGetFocus 属性必为 True。如果试图在 UserControl 上将 CanGetFocus 属性设置成 False ,而 UserControl 具有能接收焦点的子控件,则会出现错误。
Visual Basic 将不允许把可接收焦点的子控件放在 UserControl 上,这里,UserControl 的 CanGetFocus 属性为 False:当 UserControl 的设计窗口为活动时,接收焦点的控件的图标在工具框中无效。
当焦点从控件外部移至控件的某个子控件上时,UserControl 对象将收到 EnterFocus 事件。接收到焦点的子控件的 GotFocus 事件将发生在 UserControl_EnterFocus 事件过程之后。
只要焦点在控件内,UserControl 对象就不会发生与焦点有关的事件。不过,当焦点从一个子控件移到另一个时,有关的子控件的 GotFocus 和 LostFocus 事件就会发生。
当焦点移出控件时,拥有焦点的最后一个子控件将收到 LostFocus 事件。在事件过程返回后,UserControl 对象将收到它的 ExitFocus 事件。
可在 EnterFocus 事件中改变那个子控件接收到焦点。这样做的原因可能是希望让上一次最后得到焦点的子控件仍然得到焦点,而缺省情况下是由 UserControl 内子控件的 tab 键次序中的第一个子控件得到焦点。
提示 如果控件比较复杂,例如有多个子控件的 Address 控件,则最好不要在 ExitFocus 事件中确认数据。您控件的用户可以在该用户控件的 Validate 事件中放置代码以处理数据验证,如果一定要在该控件内部验证数据,请与子控件的 CausesValidation 属性共同使用 Validate 事件。请注意,您不能总是对子控件的 Validate 事件记数,如下面“处理 Validate 事件”中所讨论的。
。
提示 一般来说,当在调试与焦点相关的事件时,最好不要用 MsgBox,因为消息框立即得到焦点。在 EnterFocus 和 ExitFocus 事件中使用 MsgBox 是非常不好的。应该使用 Debug.Print。
应该避免把控件的子控件的访问键放到硬编码中,因为如果为控件赋以永久性的访问键,将减小用户为窗体选择访问键时的自由度。另外,同一窗体上的两个控件实例会有访问键冲突。
本章后面的“允许开发者为控件设置访问键”讨论了如何使用户能够提供设置控件实例访问键。
如果控件自己不能接收焦点,并且也没有能接收焦点的子控件,可以使控件的显示行为与 Label 控件一样。也就是说,当控件的访问键按下时,焦点就移到 Tab 键顺序中的下一个控件。
为了允许上述做法,需要把 UserControl 对象的 ForwardFocus 属性置为 True。
对用户控件而言,Validate 事件和 CausesValidation 属性的行为与其他控件并没有什么区别,但是子控件的 Validate 和 CausesValidation 的表现可能会使人感到费解。首先,让我们回顾一下正常的行为特性。当一个控件失去焦点的时候,在触发它的 LostFocus 事件之前,先被触发的将是它的 Validation 事件(不过,下面将要得到焦点的控件的 CausesValidation 属性需要设置为 true,否则将不会触发 Validation)。利用这种性质,可以在控件即将失去焦点之前检验数据的有效性。
用户控件提供了一个 Validate 事件,它还通过 Extender 对象提供了一个 CausesValidation 属性。当焦点从该用户控件转移到 CausesValidation 属性被设置为 true 的其他控件时,Validate 事件的代码将被执行;如果将用户控件的 CausesValidation 属性设置为 True,那么任何将焦点传递给用户控件的控件的 Validation 事件都将被触发。
如果焦点始终在用户控件的内部移动,那么子控件的 Validate 事件和 CausesValidation 属性将按照上面规定的方式正常工作。然而,如果焦点被移动到用户控件之外,子控件的 Validate 事件将不会被触发。因此,最稳妥的办法是不在用户控件的内部进行数据有效性检查。