Jak na to?

DatabßzovΘ formulß°e

V tomto Φlßnku si vytvo°φme formulß° nad jednoduchou databßzφ, slou₧φcφ k zadßvßnφ informacφ o zam∞stnancφch fiktivnφ firmy. Jde o databßzi Access s s nßsledujφcφ strukturou:

Tabulka Useky
id_usekuInteger (primßrnφ klφΦ)
nazevText(50)

Tabulka Zamestnanci
id_zamLong (automatickΘ Φφslo - primßrnφ klφΦ)
prijmeniText(50)
jmenoText(50)
uliceText(50)
obecText(50)
pscText(5)
telefonText(20)
usekInteger (cizφ klφΦ -> tabulka Useky(id_useku))
poznamkaText(200)

Pro p°φstup k databßzi pou₧ijeme technologii ADO. Aby byl p°φklad p°φstupn² pro co nejv∞tÜφ poΦet z vßs, nebudeme pou₧φvat nejnov∞jÜφ verzi, ale verzi 2.0. Tzn. otev°ete si nov² projekt a z menu Project -> References vyberte odkaz na knihovnu Microsoft ActiveX Data Object 2.0 Library.

Nejd°φve se musφme rozhodnout, jak budeme data zobrazovat. NejjednoduÜÜφ je samoz°ejm∞ pou₧φt prvek Data. Proto₧e vÜak tento prvek p°inßÜφ urΦitß omezenφ a v budoucnu bude projekt pravd∞podobn∞ rozÜφ°en, obejdeme se bez prvku Data a obslou₧φme si vÜe sami. Bude to sice vφce pracn∞jÜφ, ale nakonec nßm to p°inese vφce v²hod.

Jako prvnφ si vytvo°φme k≤d pro zobrazovßnφ dat. Vlo₧te na formulß° pole Üesti TextBox∙ s nßzvem txtZam. Ke ka₧dΘmu TextBoxu musφme samoz°ejm∞ p°idat i Label, ve kterΘm bude krßtkß identifikace pole. JednotlivΘ TextBoxy budou tedy p°edstavovat tato pole v databßzi: txtZam(0) - prijmeni, txtZam(1) - jmeno, txtZam(2) - ulice, txtZam(3) - obec, txtZam(4) - psc, txtZam(5) - telefon, txtZam(6) - poznamka. Ka₧dΘ pole v databßzi mß n∞jak omezenou dΘlku, proto nastavφme u ka₧dΘho TextBoxu do vlastnosti MaxLength maximßlnφ mo₧n² poΦet znak∙ podle maximßlnφ mo₧nΘ dΘlky v databßzi. Pole poznamka je navφc pon∞kud delÜφ, proto umo₧nφme nastavenφm vlastnosti MultiLine na True psanφ textu do vφce °ßdk∙ a takΘ zobrazφme vertikßlnφ scrollbar (ScrollBars - 2 - Vertical).

TextBoxy mßme nastaveny, te∩ jeÜt∞ jak zobrazit data. NejjednoduÜÜφ bude, kdy₧ u₧ nepou₧φvßme prvek Data, p°ipojit TextBoxy na otev°en² Recordset. Proto ka₧dΘmu nastavφme jeÜt∞ vlastnost DataField na p°φsluÜnΘ polφΦko v databßzi.

A m∙₧eme zaΦφt psßt k≤d pro zobrazenφ. Nejd°φve musφme otev°φt spojenφ na databßzi pomocφ objektu Connection a hned potom po₧adovanou tabulku p°i nahrßvßnφ formulß°e, tzn. v udßlosti Form_Load. Proto₧e vÜak budeme chtφt se spojenφm i s tabulkou pracovat i v jin²ch procedurßch a funkcφch, vytvo°φme tyto prom∞nnΘ jako lokßlnφ v rßmci formulß°e. Prvnφ k≤d ve formulß°i bude vypadat takto:

Private lconMain As New ADODB.Connection
Private WithEvents lrsMain As ADODB.Recordset

Private Sub Form_Load()
  Dim tmpText As TextBox
  
  'Otevri spojeni
  lconMain.CursorLocation = adUseClient
  lconMain.Open "PROVIDER=Microsoft.Jet.OLEDB.3.51;Data Source=" & _
                App.Path & "\data.mdb;"
  'Otevri tabulku 
  Set lrsMain = New ADODB.Recordset
  lrsMain.Open "SELECT * FROM Zamestnanci ORDER BY prijmeni", _
               lconMain, adOpenStatic, adLockOptimistic
  
  'Nastaveni textboxu
  For Each tmpText In txtZam
    Set tmpText.DataSource = lrsMain
  Next
End Sub

