Ve čtvrté části naší série článků o vývoji GUI aplikace v .NET pomocí nástrojů Progress OpenEdge pokročíme významně dále. Zatímco v předchozích článcích jsme se zabývali výhradně čtením dat z progressovské databáze ze zdroje "BindingSource" a jejich zobrazením v .NET GUI, dnes si popíšeme, jak lze data v databázi aktualizovat údaji zadanými uživatelem. A jak jsme si již zvykli, použijeme výhradně protředků OpenEdge ABL. Tak do toho.

Nejprve si ukážeme smazání záznamu. Ve formuláři fCustomer přidáme tlačítko s názvem Delete, jméno např. Del.

Poklepáním na tlačítko vytvoříme metodu Del_Click, do které vepíšeme následující kód:

DEFINE BUFFER bCust FOR Customer.
DEFINE BUFFER bOrder FOR Order.
DEFINE VARIABLE lDel AS LOGICAL NO-UNDO.
DEFINE VARIABLE iActCustNum AS INTEGER NO-UNDO.
MESSAGE "Do you want to delete this record?" SKIP 
bsCust:InputValue["Name"]:ToString() SKIP 
bsCust:InputValue["City"]:ToString() SKIP
VIEW-AS ALERT-BOX QUESTION BUTTONS YES-NO UPDATE lDel.
IF NOT lDel THEN 
RETURN.

Nyní jsme v polovině. Můžeme si ukázat zobrazení dialogového okna s dotazem (povšimněte si, že dialogové okno se zobrazí standardním příkazem ABL MESSAGE), zatím bez smazání.

Na konec metody Del_Click (za příkaz RETURN) dopíšeme kód pro smazání řádku tabulky Customer i souvisejících záznamech v tabulce Order (formálně bychom měli pokračovat dále k OrderLine).


IF NOT lDel THEN 
RETURN.

iActCustNum = INTEGER(bsCust:InputValue["CustNum"]:ToString()).
DO TRANSACTION ON ERROR UNDO, RETURN:
FOR FIRST bCust WHERE bCust.CustNum = iActCustNum:
FOR EACH bOrder WHERE bOrder.CustNum = bCust.CustNum.
DELETE bOrder.
END.
DELETE bCust.
END. 
END.

Aby program správně fungoval, je třeba mít na paměti, že v Data Dictionary mohou být definovány databázové triggery, které chrání před ztrátou dat. Funkčnost programu je nutné ověřit v souvislosti s těmito triggery, které je nutné případně upravit, v nejhorším případě zakázat.

Po odstranění záznamu formulář automaticky neaktualizuje data. Musíme to tedy udělat sami. Kód přidáme na konec metody Del_Click:

bsCust:MovePrevious().
iPos = bsCust:POSITION.
refreshData().
bsCust:POSITION = iPos.

Proměnná iPos je definována jako integer. Po vymazání řádku bude kursor umístěn na předchozím řádku.

Příklad smazání řádku je nejjednodušší, protože nevyžaduje dodatečné komponenty pro úpravu dat. Nyní přidáme tlačítka Edit a Cre (pro create) a analogickým způsobem pro ně vygenerujeme metody pro událost Click.

Obvykle mohou být stejné funkce vytvořeny různými způsoby. Můžete například využít možnosti, které nabízí přímo DataGrid, ale podle Wiesława může tento přístup způsobit neočekávané problémy a není jasně popsán. Je proto vhodnější vytvořit samostatné okno pro úpravy, které poslouží pro operace Edit i Create. Vytvoříme nový formulář fCustEdit. Protože se jedná o postup analogický tomu, který byl podrobně popsán v první části série, zde uvedeme pouze slovní popis. Při změnách se omezíme na čtyři sloupce tabulky.

Do formuláře fCustEdit vložíme pole typu TextBox: CustNum, Name, City, Country a jejich návěští.

