Inżynieria Programowania Laboratorium 6 Pierwsza finalna wersja. Paweł Paduch paduch@tu.kielce.pl



Podobne dokumenty
xmlns:prism= c. <ContentControl prism:regionmanager.regionname="mainregion" />

Programowanie obiektowe i zdarzeniowe wykład 1 Wprowadzenie do programowania zdarzeniowego

Budowa aplikacji ASP.NET współpracującej z bazą dany do obsługi przesyłania wiadomości

1 LINQ. Zaawansowane programowanie internetowe Instrukcja nr 1

Utworzenie aplikacji mobilnej Po uruchomieniu Visual Studio pokazuje się ekran powitalny. Po lewej stronie odnośniki do otworzenia lub stworzenia

Politechnika Poznańska Wydział Budowy Maszyn i Zarządzania

Leszek Stasiak Zastosowanie technologii LINQ w

Inżynieria Programowania Laboratorium 3 Projektowanie i implementacja bazy danych. Paweł Paduch paduch@tu.kielce.pl

Jak zainstalować szablon allegro?

Podręcznik użytkownika Obieg dokumentów

Podstawy technologii WWW

Formularze w programie Word

TWORZENIE FORMULARZY WORD 2007

LABORATORIUM 8,9: BAZA DANYCH MS-ACCESS

MATERIAŁY - udostępnianie materiałów dydaktycznych w sieci SGH

Informatyka II. Laboratorium Aplikacja okienkowa

Programowanie obiektowe i zdarzeniowe wykład 4 Kompozycja, kolekcje, wiązanie danych

Instrukcja obsługi Modułu Payu dla Moodle 2.x

Podstawy technologii WWW

Inżynieria Programowania Laboratorium 5 Część Wizualna. Paweł Paduch paduch@tu.kielce.pl

Dokumentacja programu. Zoz. Uzupełnianie kodów terytorialnych w danych osobowych związanych z deklaracjami POZ. Wersja

Palety by CTI. Instrukcja

I. Program II. Opis głównych funkcji programu... 19

1. Proszę wejść na stronę: poczta.home.pl i zalogować się do nowej skrzynki za pomocą otrzymanych danych.

Ćwiczenie 1. Kolejki IBM Message Queue (MQ)

KASK by CTI. Instrukcja

5.4. Tworzymy formularze

WYKONANIE APLIKACJI OKIENKOWEJ OBLICZAJĄCEJ SUMĘ DWÓCH LICZB W ŚRODOWISKU PROGRAMISTYCZNYM. NetBeans. Wykonał: Jacek Ventzke informatyka sem.

Baza danych. Program: Access 2007

BAZY DANYCH Panel sterujący

Projekt Hurtownia, realizacja rejestracji dostaw produktów

Politechnika Gdańska Wydział Elektrotechniki i Automatyki Katedra Inżynierii Systemów Sterowania KOMPUTEROWE SYSTEMY STEROWANIA (KSS)

Microsoft.NET: LINQ to SQL, ASP.NET AJAX

Instrukcja obsługi programu DHL EasySHip v. 5.3.x

UONET+ - moduł Sekretariat. Jak wykorzystać wydruki list w formacie XLS do analizy danych uczniów?

Backend Administratora

System magazynowy małego sklepu.

Ćwiczenie 1 Galeria zdjęć

Wypożyczalnia by CTI. Instrukcja

5. Bazy danych Base Okno bazy danych

Projekt Hurtownia, realizacja rejestracji dostaw produktów

Serwis jest dostępny w internecie pod adresem Rysunek 1: Strona startowa solidnego serwisu

Tworzenie pliku źródłowego w aplikacji POLTAX2B.

1. Dockbar, CMS + wyszukiwarka aplikacji Dodawanie portletów Widok zawartości stron... 3

Budowa aplikacji ASP.NET współpracującej z bazą dany do przeprowadzania ankiet internetowych

Baza danych sql. 1. Wprowadzenie

1. Wprowadzenie do WPF i XAML. Tworzenie interfejsu użytkownika.

Budowa aplikacji ASP.NET współpracującej z bazą dany do obsługi przesyłania wiadomości

Instrukcja laboratoryjna

Uruchamianie bazy PostgreSQL

Obszar Logistyka/Zamówienia Publiczne

Projektowanie i programowanie aplikacji biznesowych. Wykład 2

Konfiguracja szablonu i wystawienie pierwszej aukcji allegro

Program dla praktyki lekarskiej

Formularz MS Word. 1. Projektowanie formularza. 2. Formularze do wypełniania w programie Word

ASP.NET MVC. Podstawy. Zaawansowane programowanie internetowe Instrukcja nr 3

Karty pracy. Ustawienia. W tym rozdziale została opisana konfiguracja modułu CRM Karty pracy oraz widoki i funkcje w nim dostępne.

Wstawianie nowej strony

Budowa aplikacji ASP.NET współpracującej z bazą dany do przeprowadzania ankiet internetowych

Ćwiczenie 4 Aktualizacja tabel. 1. Skopiuj aplikację przygotowaną na poprzednich zajęciach w katalogu SKOKI1 do nowego katalogu SKOKI2.

Budowa aplikacji ASP.NET współpracującej z bazą dany do obsługi przesyłania wiadomości

Instalacja systemu zarządzania treścią (CMS): Joomla

Adobe InDesign lab.1 Jacek Wiślicki, Paweł Kośla. Spis treści: 1 Podstawy pracy z aplikacją Układ strony... 2.

Instrukcja obsługi programu Profile GT

Jak zainstalować i skonfigurować komunikator MIRANDA, aby wyglądał i funkcjonował jak Gadu Gadu Tutorial by t800.