Nejd°φve jsme tedy otev°eli spojenφ, potom tabulku Zamestnanci, set°φd∞nou podle sloupce primeni a nakonec jsme nastavili otev°en² Recordset jako zdroj pro jednotlivΘ TextBoxy. Spustφte-li te∩ program, hned se zobrazφ pokud jsou v databßzi n∞jakΘ zßznamy, hned se zobrazφ prvnφ, proto₧e vlastnost DataField u₧ je nastavenß pro ka₧d² TextBox ruΦn∞.

Co se vÜak stane v p°φpad∞, ₧e n∞kdo bude chtφt zam∞stnance °adit podle jmΘna? Budete muset upravit k≤d, znovu zkompilovat program, vytvo°it novΘ instalaΦnφ diskety atd. Proto si vytvo°φme dotaz pro v²b∞r zam∞stnanc∙ p°φmo v databßzi. Pokud bude chtφt n∞kdo zm∞nu, nebude to a₧ takov² problΘm (samoz°ejm∞, pokud bude mφt Access). Dotaz se bude jmenovat qryZamestnanci a bude vypadat takto:

SELECT jmeno, obec, poznamka, prijmeni, psc, telefon, ulice, usek
FROM Zamestnanci
ORDER BY prijmeni, jmeno

JedinΘ co se potom zm∞nφ je °ßdek, kde otevφrßme Recordset.

lrsMain.Open "qryZamestnanci", lconMain, adOpenStatic, adLockOptimistic

Te∩ mßme vytvo°en² k≤d, kter² vÜak zatφm zobrazφ pouze prvnφ zßznam. Proto si vytvo°φme Φty°i tlaΦφtka, kterß budou slou₧it k pohybu mezi zßznamy. Vytvo°φme je op∞t jako pole, kterΘ nazveme cmdPohyb. Prvnφ bude slou₧it k posunu na prvnφ zßznam, druhΘ na p°edchßzejφcφ zßznam, t°etφ na nßsledujφcφ zßznam a ΦtvrtΘ na poslednφ zßznam. K≤d pro posun je velmi jednoduch². Nejd°φve otestujeme, zda jsou v tabulce n∞jakΘ zßznamy (pokud nejsou, nenφ co hledat). Pokud ano, provedeme posun. P°i pohybu na p°edchßzejφcφ resp. nßsledujφcφ zßznam, musφme jeÜt∞ otestovat, zda ji₧ nejsme na zaΦßtku resp. na konci.

Private Sub cmdPohyb_Click(Index As Integer)
  If lrsMain.RecordCount > 0 Then
    Select Case Index
      Case 0  'Prvni
        lrsMain.MoveFirst
      Case 1  'Predchozi
        lrsMain.MovePrevious
        If lrsMain.BOF Then lrsMain.MoveFirst
      Case 2  'Dalsi
        lrsMain.MoveNext
        If lrsMain.EOF Then lrsMain.MoveLast
      Case 3  'Na konec
        lrsMain.MoveLast
    End Select
  End If
End Sub

K≤d pro pohyb mßme vytvo°en², ale jeÜt∞ nßm chybφ jedno pole a to je usek. Pole usek je v tabulce Zamestnanci pouze ΦφselnΘ, co₧ nikomu moc ne°ekne, mnohem lepÜφ je u₧ivateli nabφdnout seznam ·sek∙ podle nßzvu. Do tabulky vÜak chceme ulo₧it Φφslo. Nejvhodn∞jÜφm prvkem pro takovΘ zobrazenφ je ComboBox. Abychom nemuseli hodnoty seznamu plnit sami, pou₧ijeme rad∞ji DataCombo, kterΘ navß₧eme na otev°enou tabulku Useky.

P°idejte do projektu prvek DataCombo (menu Project -> Components, polo₧ka Microsoft DataList Controls 6.0) a vlo₧te jej na formulß°. Nazveme si jej t°eba dcUsek. Pro lepÜφ mo₧nost ·prav si op∞t vytvo°φme dotaz na v²b∞r seznamu ·sek∙. Nezveme jej qryUseky a bude vypadat takto:

SELECT id_useku, id_useku & ' - ' & nazev AS titulek
FROM Useky
ORDER BY id_useku

V²sledkem dotazu bude tabulka se dv∞ma sloupci, prvnφ bude Φφslo ·seku a druh² kombinace Φφsla s nßzvem.

Te∩ musφme sprßvn∞ navßzat prvek dcUsek na ob∞ tabulky. Pro napln∞nφ seznamu nßm bude slou₧it dotaz qryUseky. K≤d pro navßzßnφ prvku umφstφme taktΘ₧ do udßlosti Form_Load.

dcUsek.DataField = "usek"
Set dcUsek.DataSource = lrsMain

'Seznam useku
Set lrsUseky = New ADODB.Recordset
lrsUseky.Open "qryUseky", lconMain, adOpenStatic, adLockOptimistic
dcUsek.BoundColumn = "id_useku"
dcUsek.ListField = "titulek"
Set dcUsek.RowSource = lrsUseky

Nejd°φve jsme tedy prvku °ekli, v kterΘ tabulce (Recordsetu) a do kterΘho sloupce bude uklßdat hodnoty. Dßle jsme otev°eli nov² Recordset lrsUseky, kter² bude taktΘ₧ lokßlnφ v rßmci formulß°e, a ten jsme nastavili jako zdroj pro seznam. Vlastnost BoundColumn urΦuje, kterß hodnota z Recordsetu seznamu se ulo₧φ do sloupce nastavenΘho v DataField a vlastnost ListField urΦuje zase zobrazen² sloupec.

Tφmto jsme vytvo°ili kompletnφ k≤d pro zobrazovßnφ dat z tabulky Zamestnanci. Ale naÜemu k≤du jeÜt∞ n∞co chybφ. Vytvo°ili jsem jednu instanci objektu Connection a dv∞ instance objektu Recordset avÜak nikde u₧ se nestarßme o jejich uzav°enφ a uvoln∞nφ z pam∞ti. Visual Basic by sice m∞l vÜechno provΘst za nßs, ale je lepÜφ se na nic nespolΘhat a rad∞ji si to obslou₧it sßm. Proto p°idßme do udßlosti Form_QueryUnload k≤d pro uzav°enφ vÜech objekt∙ a uvoln∞nφ z pam∞ti.

Private Sub Form_QueryUnload(Cancel As Integer, UnloadMode As Integer)
  lrsUseky.Close
  Set lrsUseky = Nothing
  lrsMain.Close
  Set lrsMain = Nothing
  lconMain.Close
  Set lconMain = Nothing
End Sub

Prohlφ₧eΦ dat jsme u₧ vytvo°ili, te∩ p°ijde to nejd∙le₧it∞jÜφ, a to je vklßdßnφ, editace a mazßnφ dat. SamotnΘ akce nejsou p°φliÜ slo₧itΘ. P°idejte na formulß° t°i tlaΦφtka. Prvnφ, cmdNovy bude slou₧it pro nastavenφ p°idßvßnφ (tzn. vyprßzdn∞nφ TextBox∙, nastavenφ Recordsetu atd.), dalÜφ dv∞ budou pro ulo₧enφ zm∞n, cmdUlozit a odvolßnφ zm∞n, cmdZrusit. Prvnφ k≤d u t∞chto tlaΦφtek bude velmi jednoduch².

Private Sub cmdNovy_Click()
  lrsMain.AddNew
End Sub

Private Sub cmdUlozit_Click()
  lrsMain.UpdateBatch adAffectAll
  lrsMain.MoveLast
End Sub

Private Sub cmdZrusit_Click()
  lrsMain.CancelUpdate
End Sub

Metoda AddNew nastavφ Recordset lrsMain do editaΦnφho m≤du p°idßvßnφ zßznamu a proto₧e mßme TextBoxy i ComboBox napojeny na tento Recordset, automaticky se sami vyprßzdnφ. P°i uklßdßnφ zase staΦφ zavolat metodu UpdateBatch, kterß ulo₧φ zm∞ny do databßze a proto₧e chceme nov² zßznam hned zobrazit, p°esuneme se na konec tabulky. ZruÜenφ zm∞n je op∞t provedeno pouze jednou metodou.

DalÜφ z vlastnostφ, kterΘ chceme p°idat, je editace dat. Proto₧e objekt Recordset umo₧≥uje vyvolat editaci bez toho, abychom museli volat metodu Edit, vypl²vß z toho, ₧e zm∞nφ-li u₧ivatel jakoukoliv polo₧ku, zm∞na se ulo₧φ automaticky sama. JedinΘ co tedy m∙₧eme chtφt p°idat, je umo₧nit u₧ivateli ulo₧enφ zm∞ny nezßvisle. Proto k≤d pro ulo₧enφ p°idßme pod tlaΦφtko cmdUlozit. P°i p°idßvßnφ jsme se p°esunuli na konec tabulky, p°i editaci je jak²koliv p°esun samoz°ejm∞ nep°φpustn², proto musφme nejd°φve otestovat, v jakΘ fßzi editace se objekt lrsMain nachßzφ. Pokud ve fßzi p°idßnφ zßznamu, ulo₧φme zm∞ny a p°esuneme se na konec, pokud v jakΘkoliv jinΘ fßzi, pouze ulo₧φme zm∞ny. Nov² k≤d bude vypadat takto:

Private Sub cmdUlozit_Click()
  If lrsMain.EditMode = adEditAdd Then
    lrsMain.UpdateBatch adAffectAll
    lrsMain.MoveLast
  Else
    lrsMain.UpdateBatch adAffectAll
  End If
End Sub

Poslednφm ·kolem je p°idßnφ tlaΦφtka pro mazßnφ zßznam∙. TlaΦφtko pojmenujeme t°eba cmdSmazat. Co vÜechno musφ takov² k≤d pro smazßnφ obsahovat? Tak nejd°φve bychom m∞li otestovat, zda jsou v tabulce n∞jakΘ zßznamy. Pokud ano, sma₧eme zßznam pomocφ metody Delete. Dßle bychom m∞li op∞t otestovat poΦet zßznam∙. Pokud n∞jakΘ z∙stali, posuneme se nap°. na nßsledujφcφ zßznam, pokud ne, je t°eba vyprßzdnit TextBoxy.

Private Sub cmdSmazat_Click()
  With lrsMain
    If .RecordCount > 0 Then
      .Delete adAffectCurrent
      If .RecordCount > 0 Then
        .MoveNext
        If .EOF Then .MoveLast
      Else
        .Requery
      End If
    End If
  End With
End Sub

DalÜφ v∞c, kterß u₧ivateli pom∙₧e k lepÜφ orientaci v programu, je zapφnßnφ a vypφnßnφ tlaΦφtek, kterß jsou v danou chvφli pou₧itelnß. Logicky to bude fungovat nßsledovn∞.

  • B∞₧n∞ jsou pou₧itelnß tlaΦφtka pro pohyb, pro p°idßnφ, smazßnφ a ulo₧enφ zßznamu.
  • TlaΦφtko cmdZrusit b∞₧n∞ pou₧itelnΘ nenφ, nenφ pot°eba.
  • P°i p°idßnφ zßznamu nßs budou zajφmat pouze tlaΦφtka cmdUlozit a cmdZrusit. VÜechna ostatnφ by m∞la b²t vypnuta.
  • Pokud tabulka neobsahuje ₧ßdnΘ zßznamy, m∞lo by b²t pou₧itelnΘ pouze tlaΦφtko cmdNovy.

Proto₧e zapφnßnφ a vypφnßnφ tlaΦφtek bude skoro v ka₧dΘ udßlosti formulß°e, vytvo°φme si pro n∞j proceduru.

Private Sub NastavTlacitka(ByVal bolVal As Boolean)
  Dim cmdp As CommandButton

  cmdNovy.Enabled = bolVal
  cmdUlozit.Enabled = IIf(lrsMain.RecordCount > 0, True, False)
  cmdSmazat.Enabled = IIf((lrsMain.RecordCount > 0), bolVal, False)
  cmdZrusit.Enabled = Not bolVal
  For Each cmdp In cmdPohyb
    cmdp.Enabled = IIf((lrsMain.RecordCount > 0), bolVal, False)
  Next cmdp
End Sub

Volßnφ procedury p°idßme do t∞chto udßlostφ:

  • Form_Load, proto₧e tlaΦφtka je t°eba nastavit hned p°i startu aplikace (NastavTlacitka True).
  • cmdNovy_Click, proto₧e chceme zobrazit pouze dv∞ tlaΦφka, cmdUlozit a cmdZrusit (NastavTlacitka False).
  • cmdSmazat_Click, do Φßsti za volßnφ metody Requery, proto₧e ta nastane v p°φpad∞, ₧e je tabulka prßzdnß, tudφ₧ je pot°eba vypnout vÜe krom∞ p°idßnφ zßznamu (NastavTlacitka True).
  • cmdUlozit_Click, vypnutφ tlaΦφtka zmdZrusit a zapnutφ ostatnφch (NastavTlacitka True).
  • cmdZrusit_Click, stejn∞ jako u cmdUlozit_Click (NastavTlacitka True).

Cel² hotov² p°φklad i s databßzφ si m∙₧ete zkopφrovat. Oproti tomu, co jsme vytvo°ili zde, mß jednoduÜe oÜet°eny chyby, na kterΘ jsme v textu nedbali a takΘ jednu malou zm∞nu p°i p°idßvßnφ zßznamu. Co by se vÜak hodilo do databßzovΘho formulß°e je vyhledßvßnφ zßznam∙ a zobrazovßnφ Φφsla aktußlnφho a celkovΘho poΦtu zßznam∙. Zkuste si to vytvo°it ka₧d² sßm a zaÜlete nßm vaÜe °eÜenφ. NejlepÜφ zve°ejnφme v dalÜφm Φφsle (nßpov∞da - na zobrazenφ aktußlnφho zßznamu vyu₧ijte udßlost MoveComplete objektu lrsMain).