Dále přidáme tlačítka btnOK (návěští OK), btnCancel (návěští Cancel) a nastavíme jejich předdefinované funkce:

Ve vlastnostech fCustEdit nastavíme parametr CancelButton -> btnCancel.
Ve vlastnostech btnOK nastavíme DialogResult -> OK.

 

Ve formuláři fCustomer upravíme metodu Edit_Click.

METHOD PRIVATE VOID Edit_Click( INPUT sender AS System.Object, INPUT e AS System.EventArgs ):
   DEFINE VARIABLE oCustEdit AS fCustEdit NO-UNDO.
oCustEdit = NEW fCustEdit().
WAIT-FOR oCustEdit:ShowDialog().
DELETE OBJECT oCustEdit.
RETURN.

Tlačítko Edit již funguje, ale formulář je prázdný.

K úpravě dat lze použít různé metody. My definujeme proměnné a jejich vlastnosti pro každé editované pole ve formuláři fCustEdit. Např. PName pro pole Name a analogicky i pro ostatní pole.

DEFINE PUBLIC PROPERTY pName AS CHARACTER NO-UNDO 
GET.
SET.

Takto definované proměnné musíme ve vytvořeném kódu mapovat a pole formuláře fCustEdit. Nejprve to uděláme pro událost Shown.

METHOD PRIVATE VOID fCustEdit_Shown( INPUT sender AS System.Object, INPUT e AS System.EventArgs ):
   ASSIGN
      textBoxName:TEXT = pName
      textBoxCity:TEXT = pCity
      textBoxCountry:TEXT = pCountry
      textBoxCustNum:TEXT = string(pCustNum).
   RETURN.

END METHOD.

V okně se nyní zobrazí hodnoty definovaných proměnných, které pozdějí naplníme hodnotami z tabulky Customer. Hodnota sloupce CustNum je přiřazována databázovým triggerem, proto neumožníme editaci pole CustNum ve formuláři. Nastavíme vlastnosti Enabled False a pro lepší vzhled také BorderStyle None.

Pro uložení dat po volbě tlačítka btnOK musíme hodnoty z formuláře zapsat zpět do proměnných.

METHOD PRIVATE VOID btnOK_Click( INPUT sender AS System.Object, INPUT e AS System.EventArgs ):
   ASSIGN 
      pName    = textBoxName:Text
      pCity    = textBoxCity:Text
      pCountry = textBoxCountry:TEXT.
   RETURN.

END METHOD.

Dále musíme namapovat předávání hodnot mezi formuláři fCustomerfCustEdit, a to oběma směry a včetně zápisu upravených dat do databáze. To uděláme v metodě Edit_Click formuláře fCustomer. Nejprve v nově vytvořené instanci objektu fCustEdit nastavíme proměnné (vlastnosti) pCustNum, pName, pCity a pCountry.

...
oCustEdit = NEW fCustEdit().
                   
oCustEdit:pCustNum = int(bsCust:InputValue["CustNum"]:ToString()).
oCustEdit:pName = bsCust:InputValue["Name"]:ToString().
oCustEdit:pCity = bsCust:InputValue["City"]:ToString().
oCustEdit:pCountry = bsCust:InputValue["Country"]:ToString().
iActCustNum = INT(bsCust:InputValue["CustNum"]:ToString()).
                
WAIT-FOR oCustEdit:ShowDialog().
...

Nyní se ve formuláři fCustEdit zobrazí správné hodnoty sloupců z tabulky Customer.

A nakonec doplníme do téže metody zápis upravených dat do databáze:

...
WAIT-FOR oCustEdit:ShowDialog().
DO TRANSACTION ON ERROR UNDO, RETURN:
   FOR FIRST bCust WHERE bCust.CustNum = iActCustNum SHARE-LOCK :
      ASSIGN
         bCust.Name    = oCustEdit:pName
         bCust.City    = oCustEdit:pCity
         bCust.Country = oCustEdit:pCountry.
   END.
