Uchwyt okna dialogowego "Ustawienia strony".
Odczytu i zmiany opcji wydruku raportu.
Szczegółowe omówienie metod pracy z oknami Windows zastosowanych w przykładzie <<Opcje wydruku raportu w pliku *.MDE. Ustawianie i odczyt.>>
Na stronie << Lista okien >> zapoznaliśmy się z podstawowymi funkcjami API umożliwiającymi nam uzyskanie potrzebnych danych o oknie "Ustawienia strony".
Aby wywołać interesujące nas okno i uzyskać jego uchwyt posłużymy się poniższą procedurą:
- Private Sub btnSetupPage_Click()
Dim sRptName As String
- sRptName ="Raport1"
DoCmd.OpenReport sRptName, acViewPreview
DoCmd.RunCommand acCmdPageSetup
' pobierz uchwyt aktywnego okna
lRet = GetActiveWindow
- End Sub
Wstrzymanie wykonywania kodu przez okno dialogowe "Ustawienia strony". Odczyt uchwytu okna w procedurze Form_Timer formularza.
Niestety, po wywołaniu okna dialogowego wykonywanie dalszych instrukcji zostaje wstrzymane do czasu zamknięcia okna, a potem GetActiveWindow zwróci nam uchwyt zupełnie innego, bliżej nieokreślonego okna (prawdopodobnie Application.hWndAccessApp). Aby uzyskać uchwyt okna "Ustawienia strony" posłużymy się Timerem bieżącego formularza, w procedurze którego pobierzemy uchwyt otwartego okna dialogowego.
- Private Sub btnSetupPage_Click()
Dim sRptName As String
- sRptName ="Raport1"
DoCmd.OpenReport sRptName , acViewPreview
' Uruchamiamy Timer formularza
Me.TimerInterval = 50
' w celu wyeliminowania komunikatu błędu nr 2501
' - akcja.RunCommand została anulowana
On Error Resume Next
DoCmd.RunCommand acCmdPageSetup
' zamknij raport
DoCmd.Close acReport, sRptName
If Err.Number <> 0 Then Err.Clear
- End Sub
a w Form_Timer() pobieramy potrzebny nam uchwyt okna i uruchamiamy procedurę odczytującą dane o strukturze okna "Ustawienia strony".
- Private Sub Form_Timer()
Dim hWind As Long
- Me.TimerInterval = 0
' zakładam, że uzyskaliśmy uchwyt okna "Ustawienia Strony"
hWind = GetActiveWindow
Call zbGetInfoWindPageSetup( hWind)
- End Sub
Niestety, obserwujemy na ekranie chwilowe pojawienie się okna raportu. Czas widoczności okna raportu zależny jest od czasu ładowanie się raportu. Im raport bardziej skomplikowany tym dłuższy czas widoczności raportu.
- Aby ograniczyć do minimum czas widoczności raportu nie możemy niestety ukryć raportu bezpośrednio po otwarciu przy pomocy:
- Reports(sRptName).Visible = False
- ponieważ nie zadziała instrukcja:
- DoCmd.RunCommand acCmdPageSetup
- Skorzystamy więc z funkcji ShowWindow (...) z parametrem SW_HIDE = 0
- lRet = ShowWindow (Reports(sRptName).hwnd, SW_HIDE)
- Niestety, nie na wiele nam się to zdało, gdyż w początkowej fazie pojawia się na ekranie okno raportu. Wykorzystamy w takim razie zdarzenie Report_Open(Cancel As Integer) i w nim przesuniemy nasz raport poza obszar widoczności.
Deklarujemy w formularze zmienną publiczną Public fMoveRpt As Boolean pozwalającą rozpoznać podczas otwierania raportu (w zdarzeniu Report_Open), czy chcemy przesuwać raport poza ekran, czy też użytkownik otwiera raport w normalny sposób.
Minimalizowanie efektu migotania ekranu podczas otwierania raportu i okna dialogowego "Ustawienia strony"
Metoda 1:
Tworzymy pomocniczą procedurę w module raportu, w której za pomocą funkcji GetSystemMetrics (...) pobierzemy szerokość i wysokość ekranu, a funkcją MoveWindow (...) przesuniemy raport poza obszar ekranu (teoretycznie, ponieważ funkcja MoveWindow (...) przesuwa okna dzieci w/m okna rodzica, a nie współrzędnych ekranu.
- Private Sub zbMoveRptOutScreen()
Dim lScreenX As Long
Dim lScreenY As Long
- lScreenX = GetSystemMetrics(SM_CXSCREEN)
lScreenY = GetSystemMetrics(SM_CYSCREEN)
' Przesuń raport poza ekran (w lewo-dół o 100 pikseli)
MoveWindow Me.hwnd, lScreenX + 100, lScreenY + 100, 0, 0, False
- End Sub
Metoda 2:
Pobierzemy współrzędne okna Accessa za pomocą funkcji GetWindowRect (...) i względem tych współrzędnych przesuniemy okno raportu. Właściwie powinniśmy pobrać współrzędne okna rodzica raportu (MDIClient), ale to niewiele zmienia ponieważ przenosimy raport o wartość 100 pikseli.
- Private Sub zbMoveRptOutAcc()
Dim hAcc As Long
Dim rct As RECT
- hAcc = Application.hWndAccessApp
GetWindowRect hAcc, rct
' Przesuń raport poza okno Accessa (w lewo-dół o 100 pikseli)
MoveWindow Me.hwnd, rct.Right + 100, rct.Bottom + 100, 0, 0, False
- End Sub
- w obu przypadkach w raporcie musimy obsłużyć zdarzenie Report_Open(...):
Private Sub Report_Open(Cancel As Integer)
Dim fNotUser As Boolean
- ' Sprawdź, czy otwarcie ma na celu odczyt/zapis opcji wydruku,
' czy też raport otwiera użytkownik
On Error Resume Next
fNotUser = Forms("frmSetupPagel").fMoveRpt
If Err.Number <> 0 Then Err.Clear
If fNotUser Then
' Call zbMoveRptOutScreen
Call zbMoveRptOutAcc
End If
- End Sub
Wyeliminowaliśmy chwilowe pojawianie się otwartego raportu na ekranie, ale czas otwarcia raportu może nam w znaczny sposób wydłużyć czas odczytu lub zapisu opcji wydruku. Dodatkowo musimy oprogramować we wszystkich raportach, dla których chcemy zmieniać opcje wydruku, zdarzenie Report_Open.
Niestety, dla plików *.MDE jest to konieczne, jeżeli chcemy uniknąć nieprzyjemnego wizualnie efektu chwilowego pojawiania się okna raportu.
Metoda 3 - tylko w plikach bazy typu *.MDB
Poniżej przedstawiam sposób zmiany opcji wydruku bez konieczności zmian kodu raportów oraz ich otwierania, ale sposób ten działa tylko w plikach *.MDB ponieważ metody
DoCmd.RunCommand acCmdPageSetup
nie można zastosować w plikach *.MDE dla raportów, które zostały zaznaczone w oknie bazy.
Za pomocą funkcji GetParentt (...) pobieramy uchwyt rodzica (MDIClient) okna bazy. Okno rodzic jest także rodzicem naszego formularza. Mając uchwyt rodzica i znając nazwę klasy okna bazy danych możemy znaleźć uchwyt okna bazy danych dzięki funkcji FindWindowEx (...)
Następnie pobieramy współrzędne okna bazy i jego rodzica funkcją GetWindowRect (...) i okno bazy danych przesuwamy poza obszar rodzica funkcją MoveWindow (...). W niewidocznym teraz dla nas oknie bazy danych zaznaczamy nasz raport. Po tej operacji okno bazy danych staje się widoczne, więc aby je ponownie ukryć użyjemy funkcji ShowWindow (...).
Żeby okno bazy danych po odsłonięciu pojawiło nam się dokładnie w starym miejscu (byśmy nie musieli go szukać poza ekranem) pobierzemy dodatkowo grubość obramowania okna dzięki GetSystemMetrics(...). Przeliczamy wcześniej pobrane współrzędne okna bazy danych i jego rodzica, odejmujemy szerokość i wysokość obramowania i ukryte okno bazy danych powróci na stare miejsce.
- Private Sub zbMoveDbOutMDI()
Dim hParent As Long
Dim hWndDB As Long
Dim rctMDI As RECT
Dim rctDB As RECT
Dim lXBorder As Long
Dim lYBorder As Long
- ' pobierz uchwyt rodzica
hParent = GetParent(Me.hwnd)
' znajdź okno bazy danych
hWndDB = FindWindowEx(hParent, 0, "ODb", vbNullString)
' pobierz wymiary okna bazy danych
GetWindowRect hWndDB, rctDB
' pobierz wymiary okna MDIClient
GetWindowRect hParent, rctMDI
' przesuń okno bazy poza okno rodzica (w lewo-dół) o 100 pikseli
MoveWindow hWndDB, rctMDI.Right + 100, rctMDI.Bottom + 100, _
- rctDB.Right - rctDB.Left, rctDB.Bottom - rctDB.Top, False
- ' zaznacz raport w oknie DB
DoCmd.SelectObject acReport, cRptName, True
' ukryj okno bazy danych
ShowWindow hWndDB, SW_HIDE
' pobierz szerokośc i wysokość obramowania okien
lXBorder = GetSystemMetrics(SM_CXEDGE)
lYBorder = GetSystemMetrics(SM_CYEDGE)
' przekaż do okna bazy stare położenie
MoveWindow hWndDB, rctDB.Left - rctMDI.Left - lXBorder, _
- rctDB.Top - rctMDI.Top - lYBorder, _
- rctDB.Right - rctDB.Left, rctDB.Bottom - rctDB.Top, False
- ' uruchom Timer, który wywoła właściwą funkcję odczytu lub zapisu opcje wydruku
Me.TimerInterval = 50
On Error Resume Next
DoCmd.RunCommand acCmdPageSetup
If Err.Number <> 0 Then Err.Clear
' uaktywnij bieżący formularz
DoCmd.SelectObject acForm, Me.name, False
- End Sub
Korzystając z
wcześniej opisanych funkcji i procedur pozwalających odczytać niektóre właściwości okien możemy uzyskać bardziej szczegółowe dane o oknie "Ustawienie strony":
Szczegółowe dane o oknie "Ustawienie strony" i jego potomkach (dzieciach).
- Private Sub zbGetInfoWindPageSetup(hWndPage As Long)
Dim lRet As Long
Dim sBfClass As String
Dim hNext As Long
Dim i As Integer
Dim sMsg As String
Dim sClass As String * 20
Dim sWndText As String * 25
Dim sID As String * 5
- hNext = GetWindow(hWndPage, GW_CHILD)
' Przygotuj nagłówki kolumn
sClass = Space(5) & "Klasa okna"
sWndText = Space(7) & "Tekst okna"
sID = " Id "
sMsg = sMsg & "|" & String(63, 61) & "|" & vbNewLine
sMsg = sMsg & "| Lp." & "| " & sClass & " | " & sWndText & " | " & sID & " |" & vbNewLine
sMsg = sMsg & "|" & String(63, 61) & "|" & vbNewLine
- Do Until hNext = 0
- sBfClass = zbGetClassWind(hNext)
i = i + 1 ' licznik okien
sClass = sBfClass
sWndText = zbGetTextWind(hNext)
sID = GetDlgCtrlID(hNext)
- If i > 0 Then sMsg = sMsg & "| " & Format(i, "00") & ".| " & sClass & " | " & _
- sWndText & " | " & sID & " |" & vbNewLine
- hNext = GetWindow(hNext, GW_HWNDNEXT)
- Loop
sMsg = sMsg & "|" & String(63, 61) & "|" & vbNewLine
Me.TxtIdPage = sMsg
- End Sub
Tabela 1. Struktura okna Ustawienia strony
Lp. | Klasa okna | Tekst okna | ID |
01. | SysTabControl32 | ----- | 1959 |
02. | Button | Marginesy (milimetry) | 1075 |
03. | Static | &Górny: | 1104 |
04. | Edit | 30mm | 1156 |
05. | Static | D&olny: | 1105 |
06. | Edit | 35,01 | 1158 |
07. | Static | &Lewy: | 1102 |
08. | Edit | 40mm | 1155 |
09. | Static | &Prawy: | 1103 |
10. | Edit | 45,01 | 1157 |
11. | Button | Przykład | 1957 |
12. | Static | ----- | 1080 |
13. | Static | ----- | 1081 |
14. | Static | ----- | 1082 |
15. | Button | Drukuj &tylko dane | 1920 |
16. | Button | Drukuj nagłówki | 1964 |
17. | Static | ----- | 1084 |
18. | Button | Orientacja | 1072 |
19. | Static | ----- | 1962 |
20. | Static | ----- | 1963 |
21. | Button | Pio&nowa | 1056 |
22. | Button | Po&zioma | 1057 |
23. | Button | Papier | 1073 |
24. | Static | &Rozmiar: | 1089 |
25. | ComboBox | A4 (210 x 297 mm) | 1137 |
26. | Static | Źró&dło: | 1090 |
27. | ComboBox | Standardy | 1138 |
28. | Button | Drukarka dla: Raport1 | 1074 |
29. | Button | Dru&karka domyślna | 1960 |
30. | Button | &Użyj konkretnej drukarki | 1961 |
31. | Button | Druk&arka... | 1026 |
32. | Button | Ustawienia siatki | 1958 |
33. | Static | Liczba kol&umn: | 1944 |
34. | Edit | 1 | 1925 |
35. | Static | Odstęp wi&erszy: | 1950 |
36. | Edit | 3cm | 1931 |
37. | Static | O&dstęp kolumn: | 1945 |
38. | Edit | 4cm | 1926 |
39. | Button | Rozmiar kolumn | 1933 |
40. | Static | &Szerokość: | 1947 |
41. | Edit | 5cm | 1928 |
42. | Static | Wyso&kość: | 1946 |
43. | Edit | 3cm | 1927 |
44. | Button | &Jak pasma szczegółów | 1921 |
45. | Button | Układ kolumn | 1952 |
46. | Button | &W dół i w poprzek | 1954 |
47. | Button | W popr&zek i w dół | 1953 |
48. | Static | ----- | 1083 |
49. | Button | OK | 1 |
50. | Button | Anuluj | 2 |
- Pozostaje nam jeszcze zamknięcie okna dialogowego. W tym celu posłużymy się wcześniej zadeklarowaną funkcją SendMessage (...) z parametrem:
Private Const WM_CLOSE = &H10
lRet = SendMessage (hWndPage, WM_CLOSE, ByVal 0, ByVal 0)
lub identyfikatorem przycisku "Anuluj" z powyżej tabeli, oraz funkcją GetDlgItem(...), która zwróci nam uchwyt okna dziecka o podanym Identyfikatorze:
Private Const BM_CLICK = &HF5
Private Const cIdBtnOK As Long = 1
Private Const cIdBtnCancel As Long = 2
Call zbClickButton(hWndPage, cIdBtnCancel)
Private Sub zbClickButton(hWnd As Long, lIDWnd As Long)
Dim lRet As Long
' kliknij przycisk o ID = lIDWnd
lRet = SendMessage(GetDlgItem(hWndPage, cIdBtnCancel ), BM_CLICK, ByVal 0, ByVal 0)
End Sub
ewentualnie w bardziej pokrętny sposób, gdy znamy dokładnie strukturę okna:
- Private Sub zbClickCancel(hPageSetup As Long)
Dim hNext As Long
Dim lRet As Long
- ' pobierz uchwyt pierwszego dziecka okna rodzica
hNext = GetWindow(hPageSetup , GW_CHILD)
' ostatnie dziecko okna rodzica (przycisk Anuluj)
hNext = GetWindow(hNext, GW_HWNDLAST)
' kliknij Anuluj
lRet = SendMessage(hNext, BM_CLICK, ByVal 0, ByVal 0)
' dla przycisku OK musielibyśmy się cofnąć o jeszcze jedno okno
' hNext = GetWindow(hNext, GW_HWNDPREV)
- End Sub
Jak widać w Tabeli 1.Struktura okna "Ustawienia strony" znajomość wszystkich okien nie jest nam potrzebna. Ograniczymy się jedynie do okien zawierających istotne dla nas informacje.
W tym celu deklarujemy zmienną wariantową
Dim arrID As Variant i przypisujemy do niej najistotniejsze dane z Tabeli 1
arrID = Array("Nazwa opisowa okna", ID, Tekst)
Tabela 2. Elementy tablicy arrID()
L.p. | Nazwa opisowa okna | ID | Tekst |
01. | "Margines góra:" | 1156 | True |
02. | "Margines dół:" | 1158 | True |
03. | "Margines lewy:" | 1155 | True |
04. | "Margines prawy:" | 1157 | True |
05. | "Drukuj Dane: " | 1920 | False |
06. | "Orientacja pionowo:" | 1056 | False |
07. | "Orientacja poziomo:" | 1057 | False |
08. | "Rozmiar papieru:" | 1137 | True |
09. | "Źródło:" | 1138 | True |
10. | "Drukarka domyślna:" | 1960 | False |
11. | "Konkretna drukarka:" | 1961 | False |
12. | "Ilość kolumn:" | 1925 | True |
13. | "Odstęp wierszy:" | 1931 | True |
14. | "Odstęp kolumn:" | 1926 | True |
15. | "Szerokość kolumn:" | 1928 | True |
16. | "Wysokość kolumn:" | 1927 | True |
17. | "Jak pasmo szczegółów:" | 1921 | False |
18. | "Układ kolumn 1-w dół i poprzek:" | 1954 | False |
19. | "Układ kolumn 2-w poprzek i w dół:" | 1953 | False |
Dane z okien dla których Tekst = True odczytamy za pomocą wcześniej opisanej zbGetTextWind(hWind (...). Dane z pozostałych okien uzyskamy dzięki znanej już funkcji SendMessage (...) wywoływanej z parametrem:
Private Const BM_GETCHECK = &HF0
lRet = SendMessage(hWnd, BM_GETCHECK, ByVal 0, ByVal 0)
- Private Sub zbGetPageSetupByID(hWndPage As Long)
Dim arrID As Variant
Dim hNext As Long
Dim sMsg As String
Dim lRet As Long
Dim i As Integer
Dim sCol_1 As String * 35
Dim sCol_2 As String * 20
sCol_1 = Space(5) & "Ustawienia"
sCol_2 = Space(7) & "Wartość"
sMsg = sMsg & "|" & String(65, 61) & "|" & vbNewLine
sMsg = sMsg & "| Lp." & "| " & sCol_1 & " | " & sCol_2 & " | " & vbNewLine
sMsg = sMsg & "|" & String(65, 61) & "|" & vbNewLine
' Struktura tablicy:
' "Nazwa opisowa okna", Id okna, Odczyt - tekst = True; stan = False
arrID = Array( _
- "Margines góra: ", 1156, True, _
- "Margines dół: ", 1158, True, _
- "Margines lewy: ", 1155, True, _
- "Margines prawy: ", 1157, True, _
- "Drukuj Dane: ", 1920, False, _
- "Orientacja pionowo: ", 1056, False, _
- "Orientacja poziomo: ", 1057, False, _
- "Rozmiar papieru: ", 1137, True, _
- "Źródło: ", 1138, True, _
- "Drukarka domyślna : ", 1960, False, _
- "Konkretna drukarka : ", 1961, False, _
- "Ilość kolumn: ", 1925, True, _
- "Odstęp wierszy: ", 1931, True, _
- "Odstęp kolumn: ", 1926, True, _
- "Szerokość kolumn: ", 1928, True, _
- "Wysokość kolumn: ", 1927, True, _
- "Jak pasmo szczegółów : ", 1921, False, _
- "Układ kolumn 1-w dół i poprzek: ", 1954, False, _
- "Układ kolumn 2-w poprzek i w dół: ", 1953, False)
For i = 0 To UBound(arrID) Step 3
- hNext = GetDlgItem(hWndPage, arrID(i + 1))
- If arrID(i + 2) = True Then
- ' odczytaj tekst okna
- sCol_1 = arrID(i)
- sCol_2 = zbGetTextWind(hNext)
- sMsg = sMsg & "| " & Format(i, "00") & ".| " & _
- sCol_1 & " | " & sCol_2 & " | " & vbNewLine
- Else
- ' odczytaj stan okna klasy "Button"
- lRet = SendMessage(hNext, BM_GETCHECK, ByVal 0, ByVal 0)
- sCol_1 = arrID(i)
- sCol_2 = lRet
- sMsg = sMsg & "| " & Format(i, "00") & ".| " &
- sCol_1 & " | " & sCol_2 & " | " & vbNewLine
- End If
- Next
Call zbClickButton(hWndPage, cIdBtnCancel)
- sMsg = sMsg & "|" & String(65, 61) & "|"
- Me.TxtIdPage = sMsg
- End Sub
Odczyt dostępnych rozmiarów papieru.
Pozostało nam jeszcze tylko odczytanie dostępnych rozmiarów papieru oraz danych dotyczących podajnika papieru. Aby odczytać wszystkie pozycje listy w oknach klasy CboBox posłużymy się identyfikatorem ID okna oraz funkcją SendMessage (...) wywoływaną z odpowiednimi parametrami:
-
Private Sub zbGetItemList(hWnd As Long, ctl As ComboBox, ctlId As Long)
Dim sText As String
Dim lLenTxt As String
Dim sSourceCbo As String
Dim lCount As Long
Dim lIndex As Long
Dim lRet As Long
Dim hCbo As Long
' uchwyt okna klasy CboBox
hCbo = GetDlgItem(hWnd, ctlId)
' ilość pozycji na liście zwróci nam funkcja SendMessage (...) wywołana z parametrem:
Const CB_GETCOUNT= &H146
lCount = SendMessage(hCbo, CB_GETCOUNT, ByVal 0, ByVal 0)
' długość tekstu pozycji listy uzyskamy dzięki parametrowi:
Const CB_GETLBTEXTLEN = &H149
lLenTxt = SendMessage(hCbo, CB_GETLBTEXTLEN, ByVal lIndex, ByVal 0)
' po przygotowaniu buforu na tekst pozycji listy:
sText = String(lLenTxt, vbNullChar)
' i zadeklarowaniu nastepnej stałej:
Const CB_GETLBTEXT = &H148
' odczytujemy tekst pozycji listy :
lRet = SendMessage(hCbo, CB_GETLBTEXT, ByVal lIndex, ByVal sText)
sText = Left(sText, lLenTxt)
' Wszystkie pozycje listy odczytujemy w pętli For i tworzymy string będący źródłem listy
' pola Combo naszego formularza:
- For lIndex = 0 To lCount - 1
- lLenTxt = SendMessage(hCbo, CB_GETLBTEXTLEN, ByVal lIndex, ByVal 0
- sText = String(lLenTxt, vbNullChar)
- lRet = SendMessage(hCbo, CB_GETLBTEXT, ByVal lIndex, ByVal sText)
- sText = Left(sText, lLenTxt)
- sSourceCbo = sSourceCbo & sText & ";"
- Next
ctl.RowSourceType = "Value List"
- ctl.RowSource = sSourceCbo
- ' i wpisujemy do naszego pola Combo aktualną wartość okna klasy CboBox:
- ctl.Value = zbGetTextWind(hCbo)
End Sub
Na <<następnej stronie>> zajmiemy się zmianą tekstu w oknach klasy Edit, odczytem poszczególnych pozycji listy w oknach klasy CboBox oraz odczytem i zmianą opcji w oknach klasy RadioButton i CheckBox.