Telesprzedaż by CTI Instrukcja

Bazy danych Karta pracy 1

Podział na strony, sekcje i kolumny

Instrukcja laboratoryjna cz.3

Wprowadzenie do projektu QualitySpy

Zmiany funkcjonalne i lista obsłużonych zgłoszeń Comarch DMS

Windows Server Active Directory

Pierwsza niedogodność dotyczy strony zarządzaj działami.

,Aplikacja Okazje SMS

Kalipso wywiady środowiskowe

INSTRUKCJA OBSŁUGI PROGRAMU PRZEDSZKOLE (CZ.1)

Instrukcja obsługi Modułu Płatności dla Moodle 1.7, 1.8, 1.9

System rezerwacji online

Zanim zaczniesz. Warto ustawić kartę sieciową naszego serwera.

Instrukcja instalacji i konfiguracji bazy danych SQL SERVER 2008 EXPRESS R2. Instrukcja tworzenia bazy danych dla programu AUTOSAT 3. wersja 0.0.

Notowania Mobilne wersja Java

FAQ Systemu EKOS. 1. Jakie są wymagania techniczne dla stanowiska wprowadzania ocen?

Cel: Przypisujemy przyciskom określone funkcje panel górny (Panel1)

Tworzenie szablonów użytkownika

Platforma e-learningowa

DOKUMENTY I GRAFIKI. Zarządzanie zawartością Tworzenie folderu Dodawanie dokumentu / grafiki Wersje plików... 7

Kurs walut. Specyfikacja projektu. Marek Zając

DODAJEMY TREŚĆ DO STRONY

Zmiany w programie VinCent Office v.1.09

Jeżeli pole Krawędź będzie zaznaczone uzyskamy obramowanie w całej wstawianej tabeli

Instrukcja użytkownika

Instrukcja obsługi systemu zarządzania treścią dwajeden.pl

AKTYWNY SAMORZĄD. Instrukcja użytkownika.

Arkusz kalkulacyjny MS Excel 2010 PL.

Zaznaczanie komórek. Zaznaczenie pojedynczej komórki polega na kliknięciu na niej LPM

Dodatek. Instrukcja instalacji i konfiguracji. dla Subiekt nexo. Wersja:

Skrócona instrukcja obsługi programu EndymionKOL

Wprowadzenie do Doctrine ORM

Instalacja programu:

Miejskie Wodociągi i Oczyszczalnia sp. z o.o. w Grudziądzu. ibok. Internetowe Biuro Obsługi Klienta. Instrukcja obsługi

Transkrypt:

Inżynieria Programowania Laboratorium 6 Pierwsza finalna wersja Paweł Paduch paduch@tu.kielce.pl 25-05-2013

Rozdział 1 Wstęp Dzisiejsze zajęcia będą kontynuacją implementowania interfejsu. Skupimy się także na testowaniu aplikacji pod kontem ergonomii i niezawodności. W ocenie interfejsu może pomóc lista kontrolna Ravdena Johnsona (do znalezienia w Internecie). Jest to opracowany w 1989 r. zbiór pytań i punktów kontrolnych dotyczących projektu informatycznego. Między innymi sprawdzana jest spójność interfejsu, komunikaty do użytkownika, klarowność łatwość obsługi itd. Listę tę, należy sobie przeczytać i stosować także w swoich przyszłych projektach. 1

Rozdział 2 Okno edycji użytkowników Na ostatnich zajęciach poradziliśmy sobie ze zunifikowaniem nazewnictwa w bazie danych a co za tym idzie także w naszej bibliotece RezerwacjeLib. Poznaliśmy podstawowe elementy programowania aplikacji WPF takie jak kontrolki, zdarzenia czy tworzenie nowych okien. Na koniec został pokazany przykład podpięcia danych słownikowych jako kontrolki typu ComboBox w tabelce Zasoby. Pierwszym zadaniem będzie stworzenie okna zarządzającego osobami. Będzie to tabelka ze wszystkimi potrzebnymi danymi takimi jak imię, nazwisko, uprawnienie, tytuł, mail, hasło czy daty dodania i modyfikacji. Do tej pory w większości okien nie przywiązywaliśmy uwagi do zachowania się kontrolek podczas skalowania okna. Tym razem postaramy się prawidłowo i w miarę estetycznie rozmieścić elementy i skonfigurować ich zachowanie podczas zmiany rozmiaru okna. 2.1 Konfiguracja wyglądu Zadanie zaczynamy oczywiście od dodania nowego okna z menu projektu wybieramy Add Window. Nazywamy go WinOsoby. Po utworzeniu zmieniamy jego tytuł na Osoby. Dodajemy tabelkę i przyciski. Oprogramowujemy pozycję Osoby w menu głównego okna tak by otworzyło nam się okienko z wypełnioną tabelką. Jak zwykle pamiętamy o ustawieniu odpowiedniej przestrzeni nazw w pliku *.cs. Można uruchomić i sprawdzić czy dane dociągnęły się jak należy. Wygląd okna powinien przypominać ten na rysunku 2.1. Rysunek 2.1: Okienko edycji osób. Proszę zwrócić uwagę na kolejność kolumn, poprawione nagłówki i format daty. Dodatkowo wszystkie kolumny gdzie występował klucz obcy zastąpione 2

