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 aktywacjiEnd Sub Private Sub pp_PopupDeactivate()' miejsce na kod podczas deaktywacjiEnd 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