Warsztat 3: Detekcja zdarzeń Activate/Deactivate formularzy typu popup.

popup.zip
Autor: Krzysztof Naworyta
Baza w formacie MsAccess 2000
24kB, 28-12-2005

Opis problemu:

Ktokolwiek zetknął się z wyskakującym kalendarzykiem
(czy to moje pierwsze próby z wykorzystaniem mscal.ocx, czy w pełni niezależnym od ocx rozwiązaniem Marcina Sankowskiego)
zetknął się z problemem jak rozpoznać, że okno typu popup traci focus.

Okazuje się, że dla tego typu formularzy zdarzenia OnActivate/OnDeactivate nie działają !
Dlaczego? Nie wiadomo. Może chodzi o to, że formularze typu popup mają głównie pełnić rolę podręcznych menu, które nie powinny "deaktywować" zwykłych formularzy ?

Niemniej system doskonale wie, że okno traci focus lub go otrzymuje ... Mówi nam o tym choćby aktywacja paska tytułu okna.

Czy aby uzyskać tę informację musimy "tykać" Timerem i sprawdzać:

Screen.ActiveForm.name = Me.Name 

Rozwiązanie:

Okazuje się, że możemy to zrobić inaczej! Możemy przechwycić komunikaty jakie spływają do naszego okna i odpowiednio je obsłużyć (tzw. subclassing) !

Posłuży nam do tego odpowiednia funkcja API oraz operator AddressOf.

Rozwiązanie takie ma jednak swoje ograniczenia:
funkcja, do której prześlemy systemowe komunikaty musi znajdować się w module standardowym
(takie ograniczenie operatora AddressOf)

Osobna sprawa jak to zrobić w miarę wygodnie, aby dla każdego formularza nie pisać kolejnych linii kodu.
Tu z pomocą przyjdzie klasa, która sprawdzi:
- czy formularz jest typu popup (także ten, który nie ma ustawionej w projekcie właściwości popup=true, lecz został wywołany w trybie acDialog !)
- zarejestruje się w kolekcji wszystkich takich obiektów, z których metody skorzysta publiczna funkcja WinProc()
- wyśle nowe zdarzenia do swojego formularza
- w końcu przechwyci zdarzenie Unload aby poprawnie wyrejestrować się z pamięci.

W naszych formularzach pozostanie nam jedynie zainicjować jej instancję z przechwytem jej zdarzeń:.

Dim WithEvents pp As cPopup
'   ^^^^^^^^^^
Private Sub Form_Load()
  Set pp = New cPopup
  Set pp.Form = Me
End Sub

Private Sub pp_PopupActivate()
  ' miejsce na kod podczas aktywacji
End Sub

Private Sub pp_PopupDeactivate()
  ' miejsce na kod podczas deaktywacji
End Sub

Uwagi:

Operator AddressOf ma swoje kaprysy! Raz uruchomiony kod trudno debug'ować. Przełączenie się do IDE i ponowne uruchomienie funkcji najczęśćiej spowoduje zawieszenie access'a. Niewielkie (?) błędy w funkcji zdejmującej subclassing z formularza będą powodowąć, że na niektórych systemach program będzie się zawieszał ...

Nie piszę tego jednak aby kogoś odstraszać. Po prostu trzeba się nauczyć jak z tym operatorem postępować podczas pisania aplikacji (np. po każdej zmianie w IDE zamknąć aplikację i ponownie otworzyć nim uruchomimy kolejne okno z subclassingiem). W trakcie jej późniejszej normalnej pracy problemów już nie powinno być.

W tym miejscu pragnę serdecznie podziękować Zbyszkowi Bratko za nieocenioną pomoc w usunięciu moich błędów.

Krzysztof Naworyta