|
Исправление бага ЭУ SSTab |
|
|
Пример иллюстрирует исправление известного бага ЭУ SSTab (подтверждённого Microsoft - см. http://support.microsoft.com:80/support/kb/articles/q193/0/21.asp&NoWebContent=1), заключающегося в возможности ухода "фокуса" (в частности - по нажатию клавиши TAB) на элементы управления, расположенные на неактивной вкладке ЭУ SSTab, - что может привести к неприятным эффектам.
Для исправления этого бага используем принцип работы ЭУ SSTab, заключающийся в том, что у тех элементов, которые относятся к неактивным вкладкам, он устанавливает свойство Left в здоровенную отрицательную величину, тем самым "убирая их за пределы экрана".
Сначала "наступим на грабли" - смоделируем тот самый "неприятный эффект":
Расположите на форме ЭУ SSTab1. Сделайте у него 2 вкладки. На первой расположите TextBox "Text1", CommandButton "Command1" и Frame "Frame1", внутри Frame1 - OptionButton "Option1". На второй вкладке - всё то же самое (только вместо 1 будет 2, разумеется). Ну и на самой форме (не на SSTab1) расположите пару кнопок, к примеру, - Command3 и Command4.
Затем вставьте это в код формы:
Private Sub Option1_GotFocus()
Me.Caption = "Option1"
End Sub
Private Sub Option2_GotFocus()
Me.Caption = "Option2"
End Sub
Теперь, запустив форму, "походите" по её контролам с помощью клавиши TAB: вы обнаружите, что при открытой вкладке ?1, когда видна только Option1, у вас заголовок формы в определённый момент установится в "Option2", хотя ЭУ Option2 находится на неактивной в этот момент вкладке ?2.
Вот такой вот неприятный эффект. То же самое будет, если вместо фреймов использовать PictureBox - ещё один контрол, который умеет быть "контейнером". А вот если вы удалите из примера выше оба фрейма, и OptionButton-ы посадите прямо на соотв. вкладки SSTab, то всё будет тип-топ.
Исправляем ситуацию, добавляя в код формы следующее:
Private Sub Form_Load()
SSTab1.Tab = 0
Call SSTab1_Click(0)
End Sub
Private Sub Option1_GotFocus()
Me.Caption = "Option1"
End Sub
Private Sub Option2_GotFocus()
Me.Caption = "Option2"
End Sub
Private Sub PreventTab()
Dim ctl As Control, ctl2 As Control
On Error Resume Next
For Each ctl In Me.Controls
If TypeOf ctl.Container Is SSTab Then
If (TypeOf ctl Is Frame) Or (TypeOf ctl Is PictureBox) Then
' нужно установить TabStop для всех контролов внутри ctl:
For Each ctl2 In Me.Controls
If ctl2.Container.Name = ctl.Name Then
ctl2.TabStop = (ctl.Left > 0)
' If ctl.Left < 0 Then Debug.Print ctl2.Name
End If
Next ctl2
Else
' Свойство TabStop есть не у всех контролов, поэтому
следующий
' оператор может дать ошибку - для этого и нужен On Error.
ctl.TabStop = (ctl.Left > 0)
End If
End If
Next ctl
On Error GoTo 0
End Sub
Private Sub SSTab1_Click(PreviousTab As Integer)
Call PreventTab
End Sub
объяснение работы Sub PreventTab:
При каждом щелчке на вкладке ЭУ SSTab1, процедура PreventTab устанавливает свойство TabStop в False у тех контролов, которые находятся в этот момент "за пределами экрана" - т.е. которые относятся к неактивным вкладкам.
Оператор "If (TypeOf ctl Is Frame) Or (TypeOf ctl Is PictureBox)" нужен для того, чтобы обрабатывать ситуации, когда на ЭУ SSTab расположен контрол, который САМ является контейнером (это м.б. или Frame, или PictureBox). Т.к. свойство Left любого ЭУ исчисляется от левой границы его контейнера, то у всех ЭУ, находящихся внутри такого фрейма или картинки, Left будет > 0 даже если их вкладка НЕактивна, в то время как у самого фрейма (картинки) Left будет < 0; поэтому без данного оператора IF контролы, лежащие внутри фрейма (картинки), не попали бы в число тех, у которых свойство TabStop установливалось в False при деактивации вкладки, - а это было бы неверно!
Код необходимо модифицировать, если нужно обрабатывать более сложную ситуацию - с бОльшим числом вложенности контейнеров: например, на SSTab лежит Frame, а внутри этого фрейма - есть ещё и PictureBox, и только внутри этого PictureBox-а расположены какие-то контролы. Для неограниченного уровня вложенностей нужно, наверное, использовать рекурсию.
Полезные примечания:
Баг наблюдается не обязательно при наличии на вкладках ЭУ SSTab каких-либо контейнеров. В Сети есть "жалобы" программистов, что баг проявляется и просто при размещении на вкладках SSTab какого-либо ActiveX-компонента, вроде обычного MS Rich Textbox, а также в случаях, когда на всех вкладках суммарно расположено МНОГО различных контролов (это, увы, я знаю по себе).
Если вы используете предложенный код для ликвидации этого бага, то при большом числе контролов на всей форме (т.е. при большом размере коллекции Me.Controls) лучше сначала (например, в событии Form_Load) прочитать имена контролов и их контейнеров (хотя бы первого уровня) плюс типы контролов в многомерный строковый массив(-ы), а затем использовать в Sub PreventTab обращение не к коллекции, а к этому массиву: так будет однозначно быстрее.
Автор: Юмашин Алексей
|
|
|
|
|
|
|