2.3. Nevykreslení formuláře či sestavy

Tento problém se objevuje v několika prostředích Windows (Windows 3.11, Windows NT 3.51 a Windows 95) a proto ho považujeme za problém aplikace MS-Access. Nepravidelně se stává, že podklad formuláře není správně vykreslen. Spolu s ním chybí také řada položek, a to jak popisů (Label), tak datových objektů (Text Box), nebo obslužných tlačítek (Command Button). Často je také vnitřní část formuláře posunuta oproti správnému umístění. Na obr. 18 vidíme stav takového formuláře po jeho ručním překreslení (horká klávesa F9). Popisované chování se projevovalo u více formulářů, nepomohla oprava databáze, komprese, ani zrušení formuláře a jeho načtení ze záložní databáze. Důvod tohoto chování nám není znám.

Obr. 18. Nevykreslení celého formuláře

Podobný problém se v několika výstupních sestavách objevoval u položek, jejichž formát byl nastaven na Měna (Currency). Jejich obsah nebyl vykreslován. To se projevovalo pouze při tisku, při zobrazení sestav na obrazovce byly položky řádně vypsány. Důvod tohoto chování rovněž nebyl odhalen.

2.4. Uzamykání databáze

Tvůrce databází jistě zná přístupy k uzamykání záznamů při práci s formuláři. Je možno nastavit uzamčení celé tabulky, editovaného záznamu (ve skutečnosti jde o zamčení bufferu velikosti 1024 Byte, který může zasahovat více záznamů, závisí to na jejich délce), nebo žádného záznamu. Problém uzamykání se však objevuje na více místech. Především je třeba, aby uživatelé ve víceuživatelském systému neotevírali databázi s výhradním přístupem (Open Exclusive), pak ji nemůže otevřít nikdo jiný. Nejlepší je v oblasti globálních nastavení (Options) nastavit vlastnost "Default Open Mode for Databases" na hodnotu "Shared", tedy sdílený přístup.

Další kolize mohou nastat zejména v případě, kdy se s databází současně pracuje a přitom probíhají také opravy objektů. Je vcelku zřejmé, že při práci s vazbami mezi tabulkami (Relationships) dojde k uzamčení vazby, pokud někdo pracuje s kteroukoliv ze spojených tabulek, viz obr. 19. To samo o sobě není překvapivé. Bohužel se při delší práci se systémem stávalo, že vazby byly porušeny (ačkoliv systém nehlásil porušení databáze) a požadované kontroly konzistence neprobíhaly. Nápravu sjednalo provedení opravy databáze (Repair). Samozřejmě bylo nutno nejprve zajistit konzistenci dat ručně.

Obr. 19 Uzamčení vazby mezi tabulkami

Pozor je třeba si dát také na uzamčení přístupu k objektu (formuláři, sestavě apod.), pokud někdo upravuje jeho vzhled. Takové chování je naprosto samozřejmé a nemělo by nikoho překvapovat. Problém nastává, pokud dojde k uzamčení záznamu i když uzamykání není nastaveno a objekt není modifikován, viz. Obr. 20. Při práci v síti Microsoft Windows Network se zhruba pěti uživateli k této situaci docházelo i několikrát denně. Předpokládáme, že k uzamykání docházelo při ukládání změněného obsahu záznamu, i když každý pracoval s jiným záznamem.

Obr. 20 Uzamčení editovaného objektu

K dosud nezdůvodněným výpadkům dochází při provozu aplikací v síti. Výpadek se projevuje při zpracování funkcí. Systém hlásí, že chybí použití metody Edit nebo AddNew. Hlášení se přitom objevuje poté, co byl záznam prokazatelně otevřen pro editaci metodou Edit a několik položek bylo programem modifikováno. Zřejmě došlo k příliš dlouhé odezvě a systém přerušil komunikaci po síti. Problém je možno upravit tím, že po připojení objektu Recordset nastavíme sdílený přístup k datům:

