Platforma.NET laboratorium 5 Prowadzący: mgr inż. Tomasz Jaworski Strona WWW: http://tjaworski.kis.p.lodz.pl/ Visual Basic.NET ASP.NET + Microsoft SQL Server Połączenie z bazą serwera Microsoft SQL Server Środowisko programistyczne Microsoft Visual Studio posiada podstawową funkcjonalność pozwalającą na pracę z wieloma relacyjnymi bazami danych, poprzez dostępne sterowniki m.in. ADO, ODBC. W trakcie laboratoriów wykorzystane zostanie tylko połączenia z bazą danych serwera MS SQL Express. Aby dodać nowe połączenie z serwerem MSSQL należy wybrać: Add Connection jeśli chcemy połączyć się z istniejącą bazą danych, lub Create New SQL Server Database jeśli chcemy utworzyć nową bazę danych na istniejącym serwerze. Na potrzeby tego ćwiczenia zostanie utworzona nowa baza danych o nazwie baza_testowa. W polu Server name wybieramy lokalny serwer MSSQL. Wcześniej jednak należy dopilnować, aby na lokalnym komputerze były uruchomione dwie usługi: SQL Server (SQLEXPRESS) 1 faktyczny serwer, oraz usługa SQL Server Browser pozwalająca m.in. na listowanie lokalnych instancji 2. W polu New database name podajemy nazwę tworzonej bazy danych, a następnie zatwierdzamy operację tworzenia. 1 Przy czym tekst SQLEXPRESS to nazwa instancji serwera. Instancji może być wiele na lokalnym komputerze i nie muszą nazywać się SQLEXPRESS. Jest do nazwa domyślnie proponowana podczas instalacji serwera MS SQL Express Edition 2 http://msdn.microsoft.com/en-us/library/ms165724%28v=sql.90%29.aspx 1
Po utworzeniu nowej bazy danych na pracującym serwerze MSSQL, Visual Studio doda nowe połączenie do listy istniejących (jak na rysunku). Po stworzeniu bazy danych należy utworzyć tabelę danych o nazwie np. kontakty. Kod SQL, tworzący tabelę kontakty, znajduje się na końcu instrukcji niniejszego ćwiczenia. Tabela ta będzie wykorzystana w ćwiczeniu i dla ułatwienia powinna mieć strukturę przedstawioną na poniższym rysunku. Pole id ma zawierać unikalny identyfikator wiersza oraz ma być kluczem podstawowym 3 (Primary Key). Pole nazwa ma być tekstem dowolnej długości, np. 10. Aby utworzyć nową tabelę, należy skorzystać z polecenia Add New table z menu podręcznego elementu Tables, a tak utworzoną tabele należy wypełnić przykładowymi danymi (Kontakty -> Show Table Data). Utworzone połączenie do nowej bazy danych posiada pewne własności. Aby się z nimi zapoznać, należy wybrać odpowiednie połączenie z okna Server Explorer. W tym ćwiczeniu będzie to tj\sqlexpress.baza_testowa.dbo. Następnie należy wcisnąć klawisz F4, lub wybrać polecenie View -> Properties Window. Jedną z własności jest Connection String. Jest to tekst określający parametry połączenia z bazą danych. Jego odpowiednik dla bazy Microsoft SQL Server Compact został pokazany na poprzednim laboratorium. Przykładowy tekst konfigurujący połączenie może mieć następującą postać: "Data Source=TJ\SQLEXPRESS;Initial Catalog=baza_testowa;Integrated Security=True" 3 Do pola można dodać klucz podstawowy w trybie edycji struktury tabeli. Na wybranym polu należy kliknąć prawym przyciskiem myszy i z menu kontekstowego wybrać Set Primary Key. Jeśli pole nie będzie posiadało klucza podstawowego, kod generujący odpowiednie polecenia dla adaptera danych, np.: sql_adapter.deletecommand = cmd_builder.getdeletecommand() nie zostanie poprawnie wykonany wygeneruje wyjątek. 2
Pierwsza aplikacja webowa w ASP.NET Do stworzenia nowej, pustej aplikacji wykorzystuje się Kreatora odpowiedniego projektu. W tym wypadku będzie to ASP.NET Web Application: Ćwiczenie Celem ćwiczenia będzie stworzenie formularza (strony WWW) pozwalającej na przeglądanie i edycję danych w tabeli kontakty. Tworzenie formularza Poniżej przedstawiony jest formularz prostej aplikacji ASP.NET, tworzonej w ramach tego ćwiczenia. Formularz został podzielony na 4 oddzielne bloki <div>...<div>, ułożone pionowo (oznaczone na powyższym rysunku czerwonymi prostokątami). Aby przygotować formularz do ćwiczenia, należy wykonać następujące kroki: 3
1. Należy przełączyć się w połączonego widok kodu i formularza (ikonka Split). Kod pustego formularza wygląda następująco: <%@ Page Language="vb" AutoEventWireup="false" CodeBehind="WebForm1.aspx.vb" Inherits="web1.WebForm1" %> <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/tr/xhtml1/dtd/xhtml1-transitional.dtd"> <html xmlns="http://www.w3.org/1999/xhtml" > <head runat="server"> <title></title> </head> <body> <form id="form1" runat="server"> <div> </div> </form> </body> </html> W obszarze znacznika <form> znajduje się znacznik <div> </div>. Zgodnie z założeniem, formularz ma być podzielony na 4 części, zatem należy dodać jeszcze 3 bloki <div> </div> w znaczniku <form>. Operacje tę można wykonać również poprzez przeciągnięcie znacznika z paska narzędzi. 2. Pierwszy znacznik zawiera napis Aplikacja. Można go wpisać z poziomu edytora wizualnego (dolna cześć okna edycyjnego) lub jako kod HTML o treści: <div> <br /> <center>aplikacja</center> <br /> </div> 3. Drugim blokiem jest skonfigurowana kontrolka GridView pozwalająca na przeglądanie danych w postaci tabeli (w bardzo podobny sposób do DataGridView z poprzednich zajęć laboratoryjnych). Aby otrzymać kontrolkę o odpowiednich parametrach należy: a. Ustawić własność AutoGenerateColumns = False. Zablokuje to możliwość automatycznego tworzenia kolumn widocznych w kontrolce, odpowiadających kolumnom z tabeli, którą chcemy wyświetlić. Wartość False pozwala nam wyświetlać tylko te kolumny, które są potrzebne ze względu na projekt, oraz pozwala na indywidualną konfigurację parametrów każdej kolumny. b. Ustawienie własności HorizontalAlign = Center spowoduje wycentrowanie tabelki na formularzu. Można również podać domyślną szerokość, Width = 900px. c. W okienku własności wybranej kontrolki (Properties) znajduje się przycisk Auto Format. Dostępny jest on również z poziomu ikony [ > ] pokazanej na rysunku obok. Funkcja tego przycisku pozwala na wybranie jednego z domyślnych wyglądów kontrolki. Dzięki temu można szybko wybrać pożądaną kolorystykę bez ustawiania wielu parametrów indywidualnie. 4
d. Należy skonfigurować kolumny. W tym celu należy rozpocząć edycję kolekcji Columns (przycisk ( ) przy własności Columns) lub uruchomić funkcję Edit Columns Edycja kolumn Uruchomiony zostanie kreator, pozwalający na praktycznie dowolne ustawienie parametrów poszczególnych kolumn: Kreator Fields udostępnia kilka typów kolumn. W tym ćwiczeniu wykorzystane zostaną tylko dwa typy: BoundField, pozwalający na wyświetlanie informacji z wybranej kolumny oraz ButtonField, czyli przycisk wyświetlany w każdym wierszu wyświetlanej kontrolki. Celem pracy z tym kreatorem jest dodanie kilku kolumn dla danych wyświetlanych w kontrolce, ustawienie im nagłówków (co nie jest możliwe przy automatycznym tworzeniu kolumn), oraz skojarzenie przycisków Edytuj oraz Usuń z każdym wierszem. Należy dodać 4 kolumny typu BoundField. Dla każdej z nich należy ustawić dwie własności: a. HeaderText = tekst nagłówka wyświetlanego na stronie WWW, np.: ID wiersza, Imię, Nazwisko, Adres. b. DataField musi zawierać nazwę kolumny danych z zapytania SELECT przekazywanego do adaptera danych (patrz: poprzednie laboratorium). Ponieważ w części opisującej implementację wykorzystane zostanie zapytanie SELECT, które nie zmodyfikuje nazw kolumn, dla poszczególnych kolumn należy wpisać odpowiednio: id, imie, nazwisko, adres. c. Przydatnym parametrem kolumny jest własność Visible. Pozwala ona na określenie, czy dana kolumna będzie widoczna w tabeli w przeglądarce (True) czy nie (False). Wartością domyślną jest True. Aby przetestować tę funkcjonalność, należy ustawić wartość własności Visible = False dla kolumny id ( ID wiersza ). Każdy rekord powinien posiadać możliwość edycji lub usunięcia (akurat na potrzeby tego laboratorium). Aby to zrealizować, potrzebne będą dwie kolumny ButtonField. Własność Text takiej kolumny zawiera napis wyświetlany na stronie WWW (tytuł przycisku). Należy ustawić go odpowiednio na Edytuj oraz Usuń. Aby możliwa była reakcja na kliknięcie jednego z tych przycisków, należy skorzystać ze zdarzenia RowCommand kontrolki GridView. Jednym z parametrów tego zdarzenia jest identyfikator przycisku, który ustawia się indywidualnie dla każdego z nich we własności CommandName, np. cmd_edytuj oraz cmd_usun. Własność ButtonType pozwala na określenie wyglądu przycisku w kolumnie. Może to być typowy odnośnik (Link), przycisk (Button) lub obraz (Image). Proszę zwrócić uwagę na własność SelectedIndex = -1. Wartość -1 oznacza, że żaden rekord nie jest wybrany. Wartości >= 0 oznaczają numer wiersza tabeli kontrolki GridView. Informacja ta będzie przydatna podczas wybierania rekordu, który chcemy edytować. 5
Definicja utworzonej i skonfigurowanej kontrolki GridView powinna być zbliżona do poniższego przykładu: <div style="text-align: center"> <asp:gridview ID="GridView1" runat="server" HorizontalAlign="Center" Width="819px" BackColor="White" BorderColor="#999999" BorderStyle="None" BorderWidth="1px" CellPadding="3" DataKeyNames="id" GridLines="Vertical" AutoGenerateColumns="False"> <RowStyle BackColor="#EEEEEE" ForeColor="Black" /> <Columns> <asp:boundfield DataField="id" HeaderText="ID" Visible="False"> <ControlStyle Width="32px" /> </asp:boundfield> <asp:boundfield DataField="imie" HeaderText="Imię"> <ControlStyle Width="100px" /> </asp:boundfield> <asp:boundfield DataField="nazwisko" HeaderText="Nazwisko"> <ControlStyle Width="100px" /> </asp:boundfield> <asp:boundfield DataField="adres" HeaderText="Adres"> <ControlStyle Width="50px" /> </asp:boundfield> <asp:buttonfield CommandName="cmd_edytuj" Text="Edytuj" /> <asp:buttonfield CommandName="cmd_usun" Text="Usuń" /> </Columns> <FooterStyle BackColor="#CCCCCC" ForeColor="Black" /> <PagerStyle BackColor="#999999" ForeColor="Black" HorizontalAlign="Center" /> <SelectedRowStyle BackColor="#008A8C" Font-Bold="True" ForeColor="White" /> <HeaderStyle BackColor="#000084" Font-Bold="True" ForeColor="White" /> <AlternatingRowStyle BackColor="#DCDCDC" /> </asp:gridview> <br /> </div> 4. Trzecim blokiem jest obszar, w którym dokonywana będzie edycja wiersza wybranego z tabeli. W tym ćwiczeniu będą to 4 pola tekstowe. Aby ułatwić sobie formatowanie i pozycjonowanie pól tekstowych na formularzu, można skorzystać z tabeli HTML (Table z grupy HTML). Aby skorzystać z pól tekstowych, należy nadać im identyfikatory. Aby to zrobić, należy do pola (ID) każdej z kontrolek wpisać jej nazwę, odpowiednio: edtimie, edtnazwisko, edtadres, edtemail. Definicja tego bloku powinna być następującej postaci: <div> <table style="width: 100%;"> <tr> <td width="25%">imię</td> <td width="25%">nazwisko</td> <td width="25%">adres</td> <td width="25%">email</td> </tr> <tr> <td><asp:textbox ID="edtImie" runat="server" Width="223px"></asp:TextBox></td> <td><asp:textbox ID="edtNazwisko" runat="server" Width="224px"></asp:TextBox></td> <td><asp:textbox ID="edtAdres" runat="server" Width="227px"></asp:TextBox></td> <td><asp:textbox ID="edtEmail" runat="server" Width="226px"></asp:TextBox></td> </tr> </table> <br /> </div> 5. Ostatnim blokiem formularza będzie grupa dwóch przycisków: Zapisz rekord oraz Dodaj nowy. Ich zadaniem będzie odpowiednio: a. zapisanie zmian wprowadzonych do aktualnie edytowanego rekordu, oraz b. dodanie nowego, pustego rekordu do tabeli i ustawienie aplikacji w tryb edycji tego rekordu Po dodaniu dwóch przycisków (Button z grupy Standard) należy im nadać odpowiednio: nazwy w polu (ID), odpowiednio: btnzapisz, btndodaj, tytuły w polu Text, odpowiednio: Zapisz rekord, Dodaj nowy. 6
Definicja tego bloku powinna być następującej postaci: <div style="text-align: center"> <asp:button ID="btnZapisz" runat="server" Text="Zapisz rekord" /> <asp:button ID="btnDodaj" runat="server" Text="Dodaj nowy" /> </div> Tak stworzony formularz należy odpowiednio oprogramować. 1. Należy dodać przestrzeń nazw z klasami dostępu do danych: Imports System.Data.SqlClient Nie jest to konieczne, ale ułatwia pisanie kodu i poprawia jego czytelność. Podobnie do poprzedniego laboratorium, należy stworzyć pola klasy formularza, dostępne z poziomu wszystkich jego metod: Dim sql_conn As SqlConnection Dim sql_cmd As SqlCommand Dim sql_adapter As SqlDataAdapter Dim tabela As DataTable SqlConnection jest klasą obsługującą połączenie z bazą danych, SqlCommand obsługuje zapytania wysyłane do bazy, SqlDataAdapter umożliwia pobranie wyników zapytania, jednak nie dla wszystkich zastosowań wykorzystanie tej klasy jest konieczne, DataTable przechowuje wynik zapytania wybierającego, SELECT, czyli tabelę z pewną ilością kolumn oraz wierszy, wynikająca z samego zapytania. Przydatna będzie również funkcja wyświetlająca komunikat po stronie przeglądarki WWW. Na forach ASP.NET można znaleźć przypadki, w których dyskutanci proponują skorzystanie z metody System.Windows.Forms.MessageBox.Show( ). Okno komunikatu zostanie oczywiście wyświetlone, jednak tylko po stronie serwera (nie klienta), co skutecznie zablokuje pracę aplikacji. Poniższy kod wyświetli w poprawny sposób typowy komunikat, dostępny z poziomu języka JavaScript. Sub Alert(ByVal msg As String) msg = msg.replace("'", "\\'") Response.Write(String.Format("<script>alert('{0}');</script>", msg)) End Sub Zdarzenie Load formularza: Aby dodać zdarzenie formularza, w przeciwieństwie do klasycznego interfejsu GUI, należy skorzystać dwóch list rozwijanych dostępnych w oknie edycji kodu: Nowo utworzone zdarzenie Load może zawierać następujący kod: Protected Sub Page_Load(ByVal sender As Object, ByVal e As System.EventArgs) Handles Me.Load Dim conn_str = "Data Source=TJ\SQLEXPRESS;Initial Catalog=baza_testowa;" + _ "Integrated Security=True;Pooling=False" sql_conn = New SqlConnection(conn_str) Try sql_conn.open() Catch ex As Exception Alert("Błąd połączenia z bazą danych!") End Try tabela = New DataTable() 7
sql_adapter =New SqlDataAdapter("SELECT id, imie, nazwisko, adres, email FROM kontakty", _ sql_conn) Dim cmd_builder = New SqlCommandBuilder(sql_adapter) sql_adapter.deletecommand = cmd_builder.getdeletecommand() sql_adapter.insertcommand = cmd_builder.getinsertcommand() sql_adapter.updatecommand = cmd_builder.getupdatecommand() sql_adapter.fill(tabela) GridView1.DataSource = tabela (1) GridView1.DataBind() End Sub Przedstawiony kod jest praktycznie identyczny z kodem omawianym na poprzednich laboratoriach. Na uwagę zasługuje jednak linia (1). Jej zadaniem jest połączenie danych z podanego źródła (w tym wypadku jest do tabela tabela) z szablonem zdefiniowanym wcześniej (w edytorze formularza). Zdarzenie RowCommand kontrolki GridView1: Zdarzenie RowCommand odpowiada za reakcję programu na kliknięcie jednego z przycisków zdefiniowanych w każdym wierszu kontrolki GridView. Zadaniem poniższego kodu jest usunięcie wybranego wiersza lub zaznaczenie go, jako do edycji. 1. Reakcja na kliknięcie przycisku Edytuj Pole CommandName parametru zdarzenia, e, zawiera nazwę wpisaną wcześniej w polu CommandName kolumny dodanej do kontroli GridView. Zgodnie z oczekiwaniem, możliwe wartości są tylko dwie: "cmd_edytuj" oraz "cmd_usun". Informacja zwarta w polu CommandArgument zdarzenia pozwala określić numer wiersza, w którym dany przycisk został kliknięty. Należy pamiętać, że pole to jest tekstowe, a więc numer wiersza należy wcześniej zamienić na wartość liczbową (Integer.Parse(e.CommandArgument)). Po uzyskaniu numeru wybranego wiersza należy pobrać go z tabeli poprzez odwołanie się do kolekcji Rows: Dim wiersz = tabela.rows(wiersz_numer). Dane z wybranego wiersza zostaną następnie wykorzystane do wypełnienia pól tekstowych formularza (dla edycji przez użytkownika). Należy jeszcze zapamiętać numer wiersza wybranego do edycji po to, aby możliwe było zapisanie nowych wartości do tego wiersza. Metod postępowania w takim wypadku jest wiele, jednak na potrzeby tego ćwiczenia należy skorzystać z własności GridView1.SelectedIndex. Efektem będzie zapisanie numeru wiersza poddawanego edycji (lub -1, jeśli żaden wiersz nie jest edytowany) oraz wizualne (innym kolorem) zaznaczenie wybranego wiersza. ' reakcja na przycisk "Edytuj" If (e.commandname = "cmd_edytuj") Then Dim wiersz_numer = Integer.Parse(e.CommandArgument) Dim wiersz = tabela.rows(wiersz_numer) GridView1.SelectedIndex = wiersz_numer If TypeOf wiersz("imie") Is DBNull Then edtimie.text = "" Else edtimie.text = wiersz("imie") Przypadkiem, na który należy zwrócić uwagę jest sytuacja, w której pole tabeli danych ma wartość null (naturalne w bazach danych). Null reprezentuje rzeczywisty brak wartości, innymi słowy null nie jest napisem ani też pustym ciągiem znaków (""). 8
Przed przypisaniem zawartości takiego pola do własności Text kontrolki TextBox należy sprawdzić, czy pole nie przypadkiem jest puste. Jedną z metod zaprezentowano poniżej: If TypeOf wiersz("nazwisko") Is DBNull Then edtnazwisko.text = "" Else edtnazwisko.text = wiersz("nazwisko") Kod ten sprawdza, czy w polu wiersza, np. wiersz("nazwisko"), jest obiekt klasy DBNull. Jeśli tak, to do kontrolki pola tekstowego przesyłany jest pusty ciąg znaków (""). W przeciwnym wypadku, pobierana jest wartość pola tego wiersza (.Text = wiersz("nazwisko")). If TypeOf wiersz("adres") Is DBNull Then edtadres.text = "" Else edtadres.text = wiersz("adres") If TypeOf wiersz("email") Is DBNull Then edtemail.text = "" Else edtemail.text = wiersz("email") ' -------------------------------------------------------- ' rekacja na przycisk "Usuń" If (e.commandname = "cmd_usun") Then Dim wiersz_numer = Integer.Parse(e.CommandArgument) Dim wiersz = tabela.rows(wiersz_numer) wiersz.delete() sql_adapter.update(tabela) GridView1.DataBind() Reakcja na kliknięcie przycisku Usuń rozpoczyna się podobnie do reakcji na przycisk Edytuj. Pobierany jest numer wiersza, w którym został kliknięty przycisk oraz sam wiersz z tabeli tabela. Linia wiersz.delete() oznacza dany wiersz, jako usunięty. Natomiast linia sql_adapter.update(tabela) odpowiada za aktualizację fizycznej tabeli danych w bazie na podstawie tabeli w aplikacji (tabela). Zadaniem metody Update jest wykonanie zapytań UPDATE, INSERT oraz DELETE odpowiednio dla każdego wiersza źródła danych: Jeśli wiersz jest oznaczony jako usunięty, wykonywane jest zapytanie DELETE. Jeśli wiersz jest oznaczony jako nowy, wykonywane jest zapytanie INSERT. Jeśli wiersz jest oznaczony jako zmieniony (jedno lub więcej pól zostało zmodyfikowanych), wykonywane jest zapytanie UPDATE. Po pomyślnym wykonaniu aktualizacji bazy danych wykonywana jest metoda DataBind, opisana wcześniej. Zdarzenie Click kontrolki btndodaj: Zdarzenie ma za zadanie wykonać dwie operacje: dodać nowy wiersz do tabeli oraz ustawić aplikację w tryb edycji dodanego wiersza. (1) Dim nowy_wiersz = tabela.newrow() (2) tabela.rows.add(nowy_wiersz) 9
(3) sql_adapter.update(tabela) (4) GridView1.DataBind() (5) Dim numer_wiersza = tabela.rows.indexof(nowy_wiersz) (6) GridView1.SelectedIndex = numer_wiersza Kod (1) tworzy nowy wiersz na podstawie definicji tabeli danych zawartej w tabela. Obiekt nowy_wiersz jest typu DataRow (typ ten nie posiada publicznego konstruktora). Utworzony, na podstawie tabeli, wiersz może zostać w każdej chwili odrzucony (nie jest dodany do tabeli tabela). Kod (2) odpowiada za dodanie nowego wiersza do kolekcji wierszy w tabeli tabela. W takiej sytuacji wykonanie kodu (3) spowoduje fizycznie dodanie pustego wiersza w określonej (przez adapter danych) tabeli bazy danych. Kod (4) ponownie łączy szablon kontrolki GridView1 ze źródłem danych tabela. Teraz nowy wiersz będzie już widoczny w przeglądarce. Kod (5) pobiera pozycję nowego wiersza w kolekcji wierszy tabela. Ponieważ kontrolka GridView1 korzysta z tej tabeli, pozycja nowego wiersza w kolekcji będzie odpowiadała numerowi tego samego wiersza w oknie przeglądarki. Zatem uzasadniony jest kod (6). Kod (6), podobnie jak w przypadku edycji rekordu, ma za zadanie oznaczyć edytowany rekord. W tym przypadku będzie to nowy (pusty) rekord. Zdarzenie Click kontrolki btnzapisz: Celem zdarzenia jest zapisanie zmian wprowadzonych przez użytkownika, w wybranym wcześniej rekordzie, do fizycznej bazy danych. Przykładowy kod realizujący taką funkcjonalność może mieć następującą postać: (1) If (GridView1.SelectedIndex = -1) Then (1) Alert("Brak wybranego rekordu do zapisu") (1) Exit Sub (1) (2) Dim wiersz = tabela.rows(gridview1.selectedindex) (3) wiersz("imie") = edtimie.text (3) wiersz("nazwisko") = edtnazwisko.text (3) wiersz("adres") = edtadres.text (3) wiersz("email") = edtemail.text (4) sql_adapter.update(tabela) (4) GridView1.DataBind() (5) GridView1.SelectedIndex = -1 Kod (1) sprawdza, czy jakikolwiek rekord jest został wybrany do edycji. Wartość -1 oznacza brak rekordu do edycji, o czym użytkownik jest informowany stosownym komunikatem po kliknięciu na przycisk Zapisz rekord. Kod (2) pobiera z kolekcji wierszy tabela wybrany wiersz, po czym kod (3) ustawia wartości wybranych pól wartościami z odpowiednich kontrolek (pól tekstowych na formularzu). Kod (4) uaktualnia wiersz fizycznie, w bazie danych. Ponieważ jest to nowy wiersz, wykonane zostanie zapytanie INSERT. Zaktualizowana w ten sposób tabela zostaje ponownie przekazana do kontrolki GridView1. Kod (5) odpowiada usunięcie zaznaczenia wiersz w edycji. 10
Zadania 1. Uruchomić aplikację dla powyższego ćwiczenia. Do testowania proszę wykorzystywać przeglądarkę Internet Explorer. 2. Poprawić błąd o następujących objawach: Po dokonaniu zmian w wybranym wierszu i kliknięciu na przycisk Zapisz rekord, wybrany wiersz jest aktualizowany, ale zawartości pól tekstowych nie są czyszczone. Pola te powinny być puste po aktualizacji wiersza. 3. Dodać funkcjonalność: Wciśnięcie przycisku Dodaj nowy powinno dodawać nowy wiersz z wartościami wpisanymi wcześniej w pola tekstowe, pod tabelą. Obecnie dodawany wiersz jest zawsze pusty. Kod SQL tworzący tabelę kontakty: CREATE TABLE kontakty ( id INT PRIMARY KEY NOT NULL IDENTITY (1, 1), imie varchar(100), nazwisko varchar(100), adres varchar(100), email varchar(100) ); INSERT INTO kontakty (imie, nazwisko, adres, email) VALUES ('Kubuś', 'Puchatek', 'Chatka Puchatka', 'kubus@las.pl'); INSERT INTO kontakty (imie, nazwisko, adres, email) VALUES ('Krzyś', 'Krzyś', 'Stumilowy Las', 'krzys@las.pl'); INSERT INTO kontakty (imie, nazwisko, adres, email) VALUES ('Prosiaczek', '', 'Norka Prosiaczka', 'prosie@las.pl'); 11