- Narzędzie Windows Forms - - Przykładowe aplikacje - Architektura ADO.NET - - Dostawcy danych - - Modele dostępu do danych model połączeniowy - - Model bezpołączeniowy 1
Narzędzia WindowsForm Windows Form jest narzędziem do tworzenia aplikacji dla systemu Windows. Platforma ta umożliwia tzw. błyskawiczne tworzenie aplikacji (ang. Rapid Application Development RAD). Środowisko Visual Studio.NET ułatwia tworzenie aplikacji windows'owych z graficznym interfejsem użytkownika (GUI). Aby rozpocząć pracę nad nową aplikacją (rozpocząć tworzenie nowego "projektu" lub "rozwiązania" ang. Solution), po uruchomieniu środowiska Visual Studio należy wybrać opcję File New Project, rys. 4.1. 2
3
Po wybraniu jako wzorca aplikacji windowsowej (Windows Form Application) i określeniu nazwy katalogu zawierającego komplet plików aplikacji system wygeneruje automatycznie formularz startowy, który jest klasą o domyślnej nazwie Form1 - potomną klasy bazowej Form i otworzy środowisko projektowania (rys.4.2). Lewą stronę arkusza zajmuje "skrzynka narzędziowa" (Toolbox) z pogrupowanymi wg kategorii ikonkami kontrolek do umieszczania na formularzu. Jego obraz w fazie projektowania znajduje się na środkowym polu arkusza. Z prawej strony znajduje się Solution Explorer. Zawiera on drzewo wszystkich plików projektu. Obok system umieścił okno Properties zawierające właściwości i zdarzenia wybranej kontrolki. Jeżeli ustawienia domyślne środowiska są takie, że nie pojawiają się automatycznie te okna, to zawsze możemy je zmienić wybierając zaznaczenie odpowiedniej pozycji w menu rozwijanym opcji View. (rys.4.3) 4
5
6
Wybór pozycji Code w tym menu rozwijanym otwiera arkusz edytora tekstowego zawierającego w tym stanie kod szkieletu klasy częściowej (partial class) definicji klasy tworzonego formularza i równocześnie klasy głównej tworzonej aplikacji (rys.4.4). Każda kontrolka (również klasa formularza pochodna od klasy bazowej Form ma swój zestaw właściwości i "wyłapywanych" komunikatów o zdarzeniach. W fazie projektowania wartości wybranych właściwości można zmieniać w stosunku do domyślnych za pomocą okna Properties. Dostęp do listy zdarzeń zdefiniowanych w klasie danej kontrolki uzyskujemy po kliknięciu ikony "wysokiego napięcia" (rys. 5.5 i 4.6). W każdej klasie kontrolki jedno ze zdarzeń pełni rolę "domyślnego". Dwukrotne kliknięcie tej kontrolki spowoduje, że system utworzy "szkielet" metody obsługi tego zdarzenia. Przykład przedstawiony na rysunku 4.7 przedstawia umieszczoną na formularzu kontrolkę przycisku jest to obiekt klasy System.Windows.Forms.Button. Po umieszczeniu go na formularzu metodą "przeciągnij i upuść" system nada mu domyślną nazwę button1. 7
8
9
Rys.4.6 10
Rys.4.7 11
Zmieniamy teraz kilka właściwości formularza i przycisku (rys.4.8 i 4.9). Po dwukrotnym kliknięciu w obszarze kontrolki btnkoniec system wygeneruje szablon metody obsługującej domyślne zdarzenie w klasie Button, wstawi go do definicji klasy i przełączy widok na kod aplikacji (rys.4.10). Bardzo dużym ułatwieniem jest istnienie "podpowiadacza" mechanizmu Intelisense. 12
Rys.4.8 13
Rys.4.9 14
Rys.4.10 15
Rys.4.11 16
Rys.4.12 17
Rys.4.13 18
Przykładowe aplikacje Przykładowa aplikacja, która wyznacza wszystkie liczby pierwsze w zadanym przedziale może wyglądać tak jak na rys. 4.14 Rys.4.14 19
Kontrolka ListBox o nazwie liczbypierwsze jest kolekcją, do której metoda poszukująca liczb pierwszych dopisuje kolejną znalezioną liczbę. Kontrolka label4 wyświetla ile znaleziono liczb pierwszych w zadanym przedziale. Rys. 7.16 przedstawia kod aplikacji umieszczony przez system w pliku Form1.cs. 20
Rys.4.15 21
using System; using System.Windows.Forms; namespace LiczbyPierwsze { public partial class Form1 : Form { // Metoda prywatna, która sprawdza czy argument jest liczbą // pierwszą private bool pierwsza(int n) { if (n < 3) return true; for (int k = 2; k <= n/2; k++) { if (n % k == 0) return false; } return true; } // Konstruktor w klasie Form1 public Form1() { InitializeComponent(); } Rys.4.16 22
// Obsługa zdarzenia - wyświetlenie formularza private void Form1_Shown(object sender, EventArgs e) { updwnodliczby.value = 1; updwndoliczby.value = 2; liczbypierwsze.items.clear(); label4.text = ""; } // Obsługa zdarzenia - kliknięcie klawisza butkoniec // lub naciśnięcie kombinacji klawiszy Alt+K private void butkoniec_click(object sender, EventArgs e) { liczbypierwsze.visible = false; label4.visible = false; Application.Exit(); } // Obsługa zdarzenia - zmiana wartości w polu // kontrolki updwnodliczby private void updwnodliczby_valuechanged(object sender, EventArgs e) { updwndoliczby.value = updwnodliczby.value + 1; } 23
// Obsługa kliknięcia klawisza butszukaj - wyszukiwanie // liczb pierwszych w zadanym przedziale private void butszukaj_click(object sender, EventArgs e) { label3.visible = true; liczbypierwsze.visible = true; label4.visible = true; liczbypierwsze.items.clear(); int dół = (int)updwnodliczby.value; int góra = (int)updwndoliczby.value; int ile = 0; for (int i = dół; i <= góra; i++) { if (pierwsza(i)) { // Dopisuje kolejną liczbę pierwszą do kolekcji liczbypierwsze.items.add(i.tostring()); ++ile; } } label4.text = "Znaleziono " + ile.tostring() + " liczb pierwszych"; } } } 24
25
using System; using System.Drawing; using System.Windows.Forms; namespace Graphics_3 { public partial class Form1 : Form { // Konstruktor w klasie Form1 public Form1() { InitializeComponent(); } // Metoda f oblicza wartość funkcji y private double f(double a, double x) { return a * Math.Cos(x) * Math.Cos(x); } 26
// Metoda wylicza współrzędne ekranowe (w pikselach) punktów wykresu private void wyliczpunkty(point[] w, int n, double xp, double xk, int q) { double dx = (xk - xp) / n; double najw = f(1.0, xp); double najm = f(1.0, xp); double a; double dy; for (int j = 0; j < n; j++) { if ((a = f(1.0, xp + j * dx)) > najw) najw = a; if (a < najm) najm = a; } dy = (najw - najm) / q; dy = (dy < 0)? -dy : dy; for (int i = 0; i < n; i++) { w[i].x = i; w[i].y = q - (int)(f(1.0, (xp + i * dx))/dy); } } 27
// Obsługa kliknięcia klawisza Rysuj - rysuje wykres na panelu ("płótnie") private void butrysuj_click(object sender, EventArgs e) { Graphics gr = this.grpanel.creategraphics(); int wysokość = 600; int szerokość = 1100; Point[] wierzchołki = new Point[szerokość]; grpanel.height = wysokość; grpanel.width = szerokość; //MessageBox.Show(wysokość.ToString()); //MessageBox.Show(szerokość.ToString()); Pen penosie = new Pen(Brushes.Black, (float)1.5); Pen brązowy = new Pen(Brushes.Brown); Pen żółty = new Pen(Brushes.YellowGreen); gr.smoothingmode = System.Drawing.Drawing2D.SmoothingMode.AntiAlias; gr.drawline(penosie, new Point(0, wysokość/2), new Point(szerokość, wysokość/2)); gr.drawline(penosie, new Point(szerokość / 2, 0), new Point(szerokość / 2, wysokość)); this.wyliczpunkty(wierzchołki, szerokość, -3, 3, wysokość-1); for (int k = 0; k < szerokość - 1; k++) gr.drawline(brązowy, wierzchołki[k], wierzchołki[k + 1]); } // Obsługa kliknięcia klawisza Koniec private void butkoniec_click(object sender, EventArgs e) { Application.Exit(); } } } 28
Architektura ADO.NET (Active Data Objects) Zestaw abstrakcyjnych klas, które udostępniają dane z poziomu środowiska.net umożliwiają: - dostęp do różnych źródeł danych (relacyjne bazy danych, pliki XML, arkusze kalkulacyjne, pliki tekstowe) - dostęp za pośrednictwem interfejsów API tzw. zarządzanych dostawców danych - wykorzystują dwie metody dostępu do danych: połączeniowy i dostęp bezpołączeniowy Zestaw klas pozwala uzyskać dostęp do dowolnego źródła danych. Źródło danych traktuje się jako abstrakcyjną encję 29
30
Dostawcy danych (Microsoft u): Odbc OleDb OracleClient SqlClient Aby użyć określonego dostawcy należy w kodzie programu utworzyć obiekty specyficzne dla danego dostawcy np. obiekty połączenia to egzemplarze klas OdbcConnection OleDbConnection OracleConnection SqlConnection (Object Linking and Embedding) Przykład lista zainstalowanych dostawców danych katalog Dostawcy 31
<section name="system.data" type="system.data.common.dbproviderfactoriesconfigurationhandler, System.Data, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089" /> <section name="system.data.dataset" type="system.configuration.namevaluefilesectionhandler, System, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089" restartonexternalchanges="false" /> <section name="system.data.odbc" type="system.data.common.dbproviderconfigurationhandler, System.Data, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089" /> <section name="system.data.oledb" type="system.data.common.dbproviderconfigurationhandler, System.Data, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089" /> <section name="system.data.oracleclient" type="system.data.common.dbproviderconfigurationhandler, System.Data, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089" /> <section name="system.data.sqlclient" type="system.data.common.dbproviderconfigurationhandler, System.Data, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089" /> Fragment pliku machine.config 32
33
34
Jeżeli aplikacja musi obsługiwać wiele źródeł danych to należy użyć tzw. "fabryki klas" do dynamicznego tworzenia obiektów wybranego dostawcy. Architektura ADO.NET udostępnia klasę DbProviderFactories do zwracania obiektów wymaganych przez specyficznego dostawcę danych. Do metody GetFactory() tej klasy należy przekazać ciąg znaków z nazwą dostawcy. Metoda ta zwraca obiekt fabryczny, który służy do tworzenia specyficznych obiektów wymaganych przez określonego dostawcę. Przykład dynamicznego ustalania klasy dostawcy i pobierania z fabryki dostawców jego specyficznych obiektów: 35
36
Dostęp do danych - model połączeniowy Pomiędzy obiektem DataReader a źródłem danych jest utrzymywane aktywne połączenie. Każde wywołanie metody Read()zwraca wiersz danych ze źródła. Cechą charakterystyczną tego modelu jest fakt, że wczytuje on dane ze zbioru utworzonego za pomocą polecenia SQL owego po jednym rekordzie, sekwencyjnie i tylko w trybie do odczytu. Niemożliwa jest bezpośrednia aktualizacja danych lub ich dodawanie. Relacje pomiędzy klasami DataReader, Command, Connection wyglądają tak jak na rysunku: 37
38
39
40
Model bezpołączeniowy Polecenie SQL ładuje dane z zewnętrznego źródła do pamięci podręcznej na maszynie klienta. Program manipuluje zbiorem wynikowym na maszynie lokalnej, a aktualizacje przekazuje z danych w buforze do źródła danych. W modelu tym połączenie jest otwierane tylko na czas wczytania danych ze źródła i dokonania aktualizacji. W ten sposób zostają zwolnione zasoby serwera. 41
Kluczowe składniki modelu bezpołączeniowego to DataAdapter i DataSet. Obiekt DataAdapter pośredniczy w wymianie danych pomiędzy źródłem a buforami klienta. Obiekt DataSet funkcjonuje w pamięci klienta jako relacyjna baza danych i zawiera co najmniej jeden obiekt DataTable. Obiekt DataTable zawiera wiersze i kolumny danych, które pochodzą z tabel w źródłowej bazie danych. Klasa DataAdapter udostępnia takie ważne metody jak Fill() i Update(). Metoda Fill()przekazuje zapytanie do bazy danych i zapisuje zwrócony zbiór w wybranym obiekcie DataTable. Metoda Update()służy do wykonywania operacji wstawiania, aktualizacji i usuwania danych wg zmian dokonanych w obiekcie DataSet 42