...
Set R = D.OpenRecordset("Vyplaty", DB_OPEN_DYNASET)
R.LockEdits = False ' Odstraněno zamykání záznamů
...

2.5. Číslování záznamů ve výstupní sestavě

Často se vyskytujícím problémem je číslování záznamů ve výstupní sestavě (Report). Často se přitom jedná o číslování nadpisů, včetně jejich hierarchického členění. Řešení není příliš komplikované, např. využijeme následující funkci:

Function Pocitadlo (Uroven As Variant) As Variant
'*****************************************************
' Funkce vrací číslo, které se stále zvyšuje o 1
' Při zadání nižší úrovně je číslo inicializováno na 0
' Při zadání úrovně 0 jsou inicializovány všechny vyšší
'*****************************************************
Static P(10) As Integer, Posl As Integer
Dim I As Integer
Select Case Uroven

Case 1 To 10

I = Uroven
If I < Posl Then

P(I + 1) = 0

End If
P(I) = P(I) + 1
Pocitadlo = P(I)
Posl = I

Case Else

For I = 1 To 10

P(I) = 0

Next
Pocitadlo = Null
Posl = 0

End Select
End Function

Tuto funkci zapojíme do výstupní sestavy způsobem, který naznačuje obr. 21. Za povšimnutí stojí zejména volání funkce s parametrem 0 v oblasti záhlaví sestavy. Toto volání vynuluje všechna počítadla, takže při novém otevření sestavy bude číselná řada opět začínat od 1. Je to velmi důležité, zejména pokud si chcete zobrazit ukázku sestavy před tiskem a teprve následně ji dát vytisknout. I když si to uživatel neuvědomuje, dochází dvakrát k formátování sestavy, takže pokud bychom počítadla nulovali např. při obsluze otevření sestavy (událost On Open), pak by toto vynulování před skutečným tiskem nebylo realizováno a řada by plynule pokračovala.

Snad není nutno zdůrazňovat, že funkce při nulování musí vracet hodnotu #Null# (případně prázdný řetězec), jinak by se návratová hodnota vytiskla. Nastavit tuto nulovací položku sestavy na neviditelnou (Visible = Not) není řešením, nebyla by vůbec vyvolána. Jediným úspěšným řešením by bylo nastavení barvy popředí této položky na bílou barvu, stejně jako pozadí (spolu s vypnutím orámování položky).

Pokud bude uživatel požadovat pouze číslování souvislou řadou, není zdánlivě potřeba zadávat funkci Pocitadlo jako parametr úroveň vnoření číselné řady. Pokud však vytvoříme funkci Pocitadlo zcela bez parametrů, budeme značně překvapeni. Při prvním použití sestavy bude číslování správné, při druhém však bude číselná řada pokračovat. Je zřejmé, že jsme opomenuli nulování lokální proměnné P. Pokud bychom na tomto řešení trvali, musíme použít globální proměnnou a tu nulovat pomocí jiné funkce.

Až potud byla situace jednoduchá, skutečný problém vzniká až při použití číslování při tisku adresních štítků. Ty jsou obvykle realizovány pomocí sestavy, která obsahuje jedinou oblast - Detail. Není tedy možno umístit nulování počítadla do oblasti záhlaví sestavy. Jak již bylo uvedeno, nulování při obsluze události On Open nepostačuje, pokud si chceme sestavu před tiskem prohlédnout. Řešení ukazuje následující výpis dvou funkcí a obr. 22. Pro opakované rozpoznání začátku formátování využíváme unikátnosti hodnot primárního klíče (položka ID). K jejímu uložení používáme globální proměnnou PID.

Function Poc1_chybne ()
' ***************************
' Pokus o vytvoření počítadla
' ***************************
Static P As Long
P = P + 1
Poc1_chybne = P
End Function
Function PocitadloAdresNulovani ()
'*****************************************************
' Vynulování pomocné paměti počítadla adres
'*****************************************************
PID = Null
End Function