zostały odpowiednimi polami combobox zawierającymi słowniki. Jak stworzyć słowniki można sobie przypomnieć z ostatniej instrukcji laboratoryjnej. Po utworzeniu domyślnej tabeli, daty dodania i modyfikacji osadzone zostaną w bardzo funkcjonalnej kontrolce pozwalającej wprowadzać datę z kalendarza. Jednak nasze daty, zarówno dodania jak i edycji aktualizują się same. Powinniśmy więc ustawić je tylko do odczytu. Zamieniamy je na pole typu DataGridTextColumn tak jak na listingu 2.1 Listing 2.1: Kolumny z datą tylko do odczytu <DataGridTextColumn x:name="datadodaniacolumn" Header="Data dodania" Binding="Binding DataDodania, StringFormat=\0:dd.MM.yyyy HH:mm:ss\" Width="Auto" MinWidth="110" IsReadOnly="True"/> <DataGridTextColumn x:name="datamodyfikacjicolumn" Header="Data modyfikacji" Binding="Binding DataModyfikacji, StringFormat=\0:dd.MM.yyyy HH:mm:ss\" Width="Auto" MinWidth="110" IsReadOnly="True"/> We właściwościach widzimy, że są to pola tylko do odczytu. Widać też, że zastosowano ciąg formatujący datę (niestety ten domyślny nam nie odpowiada. Z ciekawości można usunąć parametr StringFormat w jednej z kolumn i zobaczyć efekt działania. 2.2 Rozmieszczenie elementów Okno w WPF może posiadać tylko jeden element. Nie można wstawiać bezpośrednio pod tagiem Window elementy typu guziki, etykietki i inne. Tym jedynym elementem jest panel. Jest to element układający inne elementy wewnątrz siebie. W WPF mamy do dyspozycji kilka takich paneli. Canvas - klasyczne rozmieszczenie elementów, tam gdzie je ustawimy tam pozostaną DockPanel - przyczepa element do określonej krawędzi (góra, dół, prawa, lewa) StackPanel - Elementy układane są jeden na drugim, lub jeden za drugim WrapPanel - Podobnie jak StackPanel ale powoduje zawijanie wiersza elementów. W podobny sposób zorganizowane jest menu. Grid - siatka składająca się często z wielu komórek, w każdej komórce można rozmieszczać elementy. Jest to domyślny panel w kodzie wygenerowanym automatycznie. Nasz Grid będzie zawierał dwa wiersze oraz jedną kolumnę. W pierwszym wierszu umieścimy tabelkę w drugim guziki. W listingu 2.2 widzimy prosty grid o podanej wysokości wierszy. 3

<Grid.RowDefinitions> <RowDefinition Height="260*" /> <RowDefinition Height="40" /> </Grid.RowDefinitions> Listing 2.2: Definicja panelu siatki. Gwiazdka przy wartości 260* świadczy o tym, że wiersz ten może zmieniać rozmiar. Gdy wstawimy weń tabelę też będzie trzeba określić jej sposób wyrównywania w pionie i poziomie ( VerticalAlignment= Stretch HorizontalAlignment= Stretch ). Przy drugim wierszu gdzie będą guziki nie chcemy by były one zakrywane albo rozciągane przy zmianie rozmiaru okna dlatego stosujemy stałą wysokość wiersza. Ponadto chcemy, by zarówno tabelka jak i guziki nie przylegały do krawędzi okna. Do określenia marginesu jaki należy zachować dookoła danego elementu służy atrybut Margin Określenie do jakiej komórki należy dana kontrolka dokonamy za pomocą tzw. właściwości dołączanych jak widzimy to w listingu 2.3. Listing 2.3: Przykład przycisków w gridzie. <Button Content="Zapisz" Grid.Row="1" Height="25" HorizontalAlignment="Left" Margin="10,0,10,10" Name="buttonZapisz" VerticalAlignment="Top" Width="75" Click="buttonZapisz_Click" /> <Button Content="Wyjdź" Grid.Row="1" Height="25" HorizontalAlignment="Right" Margin="10,0,10,10" Name="buttonWyjdz" VerticalAlignment="Top" Width="75" Click="buttonWyjdz_Click" /> Widzimy tu też przykład przyklejenia jednego przycisku do lewej krawędzi drugiego do prawej. Przy zmianie szerokości okna przyciski te będą albo się od siebie oddalać albo przybliżać. Wyrównywanie kolumn. Praktycznie wszystkie kolumny powinny mieć szerokość ustawioną na Auto. Wtedy szerokość będzie brana taka by zmieścił się najszerszy element. Kolumny z datą można ustawić na stałe 110, natomiast szerokość kolumny Opis ustawiamy na *. Wtedy kolumna ta będzie rozszerzalna wraz z rozszerzaniem się tabelki. Po ustawieniu odpowiednich właściwości przetestować działanie w programie. Jedyną rzeczą, której tu nie zrobimy a mogłaby się przydać to maskowanie haseł. Ze względów bezpieczeństwa, nawet admin nie powinien znać haseł użytkowników. Powinniśmy zastosować typ pola PasswordBox. Jednak związanie z danymi z bazy jest trochę bardziej skomplikowane i pominiemy ten problem. Zwłaszcza, że hasła i tak są w bazie trzymane jawnie. Docelowo nasz system będzie oparty o autentykację Active Directory i wtedy problem zmiany haseł będzie leżał po stronie domeny. 4

Rozdział 3 Przegląd rezerwacji Teraz zajmiemy się najtrudniejszą (jak do tej pory) częścią naszego programu, przeglądem i łatwym wyszukiwaniem rezerwacji. Do tego celu posłużą nam 3 tabelki typu datagrid umieszczone na głównym oknie. Wygląd będzie mniej więcej taki jak na rysunku 3.1. W tabelce osób zaznaczamy użytkowników, których rezerwacje chcemy przeglądać, w tabelce zasoby zaznaczamy interesujące nas sale, sprzęty czy grupy. Ctrl+A oczywiście zaznacza wszystko w danej tabelce. Przy zaznaczonych wszystkich elementach w obu tabelkach mamy wgląd we wszystkie rezerwacje. Rysunek 3.1: Projekt głównego okna. 5

3.1 Rozmieszczenie i zawartość tabelek Aby zapewnić odpowiednie wyrównywanie i skalowanie, tabelki umieścimy w siatce (grid). Siatka będzie składać się z dwóch kolumn oraz 4 wierszy. Definicje kolumn i wierszy umieszczamy bezpośrednio pod tagiem Grid, listing 3.1 W zerowym wierszu umieścimy menu. W kolejnych dwóch będą umieszczone tabelki rezerwacji, osób i zasobów. W ostatniej umieścimy zagnieżdżoną siatkę, w której będą elementy potrzebne do dodawania rezerwacji. Listing 3.1: Definicja grida dla głównego okna. <Grid.ColumnDefinitions> <ColumnDefinition Width="320*" /> <ColumnDefinition Width="180*" /> </Grid.ColumnDefinitions> <Grid.RowDefinitions> <RowDefinition Height="22" /> <RowDefinition Height="125*" /> <RowDefinition Height="125*" /> <RowDefinition Height="50" /> </Grid.RowDefinitions> Gwiazdki przy rozmiarach oznaczają, że dany rozmiar może się zmieniać, jednak będzie się to odbywać proporcjonalnie. W pierwszym wierszu o stałej wysokości 22 umieścimy pasek z menu. Robimy to za pomocą właściwości dołączanej (informującej element rodzicielski o parametrach dla elementu potomnego). Grid.Row - oznacza w którym wierszu umieszczamy menu, Grid.ColumnSpan określa na ile kolumn rozciągnąć pasek. Określamy też rozciągliwość paska na całą szerokość obu kolumn grida oraz wyrównywanie do samej góry. Tak jak na listingu 3.2 Listing 3.2: Umieszczenie paska menu w siatce. <Menu Height="22" Grid.Row="0" Grid.ColumnSpan="2" HorizontalAlignment="Stretch" VerticalAlignment="Top" Name="menuGlowne"> Kolejnym elementem będzie tabelka rezerwacji. Umieścimy ją w gridzie w kolumnie 1 oraz wierszach 1-2. Skonfigurujemy od razu jakie informacje chcemy w niej zawrzeć. Będą to kolumny: Id - identyfikator, docelowo zniknie ale dla testów i celów edukacyjnych narazie zostawimy. Imię osoby, której dotyczy rezerwacja Nazwisko osoby której dotyczy rezerwacja Nazwa zasobu Czas początku rezerwacji Czas końca rezerwacji Stan rezerwacji oraz Typ rezerwacji 6

W tabelce ustawimy odstęp od każdej sąsiadującej krawędzi na 5. Tabelka powinna też rozciągać się wraz ze zmianą wysokości jak i szerokości okna. Definicja rozmieszczenia tabelki oraz jej kolumn zawarto w listingu 3.3. Listing 3.3: Rozmieszczenie tabelki rezerwacji oraz jej kolumny. <DataGrid AutoGenerateColumns="False" Margin="5,5,5,5" Grid.Row="1" Grid.RowSpan="2" ItemsSource="Binding" Name="rezerwacjeDataGrid" HorizontalAlignment="Stretch" VerticalAlignment="Stretch"> <DataGrid.Columns> <DataGridTextColumn Header="Id" Binding="Binding IdRezerwacji" Width="30" MinWidth="30" /> <DataGridTextColumn Header="Imię" Binding="Binding Imie" Width="*" MinWidth="50"/> <DataGridTextColumn Header="Nazwisko" Binding="Binding Nazwisko" Width="*" MinWidth="50"/> <DataGridTextColumn Header="Zasób" Binding="Binding Nazwa" Width="*" MinWidth="60"/> <DataGridTextColumn Header="Początek" Width="Auto" MinWidth="115" Binding="Binding Od, StringFormat=\0:dd.MM.yyyy HH:mm:ss\"/> <DataGridTextColumn Header="Koniec" Width="Auto" MinWidth="115" Binding="Binding Do, StringFormat=\0:dd.MM.yyyy HH:mm:ss\"/> <DataGridTextColumn Header="Stan" Binding="Binding Stan" Width="Auto" MinWidth="55"/> <DataGridTextColumn Header="Typ" Binding="Binding Typ" Width="Auto" MinWidth="55"/> </DataGrid.Columns> </DataGrid> Wierszu 1 i kolumnie 1 siatki umieścimy prostą tabelkę osób z danymi tytuł, imię i nazwisko. Tabelkę przeciągamy z bocznego paska Data Sources i konfigurujemy tak jak pokazano na listingu 3.4. Listing 3.4: Rozmieszczenie tabelki osób oraz jej kolumny. <DataGrid AutoGenerateColumns="False" Grid.Column="1" Grid.Row="1" HorizontalAlignment="Stretch" VerticalAlignment="Stretch" ItemsSource="Binding Source=StaticResource osobyviewsource" Margin="5,5,5,5" Name="osobyDataGrid" SelectionChanged="osobyDataGrid_SelectionChanged"> <DataGrid.Columns> <DataGridTextColumn x:name="tytulcolumn" Binding="Binding Path=Tytuly.Tytul" Header="Tytuł" Width="Auto" /> <DataGridTextColumn x:name="imiecolumn" Binding="Binding Path=Imie" Header="Imię" Width="Auto" /> <DataGridTextColumn x:name="nazwiskocolumn" Binding="Binding Path=Nazwisko" Header="Nazwisko" Width="*" /> </DataGrid.Columns> </DataGrid> 3.2 Kod obsługujący Gdy już mamy zdefiniowane tabelki (na razie dwie, trzecia będzie do zrobienia samodzielnego) należy wypełnić je danymi. Najłatwiej będzie z tabelką osób. Należy to zrobić analogicznie jak w przypadku innych prostych tabelek. W funkcji wywoływanej podczas ładowania głównego okna (Window Loaded) należy dodać linię: osobyviewsource.source = zalogowany.znajdzosobyponazwisku( * ).ToList(); Tabela rezerwacji ma nieco więcej danych. Dodatkowo dane te pochodzą z tabel w relacji wiele-do-wielu. Gdy tabelki połączeniowe np. RezerwacjeZasoby 7

lub RezerwacjeOsoby posiadały tylko klucze obce generator modelu Entity Framework stworzyłby relację *-*. O ile z rezerwacjami można by się pokusić o takie rozwiązanie o tyle połączenie z osobami jest nieco bardziej skomplikowane ze względu na pole Zgoda. Tak czy inaczej stworzymy sobie nowy typ danych klasę będącą reprezentacją pojedynczego wiersza rezerwacji. Wiersza, w którym będzie zawarty jeden zasób i jedna osoba. Nową klasę jak na listingu 3.5 umieszczamy w pliku Rezerwacje.cs w bibliotece RezerwacjeLib. Listing 3.5: Nowa klasa RezerwacjoZasoboOsoby. public class RezerwacjoZasoboOsoby public Rezerwacje rez get;set; public Zasoby zas get; set; public Osoby os get; set; Kolejnym elementem jest implementacja funkcji szukającej rezerwacje związane z danymi osobami i zasobami. Podając listy odpowiednich identyfikatorów powinniśmy dostać zbiór rezerwacji. Zrealizujemy tą funkcjonalność w listingu 3.6 w pliku Rezerwacje.cs w bibliotece RezerwacjeLib. Listing 3.6: Funkcja szukająca rezerwacje. public IQueryable<RezerwacjoZasoboOsoby> znajdzrezerwacje(list<long> idkiludzikow, List<long> idkizasobow) IQueryable<RezerwacjoZasoboOsoby> rezos = (from rz in context.rezerwacje from zas in context.rezerwacjezasoby from os in context.rezerwacjeosoby from osobydowyszukania in idkiludzikow from zasobydowyszukania in idkizasobow where rz.idrezerwacji == zas.idrezerwacji //zlaczenie tabeli rezerwacji z tabela zawierajaca identyfikatory zasobow && os.idrezerwacji == rz.idrezerwacji //i czy dane osoby tez nie sa zajete w tym czasie && os.idosoby == osobydowyszukania //uwzględniamy tylko podane id && zas.idzasobu == zasobydowyszukania //uwzględniamy tylko podane id select new RezerwacjoZasoboOsoby rez=rz, os=os.osoby, zas=zas. Zasoby ).AsQueryable(); return rezos; Teraz należy stworzyć funkcję wypełniającą tabelkę rezerwacji (listing 3.7. Oczywiście funkcja ta będzie w pliku MainWindow.xaml.cs. Listing 3.7: Funkcja wypelnijtabelkerezerwacji. private void wypelnijtabelkerezerwacji() try Zasoby zasoby = new Zasoby(); List<long> idkiosob = new List<long>(); idkiosob = (from os in osobydatagrid.selecteditems.oftype<osoby>() select os. IdOsoby).ToList(); //Chwilowo weźmiemy pod uwagę wszystkie zasoby List<long> idkizasobow = (from zas in zasoby.getall() select zas.idzasobu).tolist (); 8

//znajdź rezerwacje List<RezerwacjoZasoboOsoby> rez = rezerwacje.znajdzrezerwacje(idkiosob, idkizasobow).tolist(); ListCollectionView reztest = new ListCollectionView((from rz in rez select new rz.rez.idrezerwacji, opiszas = rz.zas.opis, rz.zas.nazwa, rz.os.imie, rz.os. Nazwisko, rz.rez.od, rz.rez.do, rz.rez.stanyrezerwacji.stan, rz.rez. TypyRezerwacji.Typ ).ToList()); rezerwacjedatagrid.itemssource = reztest; catch (Exception ex) MessageBox.Show("złapałem wyjątka: " + ex.message); Tabelkę wypełniamy na samym początku podczas ładowania głównego okna. Aby zawartość tabelki zmieniała się wraz ze zmianą zaznaczonych osób, należy podpiąć wypełnianie tabelki do zdarzenia osobydatagrid SelectionChanged. Jeżeli wszystko zostało wykonane poprawnie możemy przebudować bibliotekę (F6) oraz skompilować program (F5). 3.3 Dodanie tabelki zasobów Jeżeli działa wyszukiwanie po osobach należałoby dodać jeszcze analogiczną tabelkę zasobów. Jest to zadanie samodzielne weryfikujące wiedzę nabytą w tym rozdziale. 3.4 Kolejne usprawnienia Kolejną dość łatwym zadaniem, które można przy okazji zrobić to upewnić się, że tabelki operują na listach. Listy dadzą się sortować nie da się na nich robić operacji dodawania i usuwania natomiast IQueryable odwrotnie, dodawać i usuwać da się, niestety nie działa sortowanie. Problem ten oczywiście można obejść w dość łatwy sposób jednak wymaga on dopisania jeszcze pewnej części kodu. Tabelki operujące na listach nie powinny mieć możliwości dodawania przez użytkownika nowych rekordów. Należy odszukać i poustawiać odpowiednie atrybut CanUserAddRows= False. Wtedy zniknie nam dodatkowy pusty wiersz w tabelkach. Aby użytkownik mógł sortować należy mu na to pozwolić ustawiając odpowiednio atrybut CanUserSortColumns= True. Uwaga w edytorze Visual Studio pomimo iż domyślnie te atrybuty we właściwościach są ustawione to trzeba je wyłączyć i jeszcze raz włączyć by jawnie pojawiły się w pliku konfiguracyjnym. 9

Rozdział 4 Formularz dodawania rezerwacji Dodawanie rezerwacji umieścimy w głównym oknie na dole. Metodę dodającą rezerwacje stworzyliśmy na 4 laboratoriach. Nazywa się dodajrezerwacje i potrzebuje następujących danych wejściowych: identyfikator dodającego rezerwacje czas startu czas końca lista identyfikatorów osób którym rezerwujemy czas lista identyfikatorów zasobów typ rezerwacji 4.1 Zmiany w bibliotece Do tej pory metoda ta nie zwracała nic, a w razie niepowodzenia wypisywała na konsolę odpowiednie komunikaty. Obecnie chcemy, aby w dodawanie rezerwacji informowało nas czy operacja się powiodła. Jeżeli wystąpi błąd, będziemy mogli odczytać podstawową informację na jego temat. W tym celu musimy dokonać małej przeróbki w kodzie naszej biblioteki. Do klasy Rezerwacje dodajemy zmienną typu string i określamy dostęp do niej jako tylko do odczytu, tak jak na listingu 4.1 To tu zapiszemy stosowny komunikat w razie niepowodzenia. private string _mesg = null; public string Message get return _mesg; Listing 4.1: Zmienna do zapisu komunikatu. 10

Oczywiście trzeba zmienić w nagłówku funkcji dodajrezerwacje zwracany typ z void na bool. Oraz wszędzie tam gdzie gdzie funkcja wychodziła, dodać odpowiednio return true lub false np. (listing 4.2) Listing 4.2: Przykład zapisu komunikatu i wyjście z błędem. try context.savechanges(); //ostatecznie zapisujemy zmiany, EF robi to w transakcji. catch (Exception ex) _mesg = "Wystąpił błąd podczas dodawania rezerwacji" + ex.message; if (ex.innerexception!= null) _mesg = _mesg + "\n\nbłąd wewnętrzny:\n\n" + ex.innerexception.message; Console.WriteLine(_mesg); return false; Przy okazji testów, wyszedł jeden błąd. Złe zapytanie LINQ wyszukujące konfliktowe rezerwacje nie znajdywało rezerwacji w tym samy terminie tych samych zasobów gdy rezerwacja była na inną osobę. Przyczyna prosta. Pomiędzy warunkiem określającym zasoby oraz osoby było złączenie AND. To oznacza, że konfliktowa rezerwacja byłaby wtedy kiedy oprócz dat musiał zaistnieć warunek zasób i osoba. Gdy osoba była inna, konfliktu nie było. Dlatego ten fragment należy poprawić jak w listingu 4.3. Listing 4.3: Poprawione zapytanie wyszukujące konfliktowe rezerwacje. var konfliktowerez = (from rz in context.rezerwacje from zas in context.rezerwacjezasoby from zasobydorezerwacji in idkizasobow from os in context.rezerwacjeosoby from osobydorezerwacji in idkiludzikow where ((rz.od <= dtstart && rz.do > dtstart) (dtstart <= rz.od && dtstop > rz. Od)) && (( rz.idrezerwacji == zas.idrezerwacji && zas.idzasobu == zasobydorezerwacji ) (os.idrezerwacji == rz.idrezerwacji && os.idosoby == osobydorezerwacji )) select rz).distinct(); 4.2 Dodatkowa biblioteka W bogatym zestawie kontrolek WPF brakuje tak przydatnej kontrolki jak choćby TimePicker (pobieranie czasu) czy DecimalUpDown (zwiększanie i zmniejszanie liczby typu decimal za pomocą dwóch małych przycisków). Na szczęście możemy sobie stworzyć sami własne kontrolki. Tutaj nie będziemy tego robić. Posłużymy się darmową wersją biblioteki Extended WPF Toolkit Community Edition. Zaczniemy od ściągnięcia biblioteki ze strony wpftoolkit.codeplex.com Zawartość należy rozpakować do dowolnego katalogu do którego mamy dostęp, ale najlepiej stworzyć sobie w obrębie projektu katalog ExtendedWPFToolkit i tam wypakować albo całą zawartość pliku albo tylko Xceed.Wpf.Toolkit.dll. Następnie w przypadku systemów Windows 2008 Server 11

(możliwe że 7 lub 8 też należy tę operację wykonać) należy wejść we właściwości pliku i go odblokować klikając na guzik Odblokuj. Tak jak na pokazano na rysunku 4.1 Rysunek 4.1: Odblokowanie obcej biblioteki. Teraz we właściwościach projektu należy dodać referencję do pliku Xceed.Wpf.Toolkit.dll. Aby używać przestrzeni nazw z nowej biblioteki w plikach xaml należy dodać jeszcze: xmlns:xctk= http://schemas.xceed.com/wpf/xaml/toolkit Nazwa ta powinna nam się pojawić po wpisaniu xmlns:xctk= gdyby tak się nie stało należy sprawdzić czy dodaliśmy referencję po odblokowaniu pliku. Ewentualnie można też uruchomić ponownie Visual Studio. Szczegóły instalacji oraz używania biblioteki można doczytać w dokumentacji na stronie producenta. 4.3 Nowe komponenty Nowa biblioteka dostarczyła nam ciekawego komponentu DataTimePicker. Jest to komponent zawierający kalendarz ale i pozwalający na wpisanie godziny. Możemy określić sposób formatowania daty oraz czasu. Nowe elementy umieścimy w dodatkowej siatce (grid) a tę umieścimy w ostatnim wierszu siatki głównej. Jak było to już wspomniane, dodawanie rezerwacji wymaga list identyfikatorów użytkowników i zasobów. Te weźmiemy z istniejących tabel. Identyfikator osoby dodającej będzie wzięty z zalogowanej osoby. Osoba niezalogowana dostanie informację o braku uprawnień lub inny stosowny komunikat. Typ rezerwacji będzie pobierany z pola typu combi. Oczywiście trzeba będzie go wypełnić danymi z bazy. Daty od do będą wprowadzane za pomocą nowych kontrolek. Będzie też potrzebny guzik rozpoczynający proces dodawania rezerwacji. Cała konfiguracja wyglądu zawarta jest w kodzie 4.4 Listing 4.4: Konfiguracja nowych elementów potrzebnych do dodawania rezerwacji. 12

<Grid Grid.Row="3" Grid.ColumnSpan="2" VerticalAlignment="Top" Margin="0,5,0,0" HorizontalAlignment="Stretch"> <Grid.ColumnDefinitions> <ColumnDefinition Width="125*" /> <ColumnDefinition Width="125*" /> <ColumnDefinition Width="125*" /> <ColumnDefinition Width="125*" /> </Grid.ColumnDefinitions> <Grid.RowDefinitions> <RowDefinition Height="40"/> </Grid.RowDefinitions> <ComboBox Grid.Row="0" Height="25" HorizontalAlignment="Left" Margin="95,0,0,0" Name="comboTypRezerwacji" VerticalAlignment="Center" Width="110" DisplayMemberPath="Typ" ItemsSource="Binding"/> <Label Content="Typ Rezerwacji:" Grid.Row="0" Height="25" Margin="5,0,0,0" HorizontalAlignment="Left" VerticalAlignment="Center" Name="label1"/> <Label Content="Data Od:" Grid.Row="0" Grid.Column="1" Height="25" Margin="5,0,0,0" HorizontalAlignment="Left" VerticalAlignment="Center" Name="label2"/> <Label Content="Data Do:" Grid.Row="0" Grid.Column="2" Height="25" Margin="5,0,0,0" HorizontalAlignment="Left" VerticalAlignment="Center" Name="label3"/> <xctk:datetimepicker Height="25" Width="130" Grid.Row="0" Grid.Column="1" Margin="60,0,0,0" Name="dtpickerOd" HorizontalAlignment="Left" VerticalAlignment="Center" FormatString="dd.MM.yyyy HH:mm" Format="Custom" ValueChanged="dtpickerOd_ValueChanged"/> <xctk:datetimepicker Height="25" Width="130" Grid.Row="0" Grid.Column="2" Margin="60,0,0,0" Name="dtpickerDo" HorizontalAlignment="Left" VerticalAlignment="Center" FormatString="dd.MM.yyyy HH:mm" Format="Custom"/> <Button Content="Dodaj" Height="23" Width="75" Grid.Row="0" Grid.Column="3" Margin="0,0,0,0" Name="buttonDodaj" HorizontalAlignment="Center" VerticalAlignment="Center" Click="buttonDodaj_Click" /> </Grid> 4.4 Kod W pliku xaml widzimy, podpięte procedury obsługi zdarzeń ValueChanged oraz Click. Przed kompilacją należy wygenerować puste funkcje. Teraz powinniśmy wypełnić pole combi. Wykonamy to za pomocą jednej linii combotyprezerwacji.itemssource = typyrezerwacji.getall(); Daty można podawać ręcznie, jednak miłym udogodnieniem było by gdyby wypełnione były one bieżącą datą. Użytkownik i tak nie będzie ustawiał daty wcześniejszej ale będzie miał trochę mniej szukania czy pisania. Może zgodzi mu się rok i miesiąc, a może nawet i godzina. Niestety pobranie bieżącego czasu będzie z dokładnością do sekund a to skutkowałoby zmuszeniem użytkownika do zerowania części sekundowej a i zapewne ustawiania części minutowej. Dobrym rozwiązaniem było by zaokrąglenie daty do 5 minut na przód. Wykonamy to za pomocą prostej funkcji zaczerpniętej ze strony: http://stackoverflow.com/questions/7029353/c-sharp-round-up-time-to-nearestx-minutes Tworzymy nową klasę 4.5 w pliku Enumeracje.cs (lub innym dostępnym w projekcie). public class TimeTools Listing 4.5: Funkcja zaokrąglająca czas. 13

public DateTime RoundUp(DateTime dt, TimeSpan d) return new DateTime(((dt.Ticks + d.ticks/2) / d.ticks) * d.ticks); Teraz wystarczy jej użyć w kodzie 4.6 ładującym główne okno. Listing 4.6: Ustawienie czasu. TimeTools tt = new TimeTools(); dtpickerod.value = tt.roundup(datetime.now, TimeSpan.FromMinutes(5)); Jak widzimy zrobiliśmy to tylko dla czasu początkowego. Czas końcowy ustawimy w funkcji 4.7 dtpickerod ValueChanged podpiętej do zdarzenia ValueChanged. Jest on ustawiany za każdym razem gdy pole Do jest mniejsze niż Od lub jest ono nie ustawione. Logicznym jest fakt, że czas końca rezerwacji nie może być wcześniejszy niż jej początek. Dlatego kolejnym ułatwieniem dla użytkownika jest automatyczne ustawienie czas końca na czas przynajmniej taki jak czas początku. Listing 4.7: Funkcja zaokrąglająca czas. private void dtpickerod_valuechanged(object sender, RoutedPropertyChangedEventArgs< object> e) //jeżeli po zmianie w pierwszym polu data w drugim jest mniejsza //to można już ją nastawić przynajmniej na taką samą if (dtpickerdo.value == null) //gdyby nie było to ustawiamy czas bieżący z zaokrągleniem do 5 minut w górę. TimeTools tt = new TimeTools(); dtpickerdo.value = tt.roundup(datetime.now, TimeSpan.FromMinutes(5)); if (dtpickerdo.value.value < dtpickerod.value.value) dtpickerdo.value = dtpickerod.value; Kolejną pracą samodzielną jest ustawienie automatycznej zmiany czasu początku w przypadku, gdy użytkownik zmieni czas końca tak, by czas początku nie był późniejszy niż czas końca. Pozostała nam jeszcze tylko obsługa guzika Dodaj. Pod kliknięcie podpinamy kod 4.8 Listing 4.8: Funkcja obsługi guzika Dodaj. private void buttondodaj_click(object sender, RoutedEventArgs e) if (zalogowany==null) MessageBox.Show("Nie jesteś zalogowany, nie możesz dodawać rezerwacji"); List<long> idkiosob = new List<long>(); idkiosob = (from os in osobydatagrid.selecteditems.oftype<osoby>() select os. IdOsoby).ToList(); List<long> idkizasobow = new List<long>(); idkizasobow = (from zs in zasobydatagrid.selecteditems.oftype<zasoby>() select zs.idzasobu).tolist(); if (rezerwacje.dodajrezerwacje(zalogowany.idosoby, dtpickerod.value.value, dtpickerdo.value.value, idkizasobow, idkiosob, (TypyRezerwacji) combotyprezerwacji.selecteditem)) 14

MessageBox.Show("ok nowa rezerwacja"); wypelnijtabelkerezerwacji(); else MessageBox.Show("Nie dodano rezerwacji: \n" + rezerwacje.message); Jak widać identyfikatory osób i zasobów brane są z zaznaczonych wierszy tabelek. Użytkownik dodający to ten zalogowany (niezalogowany nie będzie mógł dodać rezerwacji). Typ rezerwacji pobierany jest z pola combo. Sprawdzamy też poprawność wykonania operacji i w razie niepowodzenia wyświetlamy komunikat jaki przygotowała nam funkcja dodajrezerwacje. 15

Rozdział 5 Testowanie i wnioski Jak widać aplikacja jest niemal gotowa. Jednak do bardzo podstawowej funkcjonalności jeszcze jej brakuje dwóch rzeczy. Usuwania rezerwacji oraz przegląd i zatwierdzanie lub odrzucanie propozycji. Zauważyliśmy też, że na chwilę obecną nie ma obsługi uprawnień. Każdy zalogowany lub nie może robić co chce. W zaufanym środowisku to jeszcze nie jest problemem, jednak usuwanie i akceptacja rezerwacji są kluczowe. Aplikację można zacząć już sprawdzać i wypisywać element, które należy zmienić, usprawnić, dodać lub zabrać. Przykładowo: skoro nie możemy dodawać w tabelkach to należy ustawić zakaz usuwania (przynajmniej w tabeli osób i zasobów w głównym oknie). Jeżeli te tabelki służą jedynie do edycji należy ustawić je jako tylko do odczytu. Bindowanie nie powinno być TwoWay co oznacza możliwości nanoszenia zmian na bazę. Idąc dalej można pokusić się o dodatkowe usprawnienia np. guzik zaznacz wszystko, opcje filtrowania tylko aktywne, ukryj archiwalne itp. Ostatnim zadaniem jest spisanie tego typu uwag pomocnych w dalszym rozwoju. Tak by interfejs był spójny i jak najbardziej wygodny, czy skalując okna nie tracimy nic z czytelności, czy wszystkie wyświetlane guziki i pozycje w menu są obsługiwane. Czy wyświetlane są odpowiednie komunikaty w razie powodzenia i niepowodzenia. Trzeba oczywiście wyłapać wszystkie przypadki gdy program nam się zakończy błędem (jest bardzo wiele takich miejsc ponieważ praktycznie nie nastawialiśmy się na obsługę wyjątków). Oprócz błędów krytycznych trzeba znaleźć też błędy w działaniu, np. czy da się dodać rezerwacje tak, by były one kolidujące ze sobą, albo gdy data początku jest późniejsza niż data końca. Czy wprowadzając błędny format daty program to przyjmie, co gdy błędnie podamy konfiguracje serwera bazodanowego itd. 16

Rozdział 6 Oceny Jak zawsze całość wykonanego ćwiczenia jest na 5 punktów w skład których wchodzi: Stworzenie okna edycji osób (wraz ze skalowaniem i poprawnym rozmieszczeniem elementów) 1 pkt. Rozmieszczenie i skonfigurowanie wyglądu elementów wchodzących w skład przeglądu rezerwacji (oczywiście ze skalowaniem itp.) 0,5 pkt Dodanie kodu obsługującego przegląd rezerwacji 0,5 pkt. Dodanie swojej tablicy zasobów i uwzględnienie jej przy przeszukiwaniu rezerwacji 0,5 pkt. Zmiany w RezerwacjeLib potrzebne do dodawania rezerwacji 0,5 pkt. Dodanie nowej biblioteki z kontrolkami i użycie ich w projekcie 0,5 pkt. Kod obsługujący dodawanie rezerwacji 0,5 pkt Automatyczna zmiana czasu Od w przypadku zmian czasu Do 0,5 pkt. Testowanie i raport z testów 0,5 pkt. Na sprawozdania jak zawsze czekam do końca poniedziałku. 17