END.

bsCust:Refresh().
...

Editace nám nyní funguje. Analogicky můžeme postupovat při vytváření nového řádku. Kód metody Cre_Click může vypadat např. takto.

DEFINE BUFFER bCust FOR Customer.
DEFINE VARIABLE oCustEdit AS fCustEdit NO-UNDO.
DEFINE VARIABLE rRowid AS ROWID NO-UNDO.
oCustEdit = NEW fCustEdit().
                   
WAIT-FOR oCustEdit:ShowDialog().                
        
DO TRANSACTION ON ERROR UNDO, RETURN:
   CREATE bCust.
   ASSIGN
      bCust.Name    = oCustEdit:pName
      bCust.City    = oCustEdit:pCity
      bCust.Country = oCustEdit:pCountry.
   rRowid = ROWID(bCust).
END.

refreshData().
bsCust:HANDLE:reposition-to-rowid(rRowid).
RETURN.

Proměnná rRowid slouží k nastavení kursoru formuláře na nově vytvořený záznam. 

Někomu může vadit, že v poli CustNum je zobrazena 0 (nula). V té chvíli ještě není hodnota známa, protože se přiřazuje databázovým triggerem až po vyplnění formuláře. To bychom mohli řešit tím že příkaz CREATE bCust napíšeme na začátek metody a hodnotu předámě formuláři fCustEdit, museli bychom ale složitěji vyřešit transakční zpracování.

Tím je program v zásadě hotov. Na závěr si ještě ukážeme jednu užitečnou vlastnost, a tou je volba operací Create, Edit, Delete pomocí klávesových zkratek - klávesy Insert, Return, Delete.

Ve vlastnostech formuláře fCustomer nastavíme KeyPreview True.

Na událost KeyDown objektu dgvCustomer namapujeme metodu dataGridView1_KeyDown (Událost KeyDown v .NET nemá nic společného se "šipkou dolů", je to událost vygenerovaná okamžitě po stisknutí klávesy - bez uvolnění. Analogické události jsou KeyPress pro stisk a uvolnění a KeyUp pro uvolnění klávesy).

Kód metody v fCustomer bude vypadat takto:

A nakonec doplníme do této metody zápis upravených dat do databáze:

METHOD PRIVATE VOID dataGridView1_KeyDown( INPUT sender AS System.Object, INPUT e System.Windows.Forms.KeyEventArgs ):
CASE e:KEYCODE:ToString():
WHEN "Insert" THEN Cre:PerformClick().
WHEN "Return" THEN Edit:PerformClick().
WHEN "Delete" THEN Del:PerformClick().
END CASE.
RETURN.

END METHOD.

Cre, Edit, Del jsou názvy dříve vytvořených tlačítek.

Úplně na závěr do formuláře fCustomer ještě vložíme čítač řádků. Přidáme objekt typu label s názvem labInfo a návěštím Records. V metodě refreshData na konci přidáme kód:

A nakonec doplníme do této metody zápis upravených dat do databáze:

labInfo:TEXT = "Liczba rekordów: " + STRING(bsCust:Count).

Při každém vyvolání této metody se zobrazí počet záznamů v tabulce Customer.

CitacZaznamu.gif

Tím jsme dokončili základní vývojový cyklus aplikace s požitím .NET v ABL. V době přípravy tohoto textu byla uvolněna verze Progress OpenEdge 12 s novými vlastnostmi, proto se se k tomuto tématu ještě vrátíme. 


Poznámka
: Naše příklady ukazují přímou komunikaci .NET s daty v progressovské databázi. Pokud bychom chtěli pracovat s aplikacemi běžícími na aplikačním serveru, analogicky nakonfigurujeme "BindingSource" na definované struktuře ProDataSet s dočasnými tabulkami.

Autoři: Michal Džmuráň volně dle Piotr Tucholski, Wiesław Kurzątkowski (Novum)


Nahoru