Function PocitadloAdres (ID As Variant) As Variant
'*****************************************************
' Funkce vrací číslo, které se stále zvyšuje o 1
' Při zadání první hodnoty ID či při jejím opakovaném
' výskytu vynuluje počítadlo
'*****************************************************
Static P As Variant
If IsNull(PID) Or PID = ID Then

P = 1
PID = ID

Else

P = P + 1

End If
PocitadloAdres = P
End Function

Obr. 21 Uzamčení editovaného objektu

Obr. 22 Automatické číslování adresních štítků

V souvislosti s adresními štítky si ukažme ještě jeden problém. Požadavkem je tisk adres přímo na korespondenční lístky či obálky. To samozřejmě není nijak komplikované. V našem případě jsou však adresy seřazeny do jednotlivých skupin podle hodnoty položky Typ, která určuje jaké znění předtištěného korespondenčního lístku máme použít. Požadujeme, aby při změně typu byla upozorněna obsluha na nutnost jejich výměny. Pro řešení zpracujeme opět dvě funkce využívající globální proměnnou PTyp. První připojíme k obsluze události sestavy On Open, druhou k obsluze události oblasti Detail On Format. Na obr. 23 je uvedeno připojení funkce k formátování oblasti Detail. Všimněte si také nastavení konce stránky na dolním okraji oblasti* . Bohužel přijaté řešení není zcela dokonalé. Vykazuje tři nedostatky:

1. Při zobrazení ukázky před tiskem se objevují hlášení o změně typu také.

2. Po skončení prohlížení zůstává v paměti poslední typ. Pokud to je současně první typ, pak není na začátku tisku hlášen výskyt prvního typu. Částečně by pomohlo zrušení hlášení v případě, že paměť PTyp má hodnotu #Null#. Tak by se hlášení neobjevilo při otevření pohledu na sestavu, ale samozřejmě ani u prvního typu v případě přímého tisku sestavy na tiskárnu.

3. Při tisku na tiskárnu dojde při vyhlášení výskytu nového typu k přerušení tisku. Na tiskárně HP LaserJet 4+ to znamenalo, že poslední záznam předchozího typu byl vytištěn až po potvrzení hlášení. Zatím jsme neověřili, zda se jedná o chování obvyklé nebo typické pouze pro tento typ tiskárny. Nicméně se znalostí tohoto chování postačí upravit hlášení a poučit obsluhu.

Obr. 23 Hlášení změny typu korespondenčních lístků

Poznámka:
Občas se stává, že sestava tiskne za každou stranou se záznamem jednu prázdnou stránku. Nejčastěji je to způsobeno tím, že konec oblasti není „přiražen“ až ke značce konce stránky. Uchopte konec oblasti a přetáhněte jej nad značku konce stránky, tím zajistíte, že mezi značkou a koncem stránky již nebude žádný prostor. Druhým možným důvodem je nadbytečná šířka oblasti, přesahující nastavený tiskový prostor. Pozor si dávejte zejména na zaokrouhlování na celý počet tiskových bodů, který znamená, že některé rozměry není možno nastavit přesně. Nejjednodušší je zkrátit pravý okraj stránky tak, aby tiskový prostor na stránce byl bezpečně větší než nastavená šířka sestavy.

Function PocitadloTypuNulovani ()
'*****************************************************
' Vynulování pomocné paměti počítadla adres
'*****************************************************
PTyp = Null
End Function

Function PocitadloTypu (Typ As Variant) As Variant
'*****************************************************
' Funkce kontroluje vstupující typ a upozorňuje
' na jeho změnu
'*****************************************************
If IsNull(PTyp) Or PTyp <> Typ Then
MsgBox "Došlo ke změmě typu lístků, následuje typ: " & Typ
PTyp = Typ
End If
End Function