„WynajemSal” realizowany w trakcie drugiego semestru studiów

Transkrypt

„WynajemSal” realizowany w trakcie drugiego semestru studiów
Janusz Górczyński
Projekt
„WynajemSal” realizowany w trakcie drugiego
semestru studiów podyplomowych
WSZiM w Sochaczewie, 2011
Spis treści
1
WSTĘP .................................................................................................................... 3
2
BAZA DANYCH .................................................................................................... 4
2.1
2.2
3
TABELE ............................................................................................................. 4
PROCEDURY PRZECHOWYWANE ...................................................................... 11
APLIKACJA WINDOWSOWA ......................................................................... 15
3.1
UTWORZENIE PORJEKTU .................................................................................. 15
3.2
FORMULARZ GŁÓWNY APLIKACJI .................................................................... 17
3.3
MODUŁ OGÓLNY ............................................................................................. 20
3.4
PLIK KONFIGURACYJNY ................................................................................... 23
3.5
POTRZEBNE KLASY ZEWNĘTRZNE .................................................................... 26
3.5.1 CForStorageSub ......................................................................................... 26
3.5.2 CValidacja.................................................................................................. 27
3.5.3 CDataGridPrint.......................................................................................... 28
3.6
FORMULARZ FRMNOWASALA ......................................................................... 29
3.6.1 Klasa CAdministracja ................................................................................ 32
3.6.2 Uzupełnienie kodu klasy frmNowaSala ...................................................... 35
3.6.3 Uzupełnienie kodu formularza głównego ................................................... 36
3.6.4 Formularz frmNowaSala w „pracy” .......................................................... 37
2
1 Wstęp
W trakcie zajęć drugiego semestru studiów podyplomowych na kierunku „Projektowanie
baz danych” zrealizujemy od podstaw projekt, który otrzyma roboczą nazwę „WynajemSal”.
W ramach realizacji tego projektu zostaną zrealizowane trzy główne jego komponenty
obejmujące:
1) Zaprojektowanie bazy danych na MS SQL Server;
2) Utworzenie aplikacji VB.NET będącej interfejsem tej bazy danych;
3) Utworzenie aplikacji ASP.NET pełniącej rolę interfejsu komunikacyjnego dla
potencjalnych klientów.
Podstawą rozpoczęcia prac nad tym projektem jest przemyślenie funkcjonalności
zamierzonego rozwiązania, a sam pomysł zajęcia się takim projektem wynika między innymi z
dość dużego zainteresowaniem różnych podmiotów wynajęciem naszych sal dydaktycznych.
W przypadku pomyślnej realizacji tego projektu cały proces udostępniania wolnych
pomieszczeń ulegnie maksymalnemu uproszczeniu poprzez:
• Umożliwienie via strona www zapoznanie się ze szczegółową ofertą uczelni w
zakresie wynajęcia pomieszczeń;
• Możliwość złożenia formalnego zamówienia na wynajem określonego typu sali (czy
sal) poprzedzone uzyskaniem informacji o kosztach wynajęcia, sprawdzeniu
dostępności i przy dalszym zainteresowaniu złożenia zamówienia;
• Automatyczne wygenerowanie i dostarczenie w formie elektronicznej formalnej
umowy finansowej (e-faktury);
• Zarządzanie procesem wynajmowania pomieszczeń via intuicyjny interfejs.
3
2 Baza danych
Baza danych zostanie utworzona na serwerze MS SQL Server 2008 (słuchacze zakładają
ją indywidualnie dla siebie), na potrzeby tego opracowania będziemy się posługiwać nazwą
bazy SaleWSZiM. W bazie zostaną utworzone tabele, procedury przechowywane i funkcje,
diagram bazy i określone reguły związane z jej bezpieczeństwem. Przyjmiemy zasadę, że
jedyna możliwość manipulowania danymi będzie się odbywała via procedury przechowywane.
2.1 Tabele
Tabela RodzajSal będzie zawierać wykaz typów pomieszczeń, które znajdą się w
ofercie wynajmu. Pole idtyp jest kluczem tej tabeli (automatyczna inkrementacja ze skokiem
1). Pole rodzaj zawiera skrótowy opis odnoszący się generalnie do liczby miejsc, a pole
opis będzie wykorzystywane przy prezentacji zdjęć poszczególnych sal.
Poniżej przykładowe dane zapisane w tej tabeli.
Tabela Sale zawiera informacje o poszczególnych salach dydaktycznych, które mogą
być wypożyczane. Kluczem tej tabeli jest pole idnr przechowujące unikalny numer sali (a
więc klucz tabeli nie może być automatycznie inkrementowany)
4
Pole projektor informuje o tym, czy w sali jest
zamontowany projektor podwieszany (wartość 1) czy też nie (wartość
0).
Pole id_typ jest kluczem obcym (kluczem tabeli
RodzajSal) i będzie wykorzystywane do ustanowienia relacji typu
jeden-do-wielu.
Poniżej widok przykładowych danych zapisanych w tabeli
Sale.
Tabela ZdjeciaSal została utworzona po to, aby można
było dla danej sali dysponować więcej niż jednym jej zdjęciem.
Pole foto będzie przechowywać nazwę zdjęcia danej sali, nazwa ta w połączeniu z
nazwą katalogu stworzy pełną ścieżkę dostępu do zdjęć pomieszczeń, które będziemy
prezentować potencjalnym klientom na stronie www. Pełna nazwa katalogu będzie pobierana z
pliku konfiguracyjnego aplikacji (zarówno windowsowej jak i webowej). Przykładowe dane w
tej tabeli pokazane są niżej.
Tabela Stawki ma za zadanie przechowywanie informacji o kosztach wynajęcia
poszczególnych rodzajów sal. Tabela jest tak zaprojektowana, aby była możliwość
przechowywania informacji o historii wprowadzanych zmian stawek, zadanie to będzie
realizować pole o nazwie DataZmiany typu smalldatetime.
5
Kluczem tej tabeli jest pole idc z automatyczną inkrementacją ze skokiem jeden.
Pozostałe pola (z wyjątkiem DataZmiany) są typu money, ich odpowiednikiem po stronie
VB.NET czy ASP.NET będzie typ decimal.
Pole GodzinaKomputera przechowuje informacje o stawce godzinowej za jedno
stanowisko komputerowe, podobnie pole GodzinaProjektora opisuje godzinny koszt
udostępnienia projektora (nie dotyczy sal komputerowych). Pozostałe cztery pola przechowują
informacje o kosztach wynajęcia określonego typu sali, jest to koszt dzienny (niezależnie od
liczby osób i liczby godzin dziennie).
Poniżej widok przykładowych danych zapisanych w tabeli Stawki.
Tabela NaszeTerminy ma przechowywać terminarz zajęć własnych z podaniem
rodzaju zajęć i (ewentualnie) wykazu sal zajętych. Generalnie chodzi o uniknięcie potencjalnych konfliktów.
6
Pole TerminSobota typu smalldatetime ma przechowywać sobotnią datę zjazdu
sobotnio-niedzielnego, datę niedzielną ustalimy w odpowiedniej procedurze przechowywanej.
Pole RodzajZajec przyjmie wartość 1 dla naszych statutowych studiów, wartość 2 dla
studiów podyplomowych. W przypadku tych ostatnich pole SaleZajete będzie zawierać
numery sal rozdzielone symbolem przecinka.
Poniżej widok przykładowych danych zapisanych w tabeli NaszeTerminy.
Tabela Klienci będzie przechowywać informacje o firmach wynajmujących nasze
pomieszczenia. Projekt tej tabeli pokazany jest poniżej.
W tabeli tej zapisano poniższe, absolutnie przypadkowe dane (na etapie projektowania
aplikacji).
7
Tabela RezerwacjaKlienta ma do spełnienia w naszym projekcie bardzo ważną
rolę, to w tej tabeli potencjalny klient będzie zapisywał swoje zapotrzebowanie na wynajęcie
odpowiednich pomieszczeń dydaktycznych.
Pole idk jest identyfikatorem klienta z tabeli Klienci, wykorzystamy je do
nawiązania relacji między tabelą Klienci i RezerwacjaKlienta. Podobną rolę pełnią
pola idnr oraz idnrBufet, które powiążą tę tabelę z tabelą Sale.
Pole dataRejestracji będzie przechowywać datę zgłoszenia zapotrzebowania na
wynajęcie sali. Wartość tego pola w połączeniu z wartością klucza (idr) tej tabeli zostanie
wykorzystana do przygotowania formalnej umowy wynajęcia danej sali.
8
Pole rodzajSali wskazuje na rodzaj sali będącej przedmiotem wynajęcia (chodzi o
typ sali dydaktycznej).
Pole zakresDat typu tekstowego będzie przechowywać wszystkie daty wynajęcia
rozdzielone symbolem przecinka. Jego uzupełnieniem jest pole liczbaDni, przechowujące
informację o pełnej liczbie dni wynajęcia (tak naprawdę można z tego pola zrezygnować –
dlaczego?). Pole dniTygodnia typu tekstowego reprezentuje ciąg siedmiu znaków,
odpowiednio 1 lub 0, wskazujących na dany dzień tygodnia (od poniedziałku do niedzieli).
Jeden oznacza wynajęcie danej sali w danym dniu.
Pole liczbaGodzin będzie przechowywać informację o planowanej liczbie godzin
zajęć, a pole liczbaKompow informację o liczbie potrzebnych stanowisk komputerowych. W
przypadku wynajmowania innej sali niż komputerowa to pole przyjmuje wartość zero.
Pola projektor i salaBufetowa przyjmują wartość jeden, jeżeli wynajmujący
zgłasza potrzebę udostępnienia projektora czy sali bufetowej. Jeżeli nie, to pola te otrzymują
wartość zero.
Pola idnr oraz idnrBufet będą przechowywać numer sali przewidzianej do
wynajęcia oraz numer sali bufetowej, jeżeli było takie zapotrzebowanie. Jeżeli nie, to pole to
otrzyma wartość zero.
Pola kosztNetto oraz kosztOstateczny zawierają odpowiednio koszt netto
wyliczony zgodnie z przyjętym algorytmem i automatycznym rabatem wynikającym z wartości
zamówienia oraz koszt netto po ostatecznym ustaleniu (przykładowo z rabatem dla stałego
klienta). Na etapie składania zamówienia wartości obu pól są takie same, a ewentualna zmiana
(zmniejszenie) pola kosztOstateczny może być przeprowadzona przez osobę zarządzającą
wynajmowaniem sal.
Pole dataZaplaty pozostaje puste do momentu wpłaty należności za zrealizowaną
transakcję udostępnienia pomieszczeń dydaktycznych. Po wpływie należności na konto Uczelni
pole to jest wypełniane przez osobę nadzorującą wynajem pomieszczeń, jednocześnie pole
archiv otrzymuje wartość zero.
Pola archiv przyjmuje wartość 1 dla oznaczenia tych pozycji, które jeszcze nie zostały
zrealizowane, a wartość zero po zrealizowaniu zamówienia. W przypadku anulowania
zamówienia pole to przyjmie wartość dwa.
Przykładowe dane zapisane w tabeli RezerwacjaKlienta pokazane są niżej (tutaj
bez pól kosztNetto, kosztOstateczny i dataZaplaty).
9
A tak wygląda pole liczbaDni z większą liczbą szczegółów.
Być może, że w trakcie prac nad projektem będziemy musieli zmodyfikować
proponowane tabele, dodać nowe itd., ale to wyjdzie w trakcie pracy. Na ten moment mamy
utworzone tabele pokazane niżej na diagramie.
10
2.2 Procedury przechowywane
Procedura pCzyDobryNip sprawdza, czy potencjalny klient webowy już był
zarejestrowany w naszej bazie (dokładnie, czy istnieje rekord o takim Nip w tabeli Klienci).
Jeżeli istnieje, to procedura zwraca jego identyfikator, jeżeli nie, to zwraca wartość zero.
CREATE procedure [dbo].[pCzyDobryNip]
@nip nvarchar(13), @idk int out
as
declare @ip as int
select @ip=idk from dbo.Klienci where NIP=@nip
set @idk=ISNULL(@ip,0)
Procedura
identyfikatorze.
pDajKlientaWgId
zwraca
wszystkie
dane
klienta
o
podanym
create procedure [dbo].[pDajKlientaWgId]
@id int
as
select * from dbo.Klienci where idk=@id
Procedura pDajDaneKlientow zwraca komplet informacji z tabeli Klienci
sortując je po polu Nazwa.
create procedure [dbo].[pDajDaneKlientow]
as
select * from dbo.Klienci order by Nazwa
Procedura pWstawKlienta odpowiada za wstawienie do tabeli Klienci nowego
rekordu z jednoczesnym zwróceniem jego identyfikatora.
create procedure [dbo].[pWstawKlienta]
@naz nvarchar(100),@adres nvarchar(100), @nip nvarchar(13),
@mail nvarchar(50), @tel nvarchar(50), @osoba nvarchar(100),
@idk int out
as
insert into dbo.Klienci (Nazwa, Adres, NIP, mail, telefon,
OsobaKontaktowa)
values (@naz, @adres, @nip, @mail, @tel, @osoba)
set @idk=scope_identity()
Procedura pUpdateKlienci odpowiada za modyfikację danych wskazanego
parametrem @idk klienta.
11
create procedure [dbo].[pUpdateKlienci]
@idk int, @nazwa nvarchar(100), @adres nvarchar(100),
@nip nvarchar(13), @mail nvarchar(50), @telefon nvarchar(50),
@osoba nvarchar(100)
as
update dbo.Klienci set Nazwa=@nazwa, Adres=@adres, NIP=@nip,
mail=@mail, telefon=@telefon, OsobaKontaktowa=@osoba
where idk=@idk
Procedura pDajNaszeTerminy odpowiada za zwrócenie pełnej informacji o
„naszych” terminach zajęć z podanego zakresu dat, ich rodzaju oraz ewentualny wykaz zajętych
sal. Poza terminem sobotnim zwracane są daty o jeden dzień większe (czyli niedzielne). Jest to
realizowane funkcją SQL dateadd(symbol_interwału, o_ile_więcej, termin_wyjściowy).
create procedure [dbo].[pDajNaszeTerminy]
@datap smalldatetime, @datak smalldatetime
as
select Terminsobota as Sobota, dateadd(dd,1,TerminSobota) as
Niedziela, Rodzajzajec, SaleZajete
from dbo.NaszeTerminy
where TerminSobota >=@datap and
dateadd(dd,1,TerminSobota) <=@dataK
Procedura pDajStawki zwraca ostatni (najstarszy) rekord z tabeli Stawki.
create procedure [dbo].[pDajStawki]
as
select top(1) GodzinaKomputera,
SredniaSala, DuzaSala, Aula
from dbo.Stawki
order by DataZmiany desc
GodzinaProjektora, MalaSala,
Procedura pDajNumerySalWgTypu odpowiada za zwrócenie numerów tych sal,
które odpowiadają przekazanemu parametrem @idt typowi sal. Lista zwróconych numerów
możemy modyfikować parametrem @idpro.
CREATE procedure [dbo].[pDajNumerySalWgTypu]
@idt int, @idpro int
as
if @idpro=1
select idnr from dbo.Sale where id_typ=@idt and projektor=1
else
select idnr from dbo.Sale where id_typ=@idt
12
Procedura pDajWykazTerminow zwraca pojedynczą wartość typu tekstowego
zawierającą listę aktywnych dat rezerwacji sal określonego typu.
CREATE procedure [dbo].[pDajWykazTerminow]
@idnr nvarchar(100), @zakres nvarchar(1000) out
as
declare @xp as nvarchar(1000)
select @xp=zakresDat
from dbo.RezerwacjaKlikenta
where archiv=1 and idnr in (@idnr)
set @xp=ISNULL(@xp,'')
Procedura pDajZakresDatDlaSali zwraca
zawierającą wykaz dat aktywnych rezerwacji dla danej sali.
pojedynczą
wartość
tekstową
create procedure [dbo].[pDajZakresDatDlaSali]
@idnr int, @zakres nvarchar(1000) out
as
select @zakres=zakresDat
from dbo.RezerwacjaKlikenta
where idnr=@idnr and archiv=1
Set @zakres=ISNULL(@zakres,'')
Procedura pCzyWolnaDanaSala sprawdza, czy dana sala jest wolna.
create procedure [dbo].[pCzyWolnaDanaSala]
@idnr int, @TakNie int out
as
select @TakNie=COUNT(*)
from dbo.RezerwacjaKlikenta
where archiv=1 and idnr =@idnr
Procedura pCzyWolnaSalaBufetowa sprawdza, czy sala danego typu nie została
zarezerwowana jako sala bufetowa.
create procedure [dbo].[pCzyWolnaSalaBufetowa]
@idtyp int, @TakNie int out
as
select @TakNie=COUNT(*)
from dbo.RezerwacjaKlikenta
where archiv=1 and idnrBufet in
(
select idnr from dbo.Sale where id_typ=@idtyp
)
13
Procedura pCzyWolnaSala odpowiada za sprawdzenie, czy jest wolna jakaś sala
określonego typu.
create procedure [dbo].[pCzyWolnaSala]
@idtyp int, @idpro int, @TakNie int out
as
if @idpro=1
select @TakNie=COUNT(*)
from dbo.RezerwacjaKlikenta where archiv=1 and idnr in
(
select idnr
from dbo.Sale
where id_typ=@idtyp and projektor=@idpro
)
else
select @TakNie=COUNT(*)
from dbo.RezerwacjaKlikenta
where archiv=1 and idnr in
(
select idnr
from dbo.Sale
where id_typ=@idtyp
)
14
3 Aplikacja windowsowa
W tej części całego projektu zajmiemy się przygotowaniem aplikacji windowsowej,
która umożliwi nam pełną obsługę i zarządzanie bazą daną omówioną w poprzednim rozdziale.
Utworzymy nowy projekt aplikacji typu Windows Forms, z uwagi na złożoność projektu
będzie to aplikacja z więcej niż jednym formularzem, w jej skład będą wchodziły niektóre z
klas, które poznaliśmy w trakcie zajęć pierwszego semestru. Klasa CForStorageSub będzie
przez nas wykorzystywana jako klasa bazowa, będziemy tworzyć na jej podstawie szereg
specjalistycznych klas potomnych wykorzystywanych do realizacji konkretnych zadań
projektowanej aplikacji.
3.1 Utworzenie porjektu
Zaczynamy od uruchomienia VisualStudio.NET i wywołania polecenia New Project, co
skutkuje otwarciem okna nowego projektu. Standardowo w oknie zainstalowanych szablonów
powinien być wybrany szablon Windows Forms Application, jeżeli tak jest to przechodzimy do
pola tekstowego Name i wpisujemy w nim nazwę tworzonej aplikacji.
15
W pokazanej sytuacji wpisana jest nazwa SpWynajemSal (zgodnie ze zmodyfikowaną
notacją węgierską). Po akceptacji przycisku OK tworzone jest nowe rozwiązanie o podanej
przez nas nazwie.
Po krótkiej chwili nowy projekt zostanie utworzony, na ten moment zawiera on jedynie
jeden formularz o domyślnej nazwie Form1.
Po utworzeniu projektu powinniśmy zapisać go na dysku, wystarczy w tym celu wywołać
polecenie Save All z menu File. W oknie dialogowym tego polecenia wskazujemy folder
docelowy (korzystamy z przycisku Browse) i przyciskiem Save zapisujemy projekt,
16
3.2 Formularz główny aplikacji
Jak wcześniej zostało powiedziane będziemy tworzyć projekt wielo formularzowy, tym
samym musimy do projektu wprowadzić specjalny formularz, który będzie kontenerem
(pojemnikiem) dla innych formularzy. Można także przekształcić istniejący formularz w
formularz typu MDI (ang. Multi Document Interface), wystarczy w tym celu zmienić jego
właściwość IsMdiContainer z domyślnej False na True, tak jak to pokazano niżej.
Korzystając z okienka Solution Explorer zmieniamy jeszcze nazwę naszego formularza
z dotychczasowej Form1.vb na frmMdiForm.vb. Zmiany tej możemy dokonać z menu
kontekstowego uruchamianego prawym przyciskiem myszy po wskazaniu obiektu formularza,
wystarczy wywołać polecenie Rename z tego menu.
Kolejnym krokiem jest zmiana właściwości Name tego formularza z dotychczasowej
Form1 na frmMdiForm. Robimy to w oknie właściwości tego formularza (po lewej sytuacja
przed zmianą, po prawej po zmianie). Dla przypomnienia – system rozpoznaje każdy obiekt po
jego nazwie.
17
Jedną z istotnych właściwości formularza typu MDI jest możliwość zbudowania menu
użytkownika. Robimy to poprzez dodanie do projektu formularza specjalnej kontrolki
(formantu) o nazwie MenuStrip, znajdziemy ją w przyborniku w grupie Menus & Toolbars.
Kontrolkę tę umieszczamy w naszym formularzu, ale po zwolnieniu myszy zostaje ona
umieszczona nie w samym formularzu, lecz na specjalnym pasku, który nosi nazwę tzw. tacy.
Poniżej widok VS po wprowadzonych zmianach.
Widzimy egzemplarz formantu MenuStrip z nazwą domyślną MenuStrip1 (nie ma
potrzeby jej zmiany), w formularzu pod wierszem tytułowym pojawił się pasek menu,
będziemy w nim w trybie graficznym definiować poszczególne pozycje menu.
Na tym etapie utworzymy pozycję Administracja z poleceniami jak niżej:
- Typy sal (podpolecenia: Nowy typ, Edycja, Usunięcie);
- Sale (podpolecenia: Nowa sala, Edycja, Usunięcie);
- Zdjęcia sal (podpolecenia: Dodanie, Aktualizacja, Uusnięcie, Przegląd);
- Stawki (podpole cnie: Nowe stawki, Edycja, Usunięcie)
- Separator
- Zakończenie
18
Poniżej widok okna projektowania w trakcie tworzenia menu Administracja.
W analogiczny sposób dodajemy pozostałe pozycje menu, przy czym korzystając z
menu kontekstowego możemy wstawić taką pozycję jak separator.
Przed uruchomieniem naszej aplikacji możemy jeszcze zmienić dwie własności
formularza frmMdiForm, jedna z nich to właściwość Text (tytuł formularza), a druga to
WindowState, która określa sposób otwarcia formularza. Poniżej zmienione wartości obu
właściwości.
Możemy już uruchomić nasz projekt, wystarczy w tym celu nacisnąć klawisz F5, poniżej
pokazany jest (zminiaturyzowany) widok okna tej aplikacji z rozwiniętym menu Administracja.
19
Na tym etapie aplikacja nie reaguje na klik poszczególnych pozycji menu, jest to
spowodowane tym, że jeszcze nie zostały napisane odpowiednie procedury zdarzeniowe
reagujące na klik danego polecenia.
3.3 Moduł ogólny
W każdej poważniejszej aplikacji składającej się z wielu formularzy, raportów i klas
zachodzi potrzeba zadeklarowania stałych, zmiennych i innych obiektów (struktury,
wyliczenia) w taki sposób, aby były dostępne dla wszystkich innych modułów tworzonej
aplikacji.
Podobne oczekiwania można także sformułować odnośnie procedur i funkcji, jeżeli mają
być dostępne z innych modułów, to muszą być zadeklarowane w specjalnym module (pliku)
typu Module.
Dodamy taki obiekt do
naszego projektu, a postępowanie jest standardowe: w
oknie
Solution
Explorer
klikamy prawym przyciskiem
myszy
nazwę
naszego
rozwiązania, z menu kontekstowego wybieramy polecenie
Add i dalej New Item… .
20
W otwartym oknie Add New Item wskazujemy obiekt Module, w polu Name wpisujemy
jego nazwę i przyciskiem Add dodajemy obiekt do naszego rozwiązania.
W kodzie klasy obiektu ModulWspolny możemy teraz zadeklarować potrzebne
zmienne, stałe, struktury, utworzyć publiczne i prywatne procedury i funkcje.
Poniże kod tych deklaracji, w miarę dalszych prac można oczekiwać, że będziemy
dodawać albo nowe zmienne, albo nowe procedury i funkcje do tego modułu.
Jako pierwsze deklarowane są takie zmienne jak strTytul (tu będzie tytuł naszej
aplikacji), strBaza (łańcuch połączenia do bazy SQL), Kwestor (nazwisko i imię osoby
odpowiedzialnej za wystawienie faktury), stawkaVAT (aktualnie obowiązująca stawka VAT
na usługi wynajmu pomieszczeń), bSqlOK (zmienna wskaźnikowa, wartość True wtedy, gdy
z pliku konfiguracyjnego zostaną pobrane potrzebne informacje).
Module ModulWspolny
Public strTytul, strBaza, Kwestor As String, stawkaVAT As Decimal,
bSqlOK As Boolean
' tablica Cena() będzie przechowywać stawki za wynajem
Public Cena(5) As Decimal
' struktura OpisSal definiuje zmienną użytkownika, która w zwięzły
' sposób ma opisywać oczekiwania osoby zainteresowanej wynajęciem
Public Structure OpisSal
Dim rodzajSali As Integer
Dim zakresDat As String
21
Dim dniTygodnia As String
Dim liczbaDni As Integer
Dim liczbaGodzin As Integer
Dim liczbaStanowiskKomputerowych As Integer
Dim plusProjektor As Integer ' 1 = tak, 0 -nie
Dim plusBufet As Integer ' 1 = tak, 0 = nie
Dim WolnaSala As Integer
Dim WolnaSalaBufetowa As Integer
End Structure
' Publiczna funkcja UstalGrb ma za zadanie ustalenie, który z
' formantów typu RadioButton w kontenerze GroupBox jest zaznaczony.
' warunkiem poprawnego funkcjonowania jest zapisanie zwracanej
' wartości w nazwie każdego z radiobuttonów.
' dopuszczalna numeracja od 0 do 9
Public Function UstalGrb(ByVal grb As GroupBox) As Integer
Dim rdb As RadioButton
For Each rdb In grb.Controls
If rdb.Checked Then
Try
Return CInt(rdb.Name.Substring(rdb.Name.Length - 1, 1))
Catch ex As Exception
Return 0
End Try
End If
Next
Return 0
End Function
' prywatna funkcja wykorzystywana w innych procedurach tego modułu
Private Function WymienZnakNaPozycji(ByVal tekst As String, _
ByVal pozycja As Integer, ByVal znak As String) As String
Select Case pozycja
Case 1
Return znak & tekst.Substring(1, tekst.Length - 1)
Case tekst.Length
Return tekst.Substring(0, tekst.Length - 1) & znak
Case Else
Return tekst.Substring(0, pozycja - 1) & znak & _
tekst.Substring(pozycja, tekst.Length - pozycja)
End Select
End Function
' Publiczna funkcja UstalGrbVCheckBox ma za zadanie ustalenie, które
' z formantów typu CheckBox w kontenerze GroupBox są zaznaczone.
22
' Funkcja zwraca ciąg znaków zer i jedynek o liczbie znaków równej
' liczbie ChceckBoxow w kontenerze, symbol 1 oznacza zaznaczenie.
' Funkcja pobiera numer formantu z jego nazwy, numer występuje po
' znaku podkreślenia.
Public Function UstalGrbCheckBox(ByVal grb As GroupBox) As String
Dim chb As CheckBox, x As Integer, tp As String
tp = RepeatString("0", grb.Controls.Count)
Dim tw() As String
For Each chb In grb.Controls
If chb.Checked Then
Try
tw = chb.Name.Split("_")
x = CInt(tw(1))
tp = WymienZnakNaPozycji(tp, x, "1")
Catch ex As Exception
Return "Błąd"
End Try
End If
Next
Return tp
End Function
' prywatna funkcja wykorzystywana w innych procedurach tego modułu
Private Function RepeatString(ByVal znak As Char, _
ByVal ile As Integer) As String
Dim i As Integer, t As String = ""
For i = 1 To ile
t &= znak
Next
Return t
End Function
End Module
3.4 Plik konfiguracyjny
W środowisku VS.NET istnieje możliwości przechowywania wybranych informacji w
specjalnym pliku XML o nazwie zastrzeżonej app.config. Po kompilacji plik ten
umieszczany jest w tym samym folderze co plik wykonywalny aplikacji pod nazwą taką jak
nazwa rozwiązania uzupełnioną o fragment „,exe.config” (w przykładzie omawianym w tym
opracowaniu będzie to plik o nazwie SpWynajemSal.exe.config). Plik ten może być
swobodnie modyfikowany w dowolnym edytorze XML, a także w np. Notatniku.
Wiele wierszy tego pliku nie może być zmienione, nas będzie interesował fragment
między znacznikami <appSettings> i </appSettings> .
23
<?xml version="1.0" encoding="utf-8" ?>
<configuration>
<system.diagnostics>
<sources>
<!-- This section defines the logging configuration for
My.Application.Log -->
<source name="DefaultSource" switchName="DefaultSwitch">
<listeners>
<add name="FileLog"/>
<!-- Uncomment the below section to write to the
Application Event Log -->
<!--<add name="EventLog"/>-->
</listeners>
</source>
</sources>
<switches>
<add name="DefaultSwitch" value="Information" />
</switches>
<sharedListeners>
<add name="FileLog"
type="Microsoft.VisualBasic.Logging.FileLogTraceListener,
Microsoft.VisualBasic, Version=8.0.0.0, Culture=neutral,
PublicKeyToken=b03f5f7f11d50a3a,
processorArchitecture=MSIL"
initializeData="FileLogWriter"/>
<!-- Uncomment the below section and replace
APPLICATION_NAME with the name of your application to
write to the Application Event Log -->
<!--<add name="EventLog"
type="System.Diagnostics.EventLogTraceListener"
initializeData="APPLICATION_NAME"/> -->
</sharedListeners>
</system.diagnostics>
<!—to jest fragment dla nas -->
<appSettings>
<add key="Tytul"
value="System obsługi wynajmu sal WSZiM w Sochaczewie"/>
<add key="BazaSQL"
value="DataSource=BOSS-TECRA\JG_SQLSERVER;Initial
Catalog=SaleWSZiM;Integrated Security=True"/>
<add key="Vat" value="0,23"/>
<add key="Kwestor" value="Maria Kowalska"/>
</appSettings>
</configuration>
24
Plik wzorcowy app.config można dodać do naszego rozwiązania pobierając go z
folderu PomocniczePliki, przy czym postępujemy podobnie jak przy dodawaniu innych
obiektów – różnica polega jedynie na tym, że będziemy wywoływać polecenie Existing Item
zamiast New Item.
Po dodaniu pliku do rozwiązania możemy zmodyfikować potrzebny fragment z poziomu
VisualStudio (wystarczy zmiana nazwy serwera SQL oraz nazwy bazy danych na tym
serwerze).
Plik app.config musi być odczytany w momencie startu aplikacji, czyli w momencie
otwierania formularza głównego frmMdiForm. Zadanie to zrealizujemy w procedurze
obsługującej zdarzenie Load tego formularza. Odczytanie pliku konfiguracyjnego wymaga
zaimportowania niżej pokazanych przestrzeni nazw. Pobrane informacje są przypisywane do
odpowiednich zmiennych globalnych (zadeklarowanych w ModulWspolny), Musimy także
dodać odpowiednią referencję do biblioteki System.configuration (we właściwościach
projektu w zakładce References).
Imports System
Imports System.Configuration
Imports System.Configuration.ConfigurationSettings
Public Class frmMdiForm
Private Sub frmMDI_Load(ByVal sender As Object, _
ByVal e As System.EventArgs) Handles Me.Load
Try
strTytul =ConfigurationManager.AppSettings("Tytul")
strBaza = ConfigurationManager.AppSettings("BazaSQL")
StawkaVAT = ConfigurationManager.AppSettings("Vat")
Kwestor = ConfigurationManager.AppSettings("Kwestor")
bSqlOK = True
Catch ex As Exception
MsgBox("Mam problem z odczytaniem pliku konfiguracyjnego", _
MsgBoxStyle.Critical, "Uruchomienie formularza MDI")
End Try
Me.Text = strTytul
End Sub
' dalsze instrukcje w tej klasie
Rnd Class
25
3.5 Potrzebne klasy zewnętrzne
W tworzonej aplikacji możemy wykorzystać kilka interesujących klas, które poznaliśmy
w semestrze pierwszym. Klasy te możemy włączyć do naszego rozwiązania w postaci
odpowiedniego pliku, inna możliwość to utworzenie biblioteki DLL (ang. Dynamic link library)
i wskazanie we właściwościach projektu lokalizacji takiej biblioteki. Dla treningu zastosujemy
pierwsze rozwiązanie do klasy CForSorageSub, a drugie do dwóch pozostałych klas.
3.5.1
CForStorageSub
W folderze przeznaczonym dla przechowywania plików demonstracyjnych tego projektu
znajduje się plik CForStorageSub.vb, który zawiera pełny kod tej klasy.
Zaimportujemy teraz ten plik do naszego projektu (dokładniej: rozwiązania). Robimy to
analogicznie jak w przypadku pliku konfiguracyjnego:
1) Klik prawym przyciskiem myszy na nazwę rozwiązania w oknie Solution
Explerer;
2) Z menu kontekstowego wybieramy polecenie Add i dalej Existing Item;
3) W oknie Add Exiting Item wskazujemy potrzebny plik
(wcześniej otwierając stosowny folder);
4) Klik przycisku Add importuje wskazany plik i włącza
go do naszego rozwiązania.
Widok okna Solution Explorer po zaimportowaniu tego pliku
pokazany jest obok.
26
3.5.2
CValidacja
W przypadku klasy CValidacja postąpimy inaczej, skorzystamy z faktu, że
dysponujemy wersją DLL tej klasy, a więc wskażemy we właściwościach rozwiązania
referencję do tej biblioteki.
Wykonujemy kolejne kroki:
1) Otwieramy okno właściwości rozwiązania (klik odpowiedniej ikony w oknie
Solution Explorer lub z menu kontekstowego lub z menu głównego);
2) Przechodzimy do zakładki Referenses;
3) Poprzez klik przycisku Add otwieramy okno dodawania referencji;
4) W oknie Add Reference wskazujemy potrzebny plik biblioteki DLL;
5) Klik przycisku OK dodaje referencję (adres) do wskazanego pliku biblioteki
(tym samym kompilator VisualStudio będzie wiedział, gdzie szukać tego pliku).
Po dołączeniu referencji do biblioteki CValidacja.dll w oknie referencji
tworzonego rozwiązania znajdziemy stosowny wpis.
27
3.5.3
CDataGridPrint
28
3.6 Formularz frmNowaSala
Zaprojektujemy teraz formularz przeznaczony do zarejestrowania nowej sali dydaktycznej. Zaczynamy od dodania do naszego rozwiązania nowego obiektu typu Windows Form,
któremu nadajemy nazwę frmNowaSala.
Na powierzchni formularza umieścimy następujące obiekty (formanty):
TextBox o nazwie txtNumer – tu będziemy oczekiwać numeru sali;
CheckBox o nazwie chbProjektor – pozwoli na określenie czy w sali jest
projektor;
ComboBox o nazwie cboTypSali – będziemy w nim wybierać typ sali;
DataGridView o nazwie dgvZdjecia – w tym formancie będzie można wpisać (w
taki lub inny sposób nazwy plików zdjęć danej sali, jeżeli takowe będą);
Button o nazwie btnZapisz – przycisk uruchamiający procedurę zapisu danych;
Button o nazwie btnBrowse – przycisk uruchamiający okno typu OpenFileDialog, w
którym użytkownik będzie mógł wskazać pliki zdjęć danej sali;
Label z domyślną nazwą – formanty opisujące pole tekstowe, pole kombi i datagrid.
Dodatkowo do projektu formularza dodajemy formant typu ErrorProvider, który zostaje
umieszczony na tzw. „tacy”. Nazwę tego formantu można pozostawić bez zmiany (domyślną).
Poniżej widok projektu tego formularza, jego właściwość FormBorderStyle została
zmieniona na FixedToolWindow, co nie pozwoli na zmianę jego rozmiaru.
29
W kodzie klasy tego formularza musimy utworzyć szereg procedur obsługujących jego
funkcjonowanie, będą to oczywiście procedury zdarzeniowe.
Kod zaczyna się od zaimportowania przestrzeni nazw CValidacjaJG, jest to
przestrzeń nazw wykorzystywana przez bibliotekę DLL, którą wykorzystamy do walidacji
wprowadzonych danych.
Imports CValidacjaJG
Public Class frmNowaSala
' pole tekstowe txtNumer powinno umożliwić wprowadzenie tylko
' znaków cyfr (0 do 9), klawisza Back oraz Delete
' Procedura obsługująca zdarzenie KeyPress realizuje to zadanie
Private Sub txtNumer_KeyPress(ByVal sender As Object, ByVal e As _
System.Windows.Forms.KeyPressEventArgs) _
Handles txtNumer.KeyPress
If e.KeyChar >= "0" AndAlso e.KeyChar <= "9" Then ' cyfry OK
e.Handled = False
ElseIf Asc(e.KeyChar) = Keys.Back Or _
Asc(e.KeyChar) = Keys.Delete Then ' delete i backspace ok
e.Handled = False
Else' reszta nie jest OK
Beep()
e.Handled = True
End If
End Sub
Warto zauważyć, że procedura ta może nam się przydać także w innych przypadkach, a
więc warto ją utworzyć w module wspólnym, wtedy będzie ją można wykorzystywać
wielokrotnie. Poniżej kod tej procedury (w module wspólnym).
Public Function ObslugaKeyPress(ByVal znak As Char) As Boolean
If znak >= "0" AndAlso znak <= "9" Then ' cyfry OK
Return False
ElseIf Asc(znak) = Keys.Back Or Asc(znak) = Keys.Delete Then
'delete i backspace ok
Return False
Else' reszta nie OK
Beep()
Return True
End If
End Function
Mając napisaną tę procedurę (funkcję) procedura obsługi zdarzenia KeyPress pola
tekstowego txtNumer jest bardzo prosta.
30
Private Sub txtNumer_KeyPress(ByVal sender As Object, ByVal e As _
System.Windows.Forms.KeyPressEventArgs) _
Handles txtNumer.KeyPress
e.Handled = ObslugaKeyPress(e.KeyChar)
End Sub
Procedura obsługująca zdarzenia Load formularza musi utworzyć źródło danych dla
formantu dgvZdjecia.
Private Sub frmNowaSala_Load(ByVal sender As Object, _
ByVal e As System.EventArgs) Handles Me.Load
' w formularzu jest formant dgvZdjecia, nie jest podpięty
' pod żadne źródło danych. Jeżeli chcemy mieć możliwość dodawania
' nazw plików zdjęć sal, to musimy utworzyć obiekt DataTable
Dim dt As New DataTable
' a następnie dodać do niego nową kolumnę typu tekstowego
dt.Columns.Add(New DataColumn("Zdjecie", GetType(String)))
' można teraz przypisać obiekt dt jako źródło gridu
Me.dgvZdjecia.DataSource = dt
End Sub
Procedura obsługująca zdarzenie Click przycisku btnBrowse tworzy i wyświetla okna
dialogowe typu OpenFileDialog oczekując na wskazanie plików zdjęć danej sali.
Private Sub btnBrowse_Click(ByVal sender As System.Object, _
ByVal e As System.EventArgs) Handles btnBrowse.Click
Dim t() As String
' naszym zamiarem jest dopisanie wiersza lub wierszy do gridu
' deklarujemy obiekt dt jako DataTable i przypisujemy mu źródło
' danych gridu
Dim dt As DataTable = Me.dgvZdjecia.DataSource
' deklarujemy i tworzymt egzemplarz klasy OpenFileDialog
Dim ofd As New OpenFileDialog
' konstrukcja wiążąca z uwagi na konieczność zdefiniowania kilku
' właśności tego obiektu
With ofd
.Title = "Proszę wskazać plik zdjęcia (jpg, gif lub bmp)"
.InitialDirectory = "E:\"
.Multiselect = True
.Filter = "Obrazy JPEG|*.jpg|Obrazy GIF|*.Gif|Obrazy
Bmp|*.bmp|Wszystkie pliki|*.*"
End With
' otwieramy okno i badamy, czy użytkownik wybrał jeden lub
' więcej plików
31
If ofd.ShowDialog = Windows.Forms.DialogResult.OK Then
' wybrał, przeglądamy kolekcję nazw plików (pełna ścieżka)
For Each strFile As String In ofd.FileNames
' metoda Split dzieli ścieżkę na fragmenty opisujące
' dysk i foldery
t = strFile.Split("\")
' ostatni element to potrzebna nazwa pliku
' dodajemy go do obiekty dt korzystając z metody Add dla
' Rows. Wstawiamy ostatni element tablicy t, jego indeks
' to UBound(t)
dt.Rows.Add(t(UBound(t)))
Next
' odświeżamy obiekt dgv
Me.dgvZdjecia.Refresh()
End If
End Sub
Musimy jeszcze napisać procedurę obsługującą zdarzenie Click przycisku btnZapisz, ale
wcześniej musimy utworzyć klasę, która będzie odpowiadała za wykonanie tego polecenia i
kliku innych jeszcze związanych z obsługą sal.
3.6.1
Klasa CAdministracja
Będzie to klasa potomna dziedzicząca po klasie CForStorageSub. Utworzymy w
niej interfejs o nazwie ISale, będzie on zawierał funkcje i metody związane z operacjami
związanymi z salami dydaktycznymi (dodanie nowej sali, edycja, usunięcie).
Zaczynamy od dodania do rozwiązania nowego obiektu typu Class, któremu nadajemu
nazwę CAdministracja. W kodzie tej klasy wpisujemy najpierw instrukcję importu
potrzebnych przestrzeni nazw.
Imports System
Imports System.Data
Imports System.Data.SqlClient
Po instrukcjach importu deklarujemy interfejs ISale podając nazwy procedur i ich
parametry.
Public Interface ISale
Function Komunikat() As String
Sub PrzygotujNowaSala(ByVal strConn As String, _
ByVal frm As frmNowaSala)
Sub ZapiszSale(ByVal strConn As String, ByVal frm As frmNowaSala)
End Interface
32
Właściwy kod klasy zaczynamy od instrukcji dziedziczenia po klasie bazowej, dalej
będzie instrukcja o implementacji interfejsu ISale oraz deklaracje zmiennych prywatnych
klasy.
Public Class CAdministracja
Inherits CForStorageSub
Implements ISale
Private mKomunikat As String = ""
Kolejno tworzymy instrukcje opisujące sposób działania funkcji i procedur interfejsu
ISale.
Funkcja Komunikat zwraca zmienną prywatną klasy mKomunikat, zgodnie z naszą
koncepcją będzie to komunikat precyzujący ewentualny błąd czasu wykonania.
Public Function Komunikat() As String Implements ISale.Komunikat
Return mKomunikat
End Function
Procedura (metoda) PrzygotujNowaSala odpowiada za pobranie z bazy danych
dwóch pól z tabeli RodzajSal, które wykorzystamy jako źródło danych pola kombi
cboTypSali. Pobranie będzie wykonane poprzez wywołanie procedury przechowywanej o
nazwie pDajTypSali.
Public Sub PrzygotujNowaSala(ByVal strConn As String, _
ByVal frm As frmNowaSala) _
Implements ISale.PrzygotujNowaSala
Dim conn As New SqlConnection(strConn)
Dim dt As DataTable
Try
conn.Open()
' pobranie z bazy rekordestu, który będzie źródłem danych
' dla pola kombo cboTypSali, procedura SQL nie ma parametrów
dt = MyBase.DajRekordset(conn, "dbo.pDajTypSali", "brak")
' zmienna bFlaga zostaje ustawiona na False, dzięki temu
' w trakcie przypisywania właściwości DisplayMemeber oraz
' ValueMember
' będziemy mogli szybko opuścić procedurę obsługi zdarzenia
' SelectedIndexChange w frmNowaSala dla cboTypSali
bFlaga = False
' stosujemy strukturę wiążącą
With frm.cboTypSali
.DataSource = dt
.DisplayMember = "rodzaj"
33
.ValueMember = "idtyp"
.SelectedIndex = -1
End With
' przypisujemy do bFlaga=True dla aktywacji procedury
' SelectedIndexChange
Catch ex As Exception
mKomunikat = "Problem z pobraniem danych dla typów sal"
Finally
' sprzątamy po sobie
conn.Close()
conn = Nothing
End Try
End Sub
Procedura (metoda) ZapiszSale odpowiada za zrealizowanie zapisu wprowadzonych
informacji do tabeli Sale, a jeżeli użytkownik podał nazwę zdjęcia (lub nazwy zdjęć), to
zostaną one zapisane do tabeli ZdjeciaSal.
Public Sub ZapiszSale(ByVal strConn As String, _
ByVal frm As frmNowaSala) Implements ISale.ZapiszSale
Dim conn As New SqlConnection(strConn), i, j As Integer
Try
conn.Open()
' zapisujemy dane do tabeli Sale, ale tylko wtedy, gdy
' numner sali jest unikalny
i = MyBase.DajWartosc(conn, "dbo.pWstawSale", _
"@idnr", frm.txtNumer.Text, jgTyp.jgInteger, 0, _
"@projektor", IIf(frm.chbProjektor.Checked, 1, 0), _
jgTyp.jgInteger, 0, _
"@idtyp", frm.cboTypSali.SelectedValue, _
jgTyp.jgInteger, 0, _
"@TakNie", jgTyp.jgInteger, 0)
If i = 0 Then
' sala zarejestrowana, można wstawić zdjęcia, jeżeli są
If frm.dgvZdjecia.Rows.Count > 1 Then
' są, zapisujemy
MyBase.WstawWieleRekordow(conn, "dbo.pWstawZdjecie", _
frm.dgvZdjecia, _
"@idnr", jgTyp.jgInteger, 0, True, frm.txtNumer.Text, _
"@foto", jgTyp.jgString, 30, False, 0)
End If
End If
Catch ex As Exception
34
mKomunikat = _
"Problem z zapisaniem danych do tabel Sale i ZdjeciaSal"
Finally
conn.Close()
conn = Nothing
If i = 1 And mKomunikat.Length = 0 Then
mKomunikat = "Sala o numerrze " & frm.txtNumer.Text & _
" juz jest w bazie, odmowa zapisu"
End If
End Try
End Sub
End Class
3.6.2
Uzupełnienie kodu klasy frmNowaSala
Możemy już napisać procedurę obsługującą klik przycisku btnZapisz. Skorzystamy
w tym kodzie z dwóch klas: CValidacja i CAdministracja.
Private Sub btnZapisz_Click(ByVal sender As System.Object, _
ByVal e As System.EventArgs) Handles btnZapisz.Click
' polecenie zapisu ma być wykonane wtedy, gdy użytkownik poda
' poprawne informacje. Do ich zweryfikowania wykorzystamy metody
' udostępnione w bibliotece CValidacja
' deklarujemy i tworzymy egzemplarz tej klasy
Dim w As New CValidacja
' badamy, czy pole txtNumer jest liczbą z podanego zakresu
' numeracji sal
If Not w.CzyTextBoxToLiczba(True, Me.txtNumer, _
Me.ErrorProvider1, "Numer sali musi być podany!", minNrSali, _
maxNrSali, True) Then Exit Sub
If Not w.CzyWybranoPozycjeCombo(True, Me.cboTypSali, _
Me.ErrorProvider1, "Proszę wybrać typ sali!", True) _
Then Exit Sub
' można zapisywać
w = Nothing
' deklarujemy obiekt klasy CAdministracja wykorzystując interfejs
' ISale (co ograniczy dostępność metod do tego interfejsu)
Dim z As ISale
' tworzymy egzemplarz klasy CAdministracja
z = New CAdministracja
' wywołujemy metodę ZapiszSale
z.ZapiszSale(strBaza, Me)
' sprawdzamy, czy zapis zakończył się sukcesem?
If z.Komunikat.Length > 0 Then
35
MsgBox(z.Komunikat, MsgBoxStyle.Critical, Me.Text)
Else
MsgBox("Nowa sala zapisana w bazie", MsgBoxStyle.Information, _
Me.Text)
Me.Close()
End If
z = Nothing
End Sub
3.6.3
Uzupełnienie kodu formularza głównego
Musimy jeszcze zmodyfikować instrukcje uruchamiające formularz frmNowaSala,
chodzi o to, że ten formularz ma prawo być pokazany tylko wtedy, gdy wszystkie jego
formanty będą gotowe do pracy. W przypadku tego formularza musimy przed jego pokazaniem
dostarczyć dane źródłowe do formantu kombo o nazwie cboTypSal.
Zrobimy to poprzez utworzenie obiektu klasy CAdministracja i wywołanie metody
PrzygotujNowaSala.
Pokazana niżej procedura korzysta z publicznej procedury PokazForm, która została
utworzona w module ogólnym (z uwagi na to, że będziemy wielokrotnie z niej korzystać).
Private Sub mnuNowaSala_Click(ByVal sender As System.Object, _
ByVal e As System.EventArgs) Handles mnuNowaSala.Click
Dim frm As New frmNowaSala
Dim w As ISale
w = New CAdministracja
w.PrzygotujNowaSala(strBaza, frm)
PokazForm(w.Komunikat, frm, Me, _
"Błąd otwarcia formularza frmNowaSala", _
"Formularz dodania nowej sali")
End Sub
Wspomniana procedura PokazForm otrzymuje jako argumenty te informacje, które
pozwalają jej na wyświetlenie lub nie danego formularza.
Public Sub PokazForm(ByVal strKomunikat As String, _
ByRef frm As Form, ByRef frmParent As Form, _
ByVal strOpisOknaKomunikatu As String, _
Optional ByVal strTytulFormy As String = Nothing)
If strKomunikat.Length > 0 Then
MsgBox(strKomunikat, MsgBoxStyle.Critical, _
strOpisOknaKomunikatu)
Else
If Not IsNothing(strTytulFormy) Then
36
frm.Text = strTytulFormy
End If
frm.MdiParent = frmParent
frm.Show()
End If
End Sub
3.6.4
Formularz frmNowaSala w „pracy”
Na zakończenie kilka zrzutów ekranowych pokazujących działanie tego formularza.
37
I zapis w bazie, tabela Sale (ostatnia pozycja w tej tabeli) oraz w ZdjeciaSal (dwa
ostatnie rekordy).
38