296
PROŠNJA: Vse uporabnike te literature vljudno prosim, da mi na moj elektronski naslov [email protected] sporočijo kakršnekoli napake, ki jih najdejo bodisi v tekstu, bodisi v kodi! Hvaležen bom tudi vsem, ki mi boste sporočili, da to literaturo uporabljate, mi poslali svoje pripombe in graje! HVALA, Srečo Uranič MICROSOFT VISUAL C# .NET Srečo Uranič

PROŠNJA - uranic.tsckr.siuranic.tsckr.si/VISUAL C#/VISUAL C#.pdf · MICROSOFT VISUAL C# .NET Srečo Uranič . Visual C# . ... Za začetek novega projekta v okolju Visual C# Standard

  • Upload
    buikhue

  • View
    287

  • Download
    0

Embed Size (px)

Citation preview

Page 1: PROŠNJA - uranic.tsckr.siuranic.tsckr.si/VISUAL C#/VISUAL C#.pdf · MICROSOFT VISUAL C# .NET Srečo Uranič . Visual C# . ... Za začetek novega projekta v okolju Visual C# Standard

PROŠNJA: Vse uporabnike te literature vljudno prosim, da mi na moj elektronski naslov [email protected] sporočijo kakršnekoli napake, ki jih najdejo bodisi v tekstu, bodisi v kodi! Hvaležen bom tudi vsem, ki mi boste sporočili, da to literaturo uporabljate, mi poslali svoje pripombe in graje! HVALA, Srečo Uranič

MICROSOFT

VISUAL

C# .NET

Srečo Uranič

Page 2: PROŠNJA - uranic.tsckr.siuranic.tsckr.si/VISUAL C#/VISUAL C#.pdf · MICROSOFT VISUAL C# .NET Srečo Uranič . Visual C# . ... Za začetek novega projekta v okolju Visual C# Standard

Visual C# . NET

Algoritmi in programski jeziki – 4.letnik

2

Kaj je C# in .NET Framework C# je Microsoftov objektno orientiran programski jezik. Jezik je nekakšna kombinacija programske moči jezika C++ z dodanimi dobrotami iz Visual Basica in Jave. Čeprav C# temelji na C++, pa vsebuje številne sestavine značilne za Javo. Razvijalcem Visual C# omogoča razvoj visoko prenosljivih aplikacij. C# je bil oblikovan za delo z Microsoftovo .NET platformo (.NET Framework). .NET Framework je platforma/knjižnica, ki predstavlja ogrodje za vse .NET orientirana programska orodja in aplikacije za osebne računalnike, dlančnike, pametne telefone, razne vgrajene sisteme,…) Vsebuje pester nabor jezikov (C++, C#, Visual Basic, VBScript, J#, JScript…), omogoča hiter in predvsem lažji razvoj kot tudi izvajanje tako obsežnih in zahtevnih, kot tudi majhnih in enostavnih projektov. Omogoča tudi kreiranje projektov, sestavljenih iz posameznih modulov, zgrajenih z različnimi programskimi jeziki. Obenem omogoča kar najbolj možno prenosljivost programske kode. Vsebuje tudi kar se da optimizirano vgrajeno kodo, standardizirano z najnovejšo tehnologijo kot npr. XML in SOAP. Razvojno okolje VISUAL Studio nastopa v več različicah, odvisno od namena uporabe: najbolj znane so različice Express Edition, Standard Edition in Professonal Edition. Verzija Express Edition je brezplačna, naložimo pa si jo lahko kar preko spleta. Razlaga in vaje v tej literaturi bodo temeljili večinoma na različici Express Edition, nekatere pa tudi na Standard Edition. Zgradba .NET

Common Language Runtime

Base Class Library

ADO.NET and XML

ASP.NET

Web Forms Web Services Mobile Internet Toolkit

Windows Forms

Common Language Specification

VB C++ C# J# …

Page 3: PROŠNJA - uranic.tsckr.siuranic.tsckr.si/VISUAL C#/VISUAL C#.pdf · MICROSOFT VISUAL C# .NET Srečo Uranič . Visual C# . ... Za začetek novega projekta v okolju Visual C# Standard

Visual C# . NET

Algoritmi in programski jeziki – 4.letnik

3

Izdelava konzolnih aplikacij v VISUAL C# . NET Za boljše spoznavanje okolja Visual C# . NET bomo na začetku izdelali nekaj enostavnih konzolnih aplikacij. Več o delu s konzolnimi aplikacijami in o osnovah programskega jezika C# nasploh, pa je zapisanega v literaruri z naslovom Microsoft C#.&ET (avtor Srečko Uranič). Pri kreiranju novega projekta je majhna razlika glede na to, katero različico razvojnega okolja uporabljamo. Kasneje razlik skoraj ni, še posebej ne pri razvoju konzolnih aplikacij.

Začenjanje novega projekta v različici Express Edition Za začetek novega projekta v okolju Visual C# odprimo meni File -> &ew Project…

Odpre se novo okno &ew Project v katerem pod Templates (Vzorci) izberemo ikono Console Application. Na dnu okna napišimo še ime naše prve konzolne aplikacije, npr. Prvi Program. S klikom na gumb OK se ustvari nov projekt.

Začenjanje novega projekta v različici Standard Edition Za začetek novega projekta v okolju Visual C# Standard Edtion odprimo meni File -> &ew Project…

Odpre se novo okno &ew Project v katerem pod Templates (Vzorci) izberemo ikono Console Application. Na dnu okna napišimo še ime naše prve konzolne aplikacije (&ame), npr. Moj Prvi, določimo mapo v kateri bo shranjen naš projekt (Location) in še zapišemo ime naše rešitve ( Solution &ame - priporočljivo je, da sta vsaj v začetnih projektih imeni programa in rešitve enaka).

Page 4: PROŠNJA - uranic.tsckr.siuranic.tsckr.si/VISUAL C#/VISUAL C#.pdf · MICROSOFT VISUAL C# .NET Srečo Uranič . Visual C# . ... Za začetek novega projekta v okolju Visual C# Standard

Visual C# . NET

Algoritmi in programski jeziki – 4.letnik

4

S klikom na gumb OK se ustvari nov projekt.

Pisanje programske kode z uporabo IntelliSense tehnologije Ko smo ustvarili nov projekt, se odprejo osnovna okna za urejanje naše prve konzolne aplikacije. Toolbox (objekti za obrazce) Editor (urejevalnik - okno za pisanje/urejanje kode)

Solution Explorer (raziskovalec rešitve) Properties (lastnosti objektov)

Error list (seznam napak), ali pa Task list (seznam opravil, potrebnih za dokončanje projekta) Kratek opis osnovnih oken razvojnega okolja

• Solution Explorer (raziskovalec rešitve): V obliki drevesne strukture nam prikazuje in omogoča dostop do vseh datotek znotraj našega projekta. Datoteke lahko dodajamo, jih spreminjamo in brišemo. Okno je dostopno tudi preko tipkovnice, s kombinacijo tipk Ctrl+Alt+L. Na dnu tega okna sta dva jezička: Solution Explorer in ClassView. Okno Class View prikazuje hierarhijo razredov znotraj rešitve. Dostopno je tudi preko tipkovnice, s kombinacijo tipk Ctrl+Alt+L. Okno je bolj uporabno kot Solution Explorer predvsem pri velikih projektih.

• Toolbox (objekti za obrazce): Vsebuje objekte (kontrole) za izdelavo Windows Forms (obrazci) ali Web Forms aplikacije. Pri izdelave konzolne aplikacije je seveda okno prazno, saj v tem primeru ne delamo z že pripravljenimi objekti. Okno je dostopno tudi preko tipkovnice, s kombinacijo tipk Ctrl+Alt+X.

• Properties (lastnosti objektov): V tem oknu spreminjamo in nastavljamo lastnosti in dogodke objektom pri izdelavi Windows Forms ali Web Forms aplikacije. Pri izdelave konzolne aplikacije je seveda okno prazno, saj v tem primeru ne delamo z že pripravljenimi objekti. Okno je dostopno tudi preko tipkovnice, s pomočjo tipke F4.

• Error list (seznam napak), ali pa Task list (seznam potrebnih opravil, ki so potrebna za dokončanje projekta). Vrsta prikazanega okna je odvisna od izbire v meniju View (lahko je Error List, TaskList, Output, …).

• Editor (urejevalnik -okno za pisanje/urejanje kode): Okno je namenjeno za pisanje ter urejanje programske kode. Omogoča tudi skrivanje in prikazovanje delov kode (outlining), ki smiselno tvorijo neko celoto (+ in – na levi strani urejevalnika). V primeru da je del kode skrit (da levi strani znak -)

Page 5: PROŠNJA - uranic.tsckr.siuranic.tsckr.si/VISUAL C#/VISUAL C#.pdf · MICROSOFT VISUAL C# .NET Srečo Uranič . Visual C# . ... Za začetek novega projekta v okolju Visual C# Standard

Visual C# . NET

Algoritmi in programski jeziki – 4.letnik

5

lahko (ne da bi skriti del odprli s klikom na znak -) vidimo vsebino tudi tako, da se z miško premaknemo čez povzetek skritega dela besedila na desni strani vrstice.

skupen nabor funkcij, ki so

implementirane v orodju

&amespace – imenski prostor v katerega je »zapakiran« naš prvi razred

Začetek nekega razreda (oz. programa).

Blok kode ki predstavlja metodo/funkcijo Main (»glavni program«), z argumentoma “string[] args”.

� Opomba: C# je case sensitive jezik, zato mora biti metoda Main napisana z veliko začetnico,

sicer bo prevajalnik javil napako. V Visual C# lahko v fazi načrtovanja (razvoja) programa ustvarjamo t.i. regije (začetek regije #region konec #endregion). Del kode, zapisane med ti dve besedici lahko kadarkoli skrijemo in zopet prikažemo, s tem pa pridobimo na preglednosti izvornega programa. Ko smo projekt ustvarili, nam je C# ustvaril datoteko Program.cs, ki definira razred imenovan Program, le-ta pa vsebuje metodo imenovano Main. Vse metode našega programa morajo biti definirane znotraj razreda. Metoda Main je posebna, ker je vstopna točka programa, zato mora biti statična. Programsko kodo naše prve konzolne aplikacije bomo napisali v metodo Main. Programska koda za naš program izgleda takole: static void Main(string[] args) { Console.WriteLine("Pozdravljen svet!!"); }

Console je razred, ki je definiran v imenskem prostoru (knjižnici) System. Ker smo to knjižnico navedli na začetku naše prve konzolne aplikacije (stavek using System), se lahko na ta razred sklicujemo samo z imenom. V primeru, da pa na začetku programa ne bi bilo vrstice using System, bi do razreda prišli preko knjižnice System ( torej System.Console…). Razred Console vsebuje metode, ki lahko berejo posamezne znake ali pa zaporedja znakov (stavke oz. vrstice) preko konzole (najpogosteje tipkovnice). Vsebuje tudi metode za pretvarjanje podatkov (npr. števila v zaporedja znakov-stringe in obratno), metode za formatiranje spremenljivk, ter za formatiran izpis npr. na ekran ali pa v datoteko. Ko za besedo Console napišemo piko, se nam odpre t.i. IntelliSense meni (kontekstno občutljiva pomoč). Le-ta nam prikaže imena vseh članov in metod (atributov) razreda Console (v splošnem pa imena razreda, ki smo ga napisali pred znakom pika). Na levi strani vsakega atributa je ikona, ki označuje tip določenega atributa.

Ikona Pomen

method – metoda

property – lastnost

class – razred

using System; using System.Collections.Generic; using System.Text; namespace PrviProgram { class Program { static void Main(string[] args) { } } }

Page 6: PROŠNJA - uranic.tsckr.siuranic.tsckr.si/VISUAL C#/VISUAL C#.pdf · MICROSOFT VISUAL C# .NET Srečo Uranič . Visual C# . ... Za začetek novega projekta v okolju Visual C# Standard

Visual C# . NET

Algoritmi in programski jeziki – 4.letnik

6

struct – struktura

enum - naštevanje

interface - vmesnik

namespace – imenski prostor

event - dogodek

delagate - delegat

V IntelliSense meniju poiščimo metodo WriteLine in pritisnimo <Enter> ali dvakrat kliknimo z miško na metodo - metoda je s tem dodana programski kodi. Za tem napišimo oklepaj in zaklepaj, znotraj oklepaja pa v narekovajih napišimo besedilo, ki nam ga bo program izpisal na zaslon - v našem primeru ''Pozdravljen svet!'' . Za oklepajem dodajmo še podpičje, saj s tem zaključimo ta ukaz (dvopičje je znak za konec stavka).

� Opomba: Kadar je na vrhu okna (zavihka oz. okna urejevalnika) v katerem je naša koda znak zvezdica (*), to pomeni, da je bila v naši datoteki narejena vsaj ena sprememba, potem, ko smo jo nazadnje shranili. Ročno shranjevanje ni potrebno, saj se shranjevanje izvede

avtomatsko takoj ko poženemo ukaz za prevajanje. Seveda pa je ročno shranjevanje priporočljivo takrat, kadar smo zapisali veliko vrstic kode, pa prevajanja še ne želimo pognati.

V splošnem nam IntelliSense nudi več vrst pomoči

• List Members : kombinacija tipk Ctrl-J nam prikaže seznam vseh članov (members) nekega atributa, nad katerim se nahaja kazalnik miške.

• Parameter info (informacije o parametrih neke metode): z miško kliknimo med oklepaja poljubne metode, in uporabimo kombinacijo tipk Ctrl-Shift-Space. V okvirju (seveda če le ta za to metodo sploh obstaja) se prikažejo vse možne

variante izbrane metode. V levem zgornjem kotu imamo podatek o tem, koliko različnih vrst te metode obstaja (preobložene metode!!!). S klikom na trikotnika se lahko sprehodimo po teh metodah, obenem pa so prikazani podatki o številu in tipu parametrov, v spodnjem delu okna pa je še razlaga metode.

• Quick Info (hitra pomoč): S kombinacijo tipk Ctrl-I se nam namreč ob kazalniku miške prikaže sličica daljnogleda; če sedaj miško zapeljemo čez poljuben identifikator v urejevalniškem oknu, se v majhnem okencu prikazujejo osnovne informacije o tem identifikatorju.

• Complete Word (dokončaj besedo): S kombinacijo tipk Ctrl-Space dosežemo, da se neka beseda, ki jo pišemo, avtomatsko izpiše do konca, v primeru, da pa je možnih besed več, pa nam C# v okvirčku, v obliki PopUp menija, ponudi seznam vseh možnih besed (to je seznam vseh možnih metod, dogodkov, lastnosti, …). Zraven vsakega od njih je ustrezna ikona, ki označuje za kakšno besedo gre (ali je to metoda, lastnost, dogodek,…). Pomen ikon je pojasnjen v posebni tabeli na prejšnji strani.

Prevajanje in zagon projekta Naš prvi projekt je sedaj pripravljen za prevajanje. Na voljo imamo več možnosti

• Če želimo naš projekt le prevesti, ne pa tudi zagnati, potem v meniju Build kliknemo opcijo Build Solution. V primeru, da smo naredilo kako napako, nam C# v oknu ErrorList napiše vse potrebne informacije o številu in vrsti napak, pa tudi natančno informacijo o vrstici in stolpcu kjer je napaka;

• Prevajanje in obenem zagon projekta poženemo iz menija Debug, opcija Start Debugging (tipka

F5) ali pa Start Without Debugging (Ctrl-F5). Razlika med načinoma je v tem, da se bo v

Page 7: PROŠNJA - uranic.tsckr.siuranic.tsckr.si/VISUAL C#/VISUAL C#.pdf · MICROSOFT VISUAL C# .NET Srečo Uranič . Visual C# . ... Za začetek novega projekta v okolju Visual C# Standard

Visual C# . NET

Algoritmi in programski jeziki – 4.letnik

7

drugem primeru okno, v katerem se bo izvedla naša aplikacija takoj zaprlo in si eventuelnih rezultatov(v našem primeri izpisa na zaslonu) ne bomo mogli ogledati;

• Kliknimo gumb Start Debugging v orodjarni

• O ostalih opcijah (Rebuild Solution, Step info, Step Over) bo več zapisanega v nadaljevanju.

Naš program poženemo torej z opcijo Ctrl-F5 (oziroma v meniju Debug kliknimo Start Without Debugging). Če je program brez napak, se odpre novo okno, v katerem steče naš program in se v njem prikažejo rezultati našega programa.

Prepričajmo se, da je prikazano okno aktivno (oz. da ima focus) in pritisnimo poljubno tipko. Okno se zapre in vrnemo se v okolje Visual C#. Naš projekt se je ob prvem prevajanju tudi v celoti shranil, vendar POZOR! Običajno želimo projekt shraniti v točno določeno mapo, zaradi česar običajno projekt pred prvim zagonom shranimo tja, kamor želimo sami in ne tja kamor so nastavljene privzete nastavitve. To storimo tako, da pred prvim prevajanjem v meniju File kliknemo na ikono Save All (ali pa kar na ikono Save All v orodjarni). Če uporabljamo različico Express Edition, se prikaže okno za nastavitev shranjevanja, v katerem nastavimo (zapišemo) vse potrebne podatke o tem, kam in pod kakšnim imenom želimo naš projekt shraniti:

Ime našega programa lahko sedaj poljubno spremenimo (vrstica &ame), izberemo mapo v kateri želimo imeti naš projekt (Location). Če mapa še ne obstaja, jo lahko ob kliku na gumb Browse skreiramo. V vrstici Solution &ame imamo možnost, da se ime programa razlikuje od same rešitve, a priporočljivo je, da sta ime programa in konkretna rešitev poimenovana enako. Dodana je še možnost kreiranja novega imenika za naš program (znotraj izbranega imenika/mape v vrstici Location). Priporočljivo pa je, da za vsak nov projekt okolje Visual C# samo kreira svoj imenik oz. mapo, zato da imamo vse potrebne datoteke, ki tvorijo projekt, v svoji mapi. Pri shranjevanju v različici Standard zgornjega okna ni, saj smo vse potrebne nastavitve naredili že pri odpiranju projekta. Preglejmo še katere datoteke so nastale ob kreiranju naše prve konzolne aplikacije. V Solution Explorerju kliknimo ikono Show All Files. Po imenom mape našega projekta (v našem primeru MojPrvi) se prikažejo še mape Properties, bin in obj. Te mape se kreirajo takoj, ko poženemo prevajanje programa in vsebujejo

Page 8: PROŠNJA - uranic.tsckr.siuranic.tsckr.si/VISUAL C#/VISUAL C#.pdf · MICROSOFT VISUAL C# .NET Srečo Uranič . Visual C# . ... Za začetek novega projekta v okolju Visual C# Standard

Visual C# . NET

Algoritmi in programski jeziki – 4.letnik

8

izvršilno (executable) verzijo našega programa, ter še nekaj datotek, ki so potrebne za razhroščevanje (debugging) programa. Če v Solution Explorerju kliknimo znak + pred mapo bin, se prikaže še vsebovana podmapa Debug. Kliknimo še na + pred mapo Debug in prikažeta se datoteki MojPrvi.exe in MojPrvi.pdb.

� Opomba: Vsak izvorni program (source code) lahko prevedemo v izvršilno verzijo (executable code) tudi neposredno iz komandne vrstice (konzole) z uporabo C# csc prevajalnika.

&apake pri prevajanju (Compile Time Error) in napake pri izvajanju (Run Time Error) Pri pisanju kode se seveda lahko zgodi, da naredimo kakšno napako. V svetu programiranja napakam v programu rečemo tudi hrošči (bugs). Na srečo ima okolje Visual C# sposobnost, da nekatere vrste napak odkrije. Poznamo tri vrste napak:

• Napake pri prevajanju (Compile Time Error); • Napake pri izvajanju (Run Time Error); • Logične napake (Logical Error).

Prvi dve vrsti napak prevajalnik oziroma okolje odkrije in nam pomaga pri njihovem odpravljanju. Te vrste napak zaradi tega navadno niso problematične, saj jih zaradi pomoči prevajalnika popravimo sorazmerno enostavno in hitro. Bolj problematične so tretje vrste napak – te napake so pomenske, njihovo odpravljanje pa zaradi tega veliko težje in dolgotrajnejše. Visual C# pa ima tudi za odpravljanje logičnih oziroma pomenskih napak na voljo posebno orodje, ki pa ga bomo spoznali v drugem delu te literature. To je t.i. razhroščevalnik oz. Debugger. Na primeru naše prve konzolne aplikacije si poglejmo prikaz in odpravljanje prvih dveh vrst napak. Recimo, da smo se pri pisanju programske kode zmotili in namesto pravilnega zapisa Console.WriteLine("Pozdravljen svet!!");

zapisali stavek WriteLine z malo začetnico takole: Console.writeLine("Pozdravljen svet!!");

Page 9: PROŠNJA - uranic.tsckr.siuranic.tsckr.si/VISUAL C#/VISUAL C#.pdf · MICROSOFT VISUAL C# .NET Srečo Uranič . Visual C# . ... Za začetek novega projekta v okolju Visual C# Standard

Visual C# . NET

Algoritmi in programski jeziki – 4.letnik

9

Ko poženemo prevajanje s klikom na opcijo Debug -> Start Debugging (tipka F5) ali pa Start Without Debugging (Ctrl-F5), nam Visual C# v oknu Error list (seznam napak) prikaže za kakšno napako gre in kje se le-ta nahaja (oz. kakšne so napake in kje se nahajajo, če jih je več). Taki vrsti napake pravimo napaka pri prevajanju (Compile Time Error), ker se naš program zaradi napake sploh ni mogel prevesti.

Poleg tega nam Visual C# podčrta del kode, kjer se napaka nahaja. Če se z miško premaknemo nad podčrtani del kode, kjer je napaka, nam Visual C# v okvirčku pod to besedo izpiše za kakšno napako gre.

Napako popravimo in sprožimo novo prevajanje. V kolikor napak ni več, se bo naš program prevedel in zagnal, sicer pa v oknu Error List dobimo nov seznam napak. Postopek ponavljamo, dokler ne odpravimo vseh napak. Zgodi pa se, da se naš program prevede, a kljub temu pride do napake pri samem izvajanju programa. Kot primer take napake poglejmo klasično napako pri deljenju z nič. V našo začetno aplikacijo dodajmo še dva stavka takole: int x = 0;//deklaracija in inicializacija celoštevilske spremenljivke x int y = 5 / x;//deklaracija in prirejanje vrednosti celoštevilčne spremenljivke y System.Console.WriteLine("Pozdravljen svet!");

Ko poženemo prevajanje se program sicer prevede in se prične izvajati. V stavku int y = 5 / x pa smo zahtevali deljenje z 0 (ker je pač vrednost spremenljivke x enaka 0), kar pa je strogo prepovedana operacija. Izvajanje programa se zaradi tega ustavi in na ekranu dobimo približno takole sliko z obvestilom o napaki. Taki napaki pravimo napaka med izvajanjem programa (Run Time Error).

Page 10: PROŠNJA - uranic.tsckr.siuranic.tsckr.si/VISUAL C#/VISUAL C#.pdf · MICROSOFT VISUAL C# .NET Srečo Uranič . Visual C# . ... Za začetek novega projekta v okolju Visual C# Standard

Visual C# . NET

Algoritmi in programski jeziki – 4.letnik

10

Program moramo seveda popraviti tako, da se izognemo takim operacijam (npr. z if stavkom v katerem preverimo vrednost delitelja).

Imenski prostori Majhni programčki oz. programi postajajo s časoma vedno večji in pojavita se dva problema. Prvi je ta, da so večji programi težje obvladljivi in razumljivi kot pa manjši programi. Drugi problem pa je v tem, da več kode navadno pomeni več novih imen, več spremenljivk, funkcij, razredov, … S tem, ko se število teh atributov veča, pa slej ko prej naletimo na problem podvajanja imen, kar pa seveda ni dovoljeno. Program ne dela, potrebno je spreminjanje imen, a to opravilo je pri obsežnih programih lahko mukotrpno ali celo neizvedljivo. Problem podvajanja v sodobnih programskih jezikih rešujejo t.i. imenski prostori (namespaces), v katere »zapakiramo« našo kodo. Poskrbeti moramo le za to, da je ime našega imenskega prostora unikatno. Imenski prostor je nabor imen, spremenljivk, razredov, …, ki logično pripadajo neki celoti, v kateri velja pravilo neponovljivosti. Če želimo npr. narediti nov razred z imenom MojPrviPozdrav, je bolje da naredimo razred poimenovan Pozdrav znotraj imenskega prostora MojPrvi takole: namespace MojPrvi { class Pozdrav { ... } }

Na razred Pozdrav se sedaj lahko sklicujemo kot MojPrvi.Pozdrav. V primeru, da bo razred z enakim imenom kreiral še kdo drug v okviru njegovega imenskega prostora, ter ga instaliral na naš računalnik, bo naša aplikacija še vedno delala brez problemov. Priporočilo .NET platforme je, da vse razrede definiramo v imenskih prostorih, .NET razvojno okolje pa to priporočilo nadgradi s tem, da za ime projekta prevzame ime zunanjega imenskega prostora.

� Opomba: Izogibajmo se podvajanju imen imenskih prostorov in razredov. Z drugimi besedami, ne kreirajmo razredov z enakim imenom kot imenski prostor. Imenski prostor lahko vsebuje večje število razredov.

Vsak razred živi v svojem imenskem prostoru. Razred Console npr. živi znotraj imenskega prostora System. To pomeni, da je njegovo polno ime System.Console. Da pa nam polnega imena ni potrebno pisati vsakič znova, lahko ta problem rešimo z napovedjo using System;

Using stavke lahko napišemo na začetku našega programa, ali pa kot prve stavke znotraj našega imenskega prostora. Z uporabo teh stavkov je poimenovanje razredov bistveno krajše, koda pa bolj pregledna. Takole bi izgledal naš prvi program, če using stavka ne bi uporabili (v spodnjem primeru je stavek using System označen kot komentar). //using System; using System.Collections.Generic; using System.Text; namespace MojPrvi { class Program { static void Main(string[] args) { System.Console.WriteLine("Pozdravljen svet!"); }

Page 11: PROŠNJA - uranic.tsckr.siuranic.tsckr.si/VISUAL C#/VISUAL C#.pdf · MICROSOFT VISUAL C# .NET Srečo Uranič . Visual C# . ... Za začetek novega projekta v okolju Visual C# Standard

Visual C# . NET

Algoritmi in programski jeziki – 4.letnik

11

} }

C# nam nudi možnost pisanja lastnih imenskih prostorov – to storimo z uporabo rezervirane besede namespace, Na spremenljivke, objekte ali pa funkcije iz tega imenskega prostora se v tem primeru sklicujemo preko imena tega imenskega prostora in operatorja pika. Primer: namespace MojImenskiProstor //začetek novega imenskega prostora { public class Test { public static int funkcija() { for (int i = 0; i < 10; i++) { Console.WriteLine("i: {0}", i); } return 0; } } } //vstopna točka programa class Program { static void Main(string[] args) { MojImenskiProstor.Test.funkcija();//klic funkcije imenskega prostora MojImenskiProstor } }

Komentarji Komentarji so na poseben način označeni deli besedila, ki niso del programske kode. V komentarje zapisujemo razne opazke ali pa jih uporabljamo za lažje iskanje delov programa in za izboljšanje preglednosti programske kode. Prevajalnik komentarje ne prevaja, tako da ti ne vplivajo na velikost izvršne datoteke. Komentarje v C# označujemo na dva načina

• S paroma znakov /* in */ - večvrstični komentar • Z dvema poševnicama // - enovrstični komentar

//Tole je enovrstični komentar; /* Tole pa je večvrstični komentar! */

Visual C# pa nam ponuja še eno zelo priročno možnost, da nek del teksta, ki je že napisan, označimo kot komentar (in ga s tem npr. začasno izključimo iz prevajanja). To nam omogoča hitri gumb Comment out the selected lines. Tekst, ki ga želimo zakomentirati (označiti kot komentar) najprej označimo z miško, nato pa kliknemo na hitri gumb Comment out the selected lines, ali pa pritisnemo kombinacijo tipk Crtl+E, C.

Gumb Comment out the selected lines

Page 12: PROŠNJA - uranic.tsckr.siuranic.tsckr.si/VISUAL C#/VISUAL C#.pdf · MICROSOFT VISUAL C# .NET Srečo Uranič . Visual C# . ... Za začetek novega projekta v okolju Visual C# Standard

Visual C# . NET

Algoritmi in programski jeziki – 4.letnik

12

Podobno lahko delu teksta, ki je označen kot komentar, oznake za komentar umaknemo. To storimo tako, da ta del teksta najprej označimo z miško, nato pa kliknemo na hitri gumb Uncomment the selected lines, oziroma pritisnemo kombinacijo tipk Crtl+E, U.

Page 13: PROŠNJA - uranic.tsckr.siuranic.tsckr.si/VISUAL C#/VISUAL C#.pdf · MICROSOFT VISUAL C# .NET Srečo Uranič . Visual C# . ... Za začetek novega projekta v okolju Visual C# Standard

Visual C# . NET

Algoritmi in programski jeziki – 4.letnik

13

MICROSOFT Visual C# 2008 - OSNOVNI POJMI: Rešitve (Solutions) in projekti (projects) Največja organizacijska enota v okolju MS Visual Studio .&ET (kratica VS&) in tako tudi v Visual C# je rešitev - Solution. Posamezna rešitev obsega celotno delo, ki se tiče nekega problema ter vsebuje enega ali več projektov - Project.

Projekt vsebuje izvorne datoteke aplikacije, ki jih prevajalnik (compiler) prevede v objektne datoteke vrste .obj, povezovalnik (linker) pa le-te poveže v izvedbeno datoteko aplikacije vrste .exe ali dinamično knjižnico vrste .dll. Oba postopka skupaj se imenujeta gradnja projekta (building).

Vsaka izvedba neke aplikacije zahteva ločen projekt. Tudi če želimo npr. za isto aplikacijo zgraditi izvedbeno datoteko vrste .exe in izvedbeno datoteko vrste dinamične knjižnice .dll, moramo uporabiti za vsako od njiju ločen projekt.

Zapomnimo si:

• Posamezen odprt primerek Visual C# v operacijskem sistemu Windows obsega le eno rešitev.

• Rešitev obsega enega ali več projektov ter morebitne odvisnosti med njimi.

• Posamezen projekt lahko pripada eni ali pa več rešitvam.

• Posamezni projekti v neki rešitvi so lahko tudi v različnih programskih jezikih (takih, ki jih podpira okolje VS&).

Orodje Visual C# ustvari običajno (razen če spremenimo nastavitve) za vsak projekt ločeno mapo na disku. V tej mapi sta tudi podmapi "obj" ter "bin", v katere Visual C# shranjuje objektne ter izvedbene datoteke projekta. Projekt lahko arhiviramo (kot zaščitno kopijo) ali pošljemo drugemu razvijalcu tudi brez teh dveh podmap.

Okno Solution Explorer prikazuje rešitev, projekte in seznam njihovih datotek:

Rešitev - Solution

Projekta znotraj ene rešitve

Visual Studio uporablja dva tipa datotek (.sln in .suo) za shranjevanje nastavitev, ki ustrezajo neki rešitvi. Ti dve datoteki, ki ju s skupnim imenom imenujemo rešitev - solution files, izdela Solution Explorer, opremi pa ju z vsemi informacijami in podatki, ki so potrebni za prikaz grafičnega vmesnika in upravljanje z datotekami našega projekta. Na ta način se razvijalec projekta lahko osredotoči na svoj projekt in na rešitev problema, namesto da bi se ukvarjal z okoljskimi nastavitvami vsakič ko se vrne v razvojno okolje z željo nadaljevanja razvoja projekta. Rešitev (Solution) je torej sestavljena iz dveh datotek

Page 14: PROŠNJA - uranic.tsckr.siuranic.tsckr.si/VISUAL C#/VISUAL C#.pdf · MICROSOFT VISUAL C# .NET Srečo Uranič . Visual C# . ... Za začetek novega projekta v okolju Visual C# Standard

Visual C# . NET

Algoritmi in programski jeziki – 4.letnik

14

• .sln (Visual Studio Solution), ki vsebuje popoln opis o vsebini rešitve. Organizira projekt in njegove komponente v skupno rešitev in zagotovi ustrezno okolje s potrebnimi referencami in njihovim položajem na mediju - disku ; datoteko lahko uporablja več razvijalcev aplikacije;

• .suo (Solution User Options), ki vsebuje podatke o pozicijah oken. Datoteka je specifična za posameznega razvijalca, hrani pa vse potrebne nastavitve, zato da jih pri ponovnem odpiranju projekta ni potrebno nastavljati ponovno.

Datoteka .sln je nepogrešljiva, datoteka .suo pa je pogrešljiva: rešitev lahko zgradimo tudi brez nje.

Page 15: PROŠNJA - uranic.tsckr.siuranic.tsckr.si/VISUAL C#/VISUAL C#.pdf · MICROSOFT VISUAL C# .NET Srečo Uranič . Visual C# . ... Za začetek novega projekta v okolju Visual C# Standard

Visual C# . NET

Algoritmi in programski jeziki – 4.letnik

15

Okenske aplikacije (Windows Forms) v Visual C# . NET 2008 Express Edition Razvojno orodje Visual C# vsebuje tudi vse potrebno za razvoj okenskih aplikacij. S počjo Visual Designer-ja lahko kreiramo uporabniški vmesnik, ki temelji na t.i. obrazcih (Forms). Na obrazce postavljamo gradnike, Visual C# pa sam generira programsko kodo, ki ustreza uporabniškemu vmesniku, ki smo ga oblikovali. To pa pomeni, da imamo dva različna pogleda na našo aplikacijo: oblikovni pogled (Design View) in urejevalni oz. kodirni pogled (Code View). Med obema pogledamo lahko kadarkoli preklapljamo s klikom miške ali pa s kombinacijo tipk na tipkovnici.

Kreiranje osnovne okenske aplikacije v okolju Visual C# Za izdelavo prve okenske aplikacije odpremo meni File -> New Project. Odpre se novo okno &ew Project v

katerem pod Templates (Vzorci) izberemo ikono Windows Application. Na dnu okna napišimo še ime naše prve okenske aplikacije, npr. PrviOkenskiProgram. Obenem se bo ustvarila tudi mapa z enakim imenom, enako ime pa bo imel tudi izvršilni program (executable file), ki bo nastal kot končni rezultat našega projekta. Vnos potrdimo s klikom na gumb OK in s tem ustvarimo nov projekt.

Na ekranu se prikaže nov prazen obrazec (Windows Form) v oblikovalskem pogledu (Design View). Orodjarna (Toolbox) = skladišče razpoložljivih gradnikov, ki jih postavljamo na obrazce Obrazec (Form) Lastnosti objektov (Properties) Kratek opis posamezne lastnosti gradnika

Page 16: PROŠNJA - uranic.tsckr.siuranic.tsckr.si/VISUAL C#/VISUAL C#.pdf · MICROSOFT VISUAL C# .NET Srečo Uranič . Visual C# . ... Za začetek novega projekta v okolju Visual C# Standard

Visual C# . NET

Algoritmi in programski jeziki – 4.letnik

16

� Opomba: Število oken, ki se prikažejo v razvojnem okolju je odvisno od nastavitev. Zgornja slika prikazuje najpogosteje odprta okna, pri čemer pa lahko katerokoli od oken tudi

minimiziramo oz. zopet prikažemo. Če je okno skrito, kliknemo na Toolbox na levem robu ekrana, če pa je povsem zaprto pa ga ponovno odprimo z izbiro View -> Toolbox.

Ko se okno prikaže, ga lahko s klikom na priponko fiksiramo na tem delu ekrana. Enako pravilo velja tudi za okni Solution Explorer in Properties.

Vaja: Naš prvi uporabniški vmesnik bomo oblikovali tako, da bomo na obrazec postavili tri gradnike, nato pa pregledali generirano kodo, ki ustreza temu obrazcu in trem gradnikom na njem. Le-to bo za nas ustvaril Visual C# avtomatično s tem, ko bomo odprli nov projekt in bomo na prazen obrazec postavili tri gradnike. Gradniki, ki jih lahko uporabimo za razvoj našega projekta so zbrani v oknu ToolBox.

� Opomba: Če se v oknu Toolbox z miško zapeljemo čez poljuben gradnik in za trenutek počakamo, se v okvirčku izpiše osnovni namen tega gradnika.

Posamezni gradniki v oknu Toolbox so grupirani v posamezne skupine (Common Controls, Menus & Toolbars, Data, Components, Printing, Dialogs, ..) glede na njihov namen. Posamezno skupino gradnikov odpremo s klikom miške na znak + pred imenom skupine, zapremo pa s klikom na znak – pred imenom skupine. V oknu Toolbox kliknimo gradnik Label, nato pa kliknimo v zgornji levi del obrazca. Gradnik Label smo tako

dodali na obrazec. Ne enak način dodajmo na obrazec še gradnika ToolBox in Button. Gradnike razporedimo približno tako kot kaže slika. Če smo na obrazec pomotoma postavili gradnik, ki ga ne potrebujemo, ga lahko odstranimo na dva načina

• Označimo ga s klikom miške in pritisnemo tipko Delete na tipkovnici,

• Označimo ga s klikom miške, kliknemo desno tipko miške in v prikazanem Pop Up meniju izberemo opcijo Delete.

� Opomba: Gradnikom na obrazcu lahko poljubno spreminjamo velikost. Izberemo gradnik, ki

ga želimo povečati/pomanjšati (tako da nanj kliknemo…) , nato pa ga z miško raztegnemo/skrčimo na želeno velikost. Če je na obrazcu več gradnikov, je možna njihova poravnava tako, da nek gradnik približamo drugemu in prikažeta se vodoravna in navpična črta, ki omogočata natančno poravnavo.

Okno Properties (lastnosti objektov) je namenjeno prikazu in spreminjanju lastnosti (pa tudi metod oz. dogodkov) gradnikov (objektov), ki sestavljajo naš projekt. Vsak gradnik (tudi obrazec je gradnik oz. objekt) ima namreč cel kup privzetih lastnosti, ki pa jih lahko s pomočjo okna Propetries spremenimo in tako spremenimo npr. lastnosti posameznega gradnika na obrazcu, seveda pa tudi lastnosti samega obrazca. Če sedaj kliknemo na obrazec Form1 (ga izberemo, na njegovih robovih se prikažejo kvadratki), se v oknu Properties prikažejo lastnosti tega obrazca. Podobno si lahko ogledamo privzete lastnosti ostalih treh gradnikov, ki smo jih postavili na obrazec (label1, texbox1 in button1). Vsak od gradnikov ima cel kup lastnosti, posamezne izmed njih pa bomo spoznavali v nadaljevanju. Ena izmed lastnosti gradnikov pa je tako pomembna, da jo omenimo najprej, to je lastnost (name) – ime gradnika. Ime gradnika je namreč oznaka, preko katere se sklicujemo na posamezne gradnike. Visual C# poimenuje imena gradnikov tako, da jim zaporedoma dodeli imena z dodano številko na koncu (label1, label2, label3,… ali pa button1, button2, …). Imena gradnikov lahko poljubno spremenimo (to je pri obsežnejših projektih celo priporočljivo oz. skoraj nujno, saj sicer izgubimo pregled nad gradniki), a pri tem je potrebno paziti, da se imena ne podvajajo, sicer bomo dobili obvestilo o napaki. Kadar

Page 17: PROŠNJA - uranic.tsckr.siuranic.tsckr.si/VISUAL C#/VISUAL C#.pdf · MICROSOFT VISUAL C# .NET Srečo Uranič . Visual C# . ... Za začetek novega projekta v okolju Visual C# Standard

Visual C# . NET

Algoritmi in programski jeziki – 4.letnik

17

dodelujemo gradnikom svoja imena, je priporočljivo, da uporabljamo zveneča imena: npr GumbVnos, GumbZapri, GumbAzuriraj, … . POZOR: ime gradnika ne sme vsebovati presledka! Lastnosti gradnikov v oknu Properties so lahko nanizane na dva načina: po abecednem redu ali pa po kategorijah. S klikom na oba gumba lahko izberemo način, ki nam trenutno bolj ustreza. Seznam vseh gradnikov, ki smo jih postavili na obrazec lahko vidimo tudi v oknu Properties. Poskrbimo za to, da bo izbran gradnik obrazec (Form1), nato v oknu Properties odpremo seznam vsebovanih gradnikov.

Odpre se okno v katerem so nanizani vsi gradniki, ki so trenutno na obrazcu. Izberemo lahko kateregakoli in v oknu Properties se prikažejo njegove lastnosti v oknu Design View pa je ta gradnik izbran.

V Solution Explorerju sedaj kliknimo na ikono View Code in namesto obrazca se sedaj prikaže okno za urejanje kode (Code And Text Editor). V oknu Code/Design View se pokaže nov zavihek. Če sedaj kliknemo zavihek (Tab), ki ima dodano besedico [design] se vrnemo nazaj na oblikovni pogled obrazca.

� Opomba: Kodo, ki pripada posameznemu obrazcu lahko prikažemo tudi neposredno iz osnovnega menija. V osnovnem meniju kliknemo opcijo View -> Code (ali tipka F7 na tipkovnici). V oblikovni pogled pa se prestavimo z opcijo View -> Designer ali pa s kombinacijo tipk Shift+F7.

Vsak obrazec v Visual C# je namreč predstavljen na dva načina: v oblikovnem pogledu (Form1.cs [design]) in v pogledu za urejanje kode (Form1.cs). Oblikovni pogled (Design View) in pogled za urejanje kode (Code View) sta med seboj neodvisna, tako da lahko hkrati na ekranu prikažemo oba pogleda, kar je še posebej ugodno kadar imamo na razpolago velik monitor. Oba pogleda lahko na ekranu dobimo takole: preklopimo najprej na

pogled za urejanje, nato pa z miško primimo zavihek Form1.cs [design]) in ga povlecimo nad pogled za urejanje kode. Spustimo tipko na miški in prikaže se Pop Up meni s tremi opcijami. Izberimo npr. opcijo &ew Vertical Tab Group. Okno za urejanje kode se sedaj razdeli na dva dela: v enem imamo predstavljen pogled za urejanje kode obrazca, v drugem pa oblikovni pogled obrazca. Seveda lahko novo okno kadarkoli zopet zapremo in se

tako vrnemo v prejšnji pogled.

Page 18: PROŠNJA - uranic.tsckr.siuranic.tsckr.si/VISUAL C#/VISUAL C#.pdf · MICROSOFT VISUAL C# .NET Srečo Uranič . Visual C# . ... Za začetek novega projekta v okolju Visual C# Standard

Visual C# . NET

Algoritmi in programski jeziki – 4.letnik

18

Preglejmo sedaj še vsebino okna Code View:

Vključevanje že obstoječih imenskih prostorov v projekt Ime najbolj zunanjega imenskega prostora je enako imenu projekta Razred Form1 – znotraj našega imenskega prostora je orodje zgeneriralo razred Form1, ki ustreza obrazcu, ki ga vidimo v oblikovnem pogledu (design View). Zgeneriran je tudi osnovni konstruktor razreda Form1. (PO�OVIMO: Konstruktor je metoda, ki ima enako ime kot razred, nima tipa, nič ne vrača, njegova glavna naloga običajno pa je, da poskrbi za inicializacijo! Izvede se takrat, ko se kreira nov

objekt izpeljan iz nekega razreda - oz v našem primeru ko se kreira nov obrazec). Znotraj konstruktorja je napisana metoda InitializeComponent. Ta metoda poskrbi za pravilne začetne nastavitve obrazca.

V zgornjem primeru je razred Form1 deklariran kot public partial class Form1. Besedice partial še ne poznamo, njen pomen pa je naslednji: deklaracijo razreda (ali pa strukture, vmesnika) lahko razdelimo na dva ali več delov, ki so zapisani v ločenih datotekah. Vsaka od teh datotek vsebuje del definicije razreda in vsi ti deli se prevedejo ko se naša aplikacija prevaja. Obstaja kar nekaj situacij, ko je taka delitev definicije razredov zaželena, npr.:

• Pri delu z velikimi projekti, saj razdelitev razredov na več ločenih datotek omogoča, da več razvijalcev projekta dela istočasno;

• Pri delu s kodo, ki se generira avtomatsko, lahko novo kodo vstavljamo v razred brez ponovnega kreiranja izvorne datoteke.

Nadaljujmo sedaj z našim prvim projektom. Vsem trem gradnikom na obrazcu (labela, vnosno polje in gumb) bomo spremenili eno od privzetih lastnosti.

• Preklopimo na oblikovni pogled (Design View) in kliknimo na gumb, ki je na obrazcu – okoli gumba se pojavijo majhni kvadratki, kar pomeni, da je gumb izbran. S tem ko smo ga izbrali, imamo v oknu Properties prikazane lastnosti tega gumba. Izberimo lastnost Tekst in v okence na desni strani te lastnosti vtipkajmo OK in pritisnimo Enter. Na obrazcu se napis na gumbu spremeni v OK.

• Na obrazcu izberemo gradnik Label in v oknu Properties se prikažejo lastnosti gradnika Label. V oknu Properties zopet izberemo lastnost Text in vpišemo besedilo Vnesi svoje ime in stisnimo Enter. Na obrazcu se napis na labeli spremeni tako kot smo napisali.

• Na obrazcu izberemo še gradnik TextBox in v oknu Properties spremenimo lastnost Text v Tvoje ime? in nato še stisnimo Enter. Vneseno besedilo se pojavi tudi na obrazcu.

Vse spremembe, ki smo jih naredili v oknu Properties lahko vidimo tudi v oknu Form.designer.cs. V oknu Solution Explorer izberimo vrstico Form1.designer.cs in v njem kliknimo na + v vrstico Windows Form Designer Generated Code. Kodo si le oglejmo in obenem poiščimo vrstice, ki so nastale, ko smo v oknu Properties spremenili gradnikom lastnost Text.

using System; using System.Collections.Generic; using System.ComponentModel; using System.Data; using System.Drawing; using System.Text; using System.Windows.Forms; namespace PrviOkenskiProgram { public partial class Form1 : Form { public Form1() { InitializeComponent(); } } }

#region Windows Form Designer generated code /// <summary> /// Required method for Designer support-do not modify /// the contents of this method with the code editor. /// </summary> private void InitializeComponent() { .

. this.label1.Text = "Vnesi svoje ime"; . . this.textBox1.Text = "Tvoje ime?"; . . this.button1.Text = "OK"; . . } #endregion

Page 19: PROŠNJA - uranic.tsckr.siuranic.tsckr.si/VISUAL C#/VISUAL C#.pdf · MICROSOFT VISUAL C# .NET Srečo Uranič . Visual C# . ... Za začetek novega projekta v okolju Visual C# Standard

Visual C# . NET

Algoritmi in programski jeziki – 4.letnik

19

Tega okna običajno ne odpiramo, še manj pa spremembe v njem delamo sami. Spremembe se v tej datoteki generirarjo avtomatsko, s tem ko na obrazec postavljamo gradnike, ali pa brišemo gradnike, oz. ko spreminjamo njihove lastnosti. Okno nato zaprimo. (z miško se prestavimo na zavihek Form.designer.cs*, stisnimo desni gumb in izberimo opcijo Close). Za vajo spremenimo še nekaj lastnosti samega obrazca. V oblikovnem pogledu kliknimo kamorkoli na obrazec, da ga izberemo; na njegovih robovih se pojavijo kvadratki, v oknu Properties pa se pojavijo lastnosti obrazca. Lastnost Text spremenimo v Moj prvi okenski program in stisnimo Enter. Napis v naslovni vrstici obrazca se spremeni tako kot smo napisali. Z miško se nato prestavimo na spodnji desni kvadratek na robu obrazca, Kazalnik miške se spremeni v diagonalno puščico; stisnimo levi gumb na miški in nato spremenimo velikost

obrazca tako, kot prikazuje slika.

� Opomba: Marsikateri gradnik (med njimi tudi TextBox) ima, če ga označimo, v desnem zgornjem kotu posebno puščico. Le-ta omogoča dodatne hitre nastavitve, ki pa se razlikujejo od gradnika do gradnika. Pri gradniku TextBox je dodatna hitra nastavitev lastnost Multiline. Če odkljukamo to možnost, lahko besedilo v gradniku TextBox raztegnemo čez večje število vrstic.

Sedaj pa bomo napisali naš prvi dogodek, to je dogodek ki naj se zgodi ob kliku na gumb OK.

• Z miško se postavimo nad gumb OK na obrazcu in dvokliknimo na ta gumb. Na ekranu se oblikovni pogled spremeni v pogled za urejanje kode (Form1.cs), Visual C# pa je v razred Form1 v tem oknu dodal metodo, ki jo je poimenoval button1_Click. Prav tako se je v metodi InitializeComponent dodal stavek, ki bo poskrbel za klic metode button1_Click v trenutku, ko bo uporabnik kliknil na gumb OK.

• Znotraj metode button1_Click vstavimo sedaj vrstico z naslednjo kodo.

private void button1_Click(object sender, EventArgs e) { MessageBox.Show("Pozdravljen " + textBox1.Text); }

Metoda MessageBox predstavlja sporočilno okno uporabniku. V tem primeru smo napisali njegovo najenostavnejšo obliko, ostale oblike tega sporočilnega okna pa bomo spoznali kasneje.

� Pozor: Če želimo v oknu Properties odstraniti nek dogodek, ga enostavno pobrišemo s tipko Delete. A pri tem velja: če je telo ustrezne metode v oknu za urejanje kode (Code Window) prazno, bo ob brisanju imena dogodka v oknu Properties urejevalnik pobrisal tudi celotno metodo (glavo in telo) v oknu CodeWindow. Če pa smo v telo metode že napisali karkoli (četudi komentar), potem bo celotna koda metode v oknu Code Window ostala. Obratno pa se lahko zgodi, da najprej v oknu za urejanje kode pobrišemo celotno metodo, ne da bi prej odstranili njegovo najavo v oknu Poperties. V takem primeru nam bo pri prvem prevajanju prevajalnik javil napako. Problem rešimo tako, da v oknu Form1.designer.cs poiščemo ustrezno z najavo tega dogodka in vrstico v celoti pobrišemo. Najhitreje to vrstico najdemo tako, da v oknu s seznamom napak kliknemo na vrstico z napako in se s tem avtomatsko prestavimo v okno Form1.designer.cs.

Naš prvi projekt v Windows okolju je sedaj pripravljen za zagon. V meniju Debug kliknimo opcijo Start Without Debugging (lahko pa tudi Start Debugging ali pa kar pritisnimo na tipko F5 ali pa kliknemo gumb Start Debugging v orodjarni.

Page 20: PROŠNJA - uranic.tsckr.siuranic.tsckr.si/VISUAL C#/VISUAL C#.pdf · MICROSOFT VISUAL C# .NET Srečo Uranič . Visual C# . ... Za začetek novega projekta v okolju Visual C# Standard

Visual C# . NET

Algoritmi in programski jeziki – 4.letnik

20

Visual C# bo najprej shranil naše dosedanje delo, nato pa zgradil program in ga zagnal. Na ekranu se prikaže obrazec: V vnosno polje vnesimo naše ime in kliknimo na gumb OK. V sporočilnem oknu se prikaže pozdravno sporočilo. S klikom na gumb OK se sporočilno okno zapre. Če želimo zapreti/zaključiti še naš prvi program, kliknemo na gumb za zapiranje okna.

� Opomba: Gradnik TextBox ima tudi lastnost PasswordChar: namenjena je vnosu znaka, ki se prikaže pri vnosu gesla v enovrstični gradnik tipa TextBox. (Kadar je gradnik TextBox namenjen vnosu gesla, mora biti torej lastnost multiline nastavljena na false).

Lastnosti obrazca smo v prejšnjem primeru spreminjali preko okna Solution Explorer. Seveda pa lahko lastnosti spreminjamo tudi programsko, s pomočjo dogodkov. Ob odprtju vsakega obrazca se tako najprej zgodi dogodek Load, ki ga lahko uporabimo za programsko nastavljanje lastnosti obrazca. Vendar POZOR! Na lastnosti obrazca se ne sklicujemo preko njegovega imena (npr. Form1.Text = … ) ampak preko kazalca this (napisati moramo torej npr. this.Text= …). Enako velja za programski dostop do ostalih lastnosti.

� Opomba: PONOVITEV IZ OSNOV jezika C#: Prevajalnik vsaki metodi razreda doda še en prameter- kazalec, ki kaže na konkreten objekt ( v našem primeru obrazec) nad katerim deluje metoda. Temu parametru je ime this. Inicializira se ob klicu metode in ga uporabljamo za dostop do lastnosti tega objekta.

Preimenovanje obrazca (forme) Včasih se zgodi, da želimo že pripravljen obrazec preimenovati. To lahko storimo na dva načina

• V Solution Explorerju izberemo obrazec, ki ga želimo preimenovati (npr. Form1.cs) in ga preimenujemo. To lahko storimo tako, da ga najprej izberemo, kliknemo desni miškin gumb in nato izberemo opcijo Rename. Seveda pa ga lahko preimenujemo tudi tako, da obrazec najprej izberemo (ga kliknemo), počakamo nekaj trenutkov, nato pa ga še enkrat kliknemo. Po nekaj trenutkih se prejšnje ime obda z okvirčkom in nato kar v njem spremenimo ime obrazca (končnico cs ni potrebno pisati, doda se avtomatsko). Po spremembi imena nam okolje Visual C# v sporočilnem oknu izpiše obvestilo, da smo spremenili ime datoteke in nas vpraša, če želimo preimenovati tudi vse reference, ki se v tem projektu nanašajo na ta obrazec. Izberimo gumb Da.

Desedanje ime obrazca Form1.cs smo spremenili v frmPrvi.cs

Page 21: PROŠNJA - uranic.tsckr.siuranic.tsckr.si/VISUAL C#/VISUAL C#.pdf · MICROSOFT VISUAL C# .NET Srečo Uranič . Visual C# . ... Za začetek novega projekta v okolju Visual C# Standard

Visual C# . NET

Algoritmi in programski jeziki – 4.letnik

21

• V oknu Properties izberemo lastnost &ame tega obrazca in spremenimo ime. A v tem primeru bomo spremenili le ime temu obrazcu, ne pa tudi reference na prejšnje ime. Boljši je torej prvi način preimenovanja obraza.

Preimenovanje imen odzivnih dogodkov Po spremembi imena obrazca (ali pa kateregakoli gradnika, npr. gumba) se mogoče pojavi želja, da bi spremenili še imena odzivnih dogodkov. Pa tudi sicer se večkrat pojavi potreba, da bi spremenili ime nekega dogodka in mu dali bolj domače oz. zveneče ime. Recimo, da bi radi spremenili ime dogodka, ki se dogodi o kliku na gumb OK v našem prvem okenskem programu. Na obrazcu dvokliknimo na ta gumb (v splošnem pa se prestavimo v urejevalnik kode in poiščemo dogodek, ki ga želimo preimenovati) in se s tem postavimo v urejevalnik kode. Sedanje ime dogodka button1_Click preimenujmo v Gumb_Click. Po preimenovanju se pod imenom dogodka

pojavi majhen pravokotniček. Kliknimo ga da se prikaže okence z ikono in trikotnikom za aktiviranje spustnega seznama. Odprimo spustni seznam in izberimo opcijo Rename …

Glavne datoteke, ki sestavljajo okenski projekt v Visual C# Sedaj, ko smo naredili prvi projekt v Visual C#, si še oglejmo, najpomembnejše datoteke, ki se kreirajo ob vsakem novem projektu. Te datoteke so nepogrešljive za delovanje našega projekta znotraj neke rešitve.

Ime datoteke Oznaka imenika

(mape) Opis

Solname.sln Imeprojekta Datoteka rešitve uporabljena v razvojnem okolju. Njena naloga je organizacija vseh elementov projekta oz. več projektov ki sestavljajo rešitev. Datoteka je shranjena v pripadajoči projektni mapi.

Projname.suo Imeprojekta

Datoteka hrani vse potrebne nastavitve, ki smo jih kot razvijalci projekta naredili za našo rešitev Pri vnovičnem odpiranju projekta ima le-ta izgled in vse potrebne nastavitve enake kot tedaj, ko smo projekt nazadnje odprli. Tudi ta datoteka je shranjena v pripadajoči projektni mapi.

Projname.csproj Imeprojekta

Projektna datoteka uporabljena znotraj razvojnega okolja. Hrani informacije specifične za naš projekt, zaradi tega ima vsak projekt svojo datoteko .csproj. Tudi ta datoteka je shranjena v pripadajoči projektni mapi. Datotek tipa .csproj je znotraj rešitve (Solution) toliko, kot je projektov, ki sestavljajo to rešitev.

FormX.cs

Datoteka s kodo (programski modul, .cs je kratica za C Sharp), ki pripada nekemu obrazcu. Pripadajoči obrazec ima enako ime kot ta datoteka, le da ima drugo končnico (.Designer.cs). Obstajajo tudi datoteke tipa cs ki nimajo svojega obrazca: v njih je npr. napisan kak pomemben razred, v njej so lahko zbrane pomembne metode.

FormX.resx

Datoteka namenjena urejanju resursov aplikacije. Vsebujejo lahko podatke v različnih oblikah:to lahko slike, besedila,.. Shranjevanje podatkov v datoteko tipa .resx omogoča spreminjanju le-teh brez ponovnega prevajanja celotnega projekta.

Page 22: PROŠNJA - uranic.tsckr.siuranic.tsckr.si/VISUAL C#/VISUAL C#.pdf · MICROSOFT VISUAL C# .NET Srečo Uranič . Visual C# . ... Za začetek novega projekta v okolju Visual C# Standard

Visual C# . NET

Algoritmi in programski jeziki – 4.letnik

22

Projname.pdb Datoteka hrani informacije v Code View formatu za Visual Studio razhroščevalnik (Debugger).

Projname.txt Imeprojekta Datoteka, ki hrani podatke o tem, v katerih mapah se nahajajo druge datoteke tega projekta.

Vstopna točka vsake windows aplikacije pa je datoteka tipa .cs, ki pa nima svojega obrazca in ki vsebuje glavno funkcijo za zagon projekta – funkcijo main. Datoteka ima običajno ime Program.cs in bi ji lahko tudi rekli glavni program našega projekta.

Datoteka običajno vsebuje le napovedi osnovnih potrebnih imenskih prostorov, v funkciji main pa so običajno le štirje stavki: Metoda, ki pove, da gre za vizualni projekt; Inicializacija; Odpiranje osnovnega obrazca

Poleg omenjenih datotek seveda obstajajo še nekatere druge, ki pa zaenkrat niso tako pomembne.

using System; using System.Collections.Generic; using System.Linq; using System.Windows.Forms; namespace Ime_Projekta { static class Program { /// <summary> /// The main entry point for the application. /// </summary> /// [STAThread] static void Main() { Application.EnableVisualStyles(); Application.SetCompatibleTextRenderingDefault(false); Application.Run(new Form1()); } } }

Page 23: PROŠNJA - uranic.tsckr.siuranic.tsckr.si/VISUAL C#/VISUAL C#.pdf · MICROSOFT VISUAL C# .NET Srečo Uranič . Visual C# . ... Za začetek novega projekta v okolju Visual C# Standard

Visual C# . NET

Algoritmi in programski jeziki – 4.letnik

23

Identifikatorji, spremenljivke, operatorji in izrazi v Visual C# . NET Omenjeni izrazi so nam že poznani iz osnov jezika C#. Identifikatorji so imena, ki jih v programih uporabljamo za označevanje elementov našega programa. V C# veljata pri poimenovanju identifikatorjev naslednji pravili:

• Uporabljajo se lahko le črke (velike ali male), cifre in znak podčrtaj (_). • Identifikator se mora začeti s črko (podčrtaj se smatra kot črka).

Veljavna imena identifikatorjev so tako: result, _rezultat, DvaInDvajset, plan10, … Neveljavna imena pa so : rezult%, Trinajst%, 7Plan, … Pri poimenovanju spremenljivk veljajo pri .&ET platformi na splošno nekatera priporočila:

• Ne uporabljamo podčrtajev v imenih spremenljivk. Spremenljivko poimenovati npr. _ime ni priporočljivo (je pa seveda dovoljeno!);

• Ne kreirajmo imen spremenljivk (pa tudi ne imen identifikatorjev nasploh) , ki se razlikujejo le po tem, da so v njih velike ali pa male črke. Tako npr. ni priporočljivo, da imamo v programu spremenljivki z imenoma mojaSpr in MojaSpr;

• Spremenljivka naj se začenja z malo črko; ime spremenljivke GlavnoMesto ni priporočljivo (bolje je glavnoMesto)

• Če je imen spremenljivk sestavljeno iz več besed, naj se vsaka nadaljnja beseda začne z veliko začetnico. Kot primer vzemimo npr. spremenljivko glavnoMesto (t.i. camelCase konotacija).

Osnovni podatkovni tipi v C#

Podatkovni tip Opis Velikost v bitih Obseg Primer uporabe

int Cela števila 32 -231 do 231 -1 int stevilo; stevilo = 100;

long Cele števila ( večji obseg )

64 -263 do 263 -1 long veliko; veliko = 42L;

float Decimalno število 32 -3.4x1038 do

3.4x1038 float znesek; znesek = 0.42F;

double Decimalno število ( večji obseg )

64 -1.7x10308 do

1.7x10308 double ogromno; ogromno = 0.42;

decimal Denarna vrednost 128 28 cifer decimal kovanec; kovanec = 0.42M;

string Znak 16 bitov za en znak 0 do 216 -1 string beseda; beseda=''Denar'';

char Znak 16 0 do 216 -1 char znak; znak = 'K';

bool Logična vrednost ( true oz. false )

8 true ali false bool logicna; logicna = false;

Podatkovni tip string označuje zaporedje znakov. Spremenljivko tega tipa v C# lahko inicializiramo tudi takole: string var5; var5 = "Lokomotiva";

Page 24: PROŠNJA - uranic.tsckr.siuranic.tsckr.si/VISUAL C#/VISUAL C#.pdf · MICROSOFT VISUAL C# .NET Srečo Uranič . Visual C# . ... Za začetek novega projekta v okolju Visual C# Standard

Visual C# . NET

Algoritmi in programski jeziki – 4.letnik

24

&ajpomembnejše lastnosti in metode razreda string za delo z zaporedji znakov (stringi)

Indeks Razlaga

[indeks] Dostop do znaka na določeni poziciji.

Lastnost Razlaga

Length Število znakov stringu.

Metoda Razlaga

StartsWith(string) Vrne logično vrednost, ki označuje, ali se nek string začenja z navedenim stringom.

EndsWith(string) Vrne logično vrednost, ki označuje, ali se nek string končuje z navedenim stringom.

IndexOf(string[,začetni indeks])

Vrne celo število ki predstavlja pozicijo (indeks) prve pojavitve navedenega stringa v nekem stringu od začetnega indeksa naprej. Če začetni indeks ni naveden, se iskanje začne na začetku stringa. Če navedeni string ni najden, je vrnjena vrednost -1.

Insert(začetni indeks, string) Vrne string v katerega je na navedeno mesto (začetni indeks) vrinjen navedeni string.

PadLeft(skupna_dolžina) Vrne string, ki je DESNO poravnan in na levi strani zapolnjen s tolikšnim številom presledkov, da je skupno število znakov enako vrednosti skupna_dolžina.

PadRight(skupna_dolžina) Vrne string, ki je LEVO poravnan in na desni strani zapolnjen s tolikšnim številom presledkov, da je skupno število znakov enako vrednosti skupna_dolžina.

Remove(začetni_indeks, &) Vrne string iz katerega je odstranjeno & znakov od pozicije začetni_indeks naprej.

Replace(stariString, noviString) Vrne string, v katerem so vse pojavitve stringa stariString zamenjane s stringom noviString.

Substring(začetni_indeks[,dolžina]) Vrne del stringa, ki se začne na navedeni poziciji in ima navedeno dolžino. Če dolžina ni navedena metoda vrne vse znake do konca stringa.

ToLower() Vrne string v katerem so vsi znaki zamenjani z malimi znaki.

ToUpper() Vrne string v katerem so vsi znaki zamenjani z velikimi znaki.

Trim() Vrne string iz katerega so odstranjeni vsi vodilni in končni presledki.

Split(razmejitveni_znak) Vrne tabelo stringov v katerem so vsi elementi deli tega stringa (besede) med seboj razmejeni z znakom razmejitveni_znak.

Nekaj primerov: //dostop do posameznega znaka v stringu string znaki = "abcdefg"; char a=znaki[0]; // 'a'

Page 25: PROŠNJA - uranic.tsckr.siuranic.tsckr.si/VISUAL C#/VISUAL C#.pdf · MICROSOFT VISUAL C# .NET Srečo Uranič . Visual C# . ... Za začetek novega projekta v okolju Visual C# Standard

Visual C# . NET

Algoritmi in programski jeziki – 4.letnik

25

char b=znaki[1]; // 'b' //for zanka za dostop do vseh znakov v stringu string znakiInPresledki = ""; for (int i = 0; i < znaki.Length; i++) znakiInPresledki += znaki[i] + " "; //spremenljivka znakiInPresledki dobi vrednost "a b c d e f g " //foreach zanka za dostop do vseh znakov v stringu string znakiSPresledki = ""; foreach (char znak in znaki) znakiSPresledki += znak + " "; //sprem. znakiSPresledki dobi vrednost "a b c d e f g " //Uporaba metod StartsWith in EndsWith bool zacneZabc = znaki.StartsWith("abc"); // true bool koncaZabc = znaki.EndsWith("abc"); // false //Uporaba metode IndexOf string sola = "Tehniški Šolski Center Kranj"; int index1 = sola.IndexOf(" "); // 8, ker se string " " pojavi prvič na osmem mestu int index2 = sola.IndexOf(' '); // 8, ker se znak ' ' pojavi prvič na osmem mestu int index3 = sola.IndexOf("Center"); // 16, ker se string "Center" pojavi prvič na 16 mestu int index4 = sola.LastIndexOf(" "); // 22, ker se string " " pojavi zadnjič na 22 mestu //Uporaba metod Remove, Insert in Replace sola = sola.Remove(0, 9); // Šolski center Kranj sola = sola.Insert(sola.Length, ", Slovenija"); // Šolski center Kranj, Slovenija sola = sola.Replace("Slovenija", "4000 Kranj"); // Šolski center Kranj, 4000 Kranj //Uporaba metod Substring, ToUpper in ToLower string ime = "aNJA"; string prvaCrka = ime.Substring(0, 1).ToUpper(); // A string drugeCrke = ime.Substring(1).ToLower(); // nja ime = prvaCrka + drugeCrke; // Anja //Kopiranje enga stringa v drug string string s1 = "abc"; string s2 = s1; // string s2 dobi vrednost strinfa s1, torej s2 postane "abc" s2 = "def"; // string s2 postane "def", string s1 pa se ne spremeni string s3 = s1 + s2; // strig s3 dobi vrednost "abcdef" //Uporaba metode Substring string polnoIme = " Edward C Koop "; // " Edward C Koop " polnoIme = polnoIme.Trim(); // "Edward C Koop" int prvipresledek = polnoIme.IndexOf(" "); // 6 string lastnoIme = " "; if (prvipresledek == -1) lastnoIme = polnoIme; else lastnoIme=polnoIme.Substring(0,prvipresledek); // "Edward" //Uporaba razmejitvenih znakov v stringu string naslov = " |34 Kališka ulica|Slovenija|Kranj|4000 "; naslov = naslov.Trim(); // "|34 Kališka ulica|Slovenija|Kranj|4000" if (naslov.StartsWith("|")) naslov.Remove(0,1); // Če se naslov začne z razmejitvenim znakom,ga odstranimo if (naslov.EndsWith("|")) naslov.Remove(naslov.Length-1,1);// Če se naslov konča z razmejitvenim znakom,ga odstranimo int indeksUlice = naslov.IndexOf("|") + 1; // 1 int indeksDrzave = naslov.IndexOf("|", indeksUlice) + 1; // 18 int indeksKraja = naslov.IndexOf("|", indeksDrzave) + 1; // 28 int indeksPoste = naslov.IndexOf("|", indeksKraja) + 1; // 34 string ulica = naslov.Substring(0, indeksDrzave-1); // 34 Kališka ulica string mesto = naslov.Substring(indeksKraja, indeksPoste - indeksKraja - 1); // Kranj string posta = naslov.Substring(indeksPoste); // 4000 //Uporaba metode Split string pisatelj = " Edward C Koop "; // " Edward C Koop " pisatelj = pisatelj.Trim(); // "Edward C Koop" string[] imena = pisatelj.Split(' '); string imePisatelja = imena[0]; // "Edward" string imePisatelja1 = imena[1]; // "C" string imePisatelja2 = imena[2]; // "Koop"

Page 26: PROŠNJA - uranic.tsckr.siuranic.tsckr.si/VISUAL C#/VISUAL C#.pdf · MICROSOFT VISUAL C# .NET Srečo Uranič . Visual C# . ... Za začetek novega projekta v okolju Visual C# Standard

Visual C# . NET

Algoritmi in programski jeziki – 4.letnik

26

//Uporaba metode Insert string telefonskaStevilka = "041827919"; // "041827919" telefonskaStevilka = telefonskaStevilka.Insert(3, "-"); // "041-827919" telefonskaStevilka = telefonskaStevilka.Insert(7, "-"); // "041-827-919" //Uporaba metode Replace string datum = "21-08-2007"; // "21-08-2007" datum = datum.Replace("-", "/");

Vaja:

Za vajo in obenem za ponovitev osnovnih podatkovnih tipov kreirajmo nov projekt, ga poimenujmo OsnovniPodatkovniTipi in na obrazec postavimo naslednje gradnike:

Gradniki tipa Label TextBox (lastnost (Name) = lVrednost, lastnost Enabled = false) ListBox (lastnost (name) = lbTip) Button (lastnost (name) = Konec)

� Moje priporočilo: Če gradnikom, ki jih izberemo v oknu Toolbox spreminjamo imena, naj bo

novo ime tako, da prve črke gradnika označujejo gradnik (l za Labelo, tb za TextBox, b za Button, lb za ListBox, …). Na ta način bomo v seznamu vseh gradnikov takoj vedeli, kateri izmed njih so npr labele, kater gumbi, itd.

Vsebino gradnika ListBox določimo tako, da gradnik najprej izberemo, nato pa v oknu z lastnostmi tega objekta (Properties) poiščimo lastnost Items. Kliknimo na tropičje povsem na desni strani te lastnosti in odpre se okno, v katerega vrstico za vrstico vnesemo imena vseh podatkovnih tipov. Vnos na koncu potrdimo s klikom na gumb OK. Napisati moramo še dogodek, ki se bo zgodil, ko bo uporabnik kliknil na poljuben podatkovni tip v gradniku tipa ListBox. Izberimo gradnik ListBox, nato v oknu Properties kliknimo gumb za prikaz vseh dogodkov tega gradnika in med nanizanimi dogodki poiščemo dogodek SelectedIndexChanged. Kliknimo v okno na desni strani tega dogodka in se tako prestavimo v pogled za urejanje kode. Visual C# nam je že zgradil ogrodje potrebne metode, napisati moramo še njeno vsebino. Uporabili bomo switch stavek, s pomočjo katerega bomo testirali katero vrstico je uporabnik kliknil. Celotna metoda, skupaj s switch stavkom naj bo takale: //Metoda gradnika ListBox private void type_SelectedIndexChanged(object sender, System.EventArgs e) { switch (lbTip.Text) //testiramo tekst v vrstici ki { //jo je kliknil uporanik case "int": int var; var = 42; lVrednost.Text = Convert.ToString(var); break; case "long": long var1; var1 = 42L; lVrednost.Text = Convert.ToString(var1);

Page 27: PROŠNJA - uranic.tsckr.siuranic.tsckr.si/VISUAL C#/VISUAL C#.pdf · MICROSOFT VISUAL C# .NET Srečo Uranič . Visual C# . ... Za začetek novega projekta v okolju Visual C# Standard

Visual C# . NET

Algoritmi in programski jeziki – 4.letnik

27

break; case "float": float var2; var2 = 0.42F; lVrednost.Text = Convert.ToString(var2); break; case "double": double var3; var3 = 0.42; lVrednost.Text = Convert.ToString(var3); break; case "decimal": decimal var4; var4 = 0.42M; lVrednost.Text = Convert.ToString(var4); break; case "string": string var5; var5 = "42"; lVrednost.Text = Convert.ToString(var5); break; case "char": char var6; var6 = '4'; lVrednost.Text = Convert.ToString(var6); break; case "bool": bool var7; var7 = false; lVrednost.Text = Convert.ToString(var7); break; } }

Switch stavek v C# se nekoliko razlikuje od switch stavka v C oz C++. Splošna oblika tega stavka je takale: switch (kontrolniIzraz) { case konstantniIzraz: stavki break; case konstantniIzraz: stavki break; . . . default stavki break; }

V kolikor kontrolniIzraz ni izpolnjen v nobeni case veji, se bodo izvedli stavki v veji default. Switch stavek je zelo uporaben, a pri njegovi implementaciji se moramo držati naslednjih pravil:

• Stavek switch lahko uporabimo za testiranje le osnovnih podatkovnih tipov, npr. int, string, char,..; • V posamezni case veji lahko uporabimo le konstantne vrednosti, kot npr. 42, ali pa "42". Če je

potrebno vrednost v case vejah šele izračunati, moramo uporabiti if stavek; • Za razliko od C in C++ je potrebno v vsaki case veji zapisati vse potrebne stavke. Prav tako je v vsaki

veji stavek break obvezen, tudi v bloku default; Napišimo še vsebino metode, ki naj se izvede, ko uporabnik klikne gumb za konec programa. Preklopimo na oblikovni pogled (Design View) in dvokliknimo na gumb bKonec. Visual C# nam ponudi ogrodje metode, ki se bo izvedla, ko bo uporabnik kliknil na ta gumb. Celotna metoda, naj bo takale (dopišimo vsebino!). private void quit_Click(object sender, System.EventArgs e)

Page 28: PROŠNJA - uranic.tsckr.siuranic.tsckr.si/VISUAL C#/VISUAL C#.pdf · MICROSOFT VISUAL C# .NET Srečo Uranič . Visual C# . ... Za začetek novega projekta v okolju Visual C# Standard

Visual C# . NET

Algoritmi in programski jeziki – 4.letnik

28

{ Application.Exit(); //zaključek programa }

Operatorji Večino operatorjev že poznamo, osnovni med njimi pa so regularni matematični operatorji + (seštevanje), - (odštevanje), * (množenje) / (deljenje) in % (ostanek pri deljenju). Operator + lahko uporabljamo tudi za sestavljanje besed – stringov. V C# uporabljamo za delo z besedami (stringi) večinoma podatkovni tip string. Pri uporabi operatorjev moramo upoštevati prednosti operatorjev ( npr * in / pred + in - , tako kot je to v matematiki). Vrstni red pa lahko seveda spremenimo s pomočjo okroglih oklepajev! Primer: string beseda; beseda ="Danes"; //Dve spremenljivki tipa string lahko sestavimo s pomočjo operatorja + beseda = beseda + " je lep dan!";

Pretvarjanje osnovnih podatkovnih tipov Pri delu z vnosnimi polji oz. pri delu z gradniki na obrazcih se velikokrat srečamo s potrebo po pretvarjanju osnovnih podatkovnih tipov (npr. zaporedja znakov v celo število ali realno število, znaka v cifro, celo število v string, …). Najpogosteje za pretvarjanje uporabljamo razred Convert, ki vsebuje celo vrsto metod za pretvarjanje. Omenimo le nekaj najpomembnejših:

• ToInt32 – pretvorba določene vrednosti v 32 bitno predznačeno (signed) celo število • ToInt64 – pretvorba določene vrednosti v 64 bitno predznačeno (signed) celo število • ToByte – pretvorba v 8-bitno nepredznačeno celo število • ToDouble – pretvorna določene vrednosti v vrednost tipa double • ToDecimal – pretvorba v decimalno število • ToSingle – pretvorba v spremenljivko tipa float • ToChar – pretvorba v znakovno vrednost • ToBoolean – pretvorba v logično vrednost • ToString – pretvorba v zaporedje znakov - string

Primeri uporabe: int celo_stevilo = Convert.ToInt32("12345"); long veliko_celo = Convert.ToInt64("123456789"); float realno = Convert.ToSingle("12.89"); ;//namesto decimalne pike lahko pišemo vejico!!! double veliko_stevilo = Convert.ToDouble("250000.89"); char znak = Convert.ToChar(65); bool logicna = Convert.ToBoolean(1); double vsota = 100.22 + 200.33; string stavek = Convert.ToString(vsota);

Standardne oznake/kode za formatiranje števil V naslednji tabeli so prikazane standardne kode (oznake - šifre), ki jih uporabimo za formatiranje števil, kadar jih želimo z metodo ToString spremeniti v zaporedje znakov – string. Katerokoli od teh kod (znakov) lahko uporabimo znotraj oklepajev metode ToString, obvezno pa jih moramo zapisati med dvojna narekovaja.

Page 29: PROŠNJA - uranic.tsckr.siuranic.tsckr.si/VISUAL C#/VISUAL C#.pdf · MICROSOFT VISUAL C# .NET Srečo Uranič . Visual C# . ... Za začetek novega projekta v okolju Visual C# Standard

Visual C# . NET

Algoritmi in programski jeziki – 4.letnik

29

Oznaka Format Opis

C ali c Denarna valuta Formatiranje števila kot denarno valuto z določenim številom decimalnih mest.

P ali p Procent Formatiranje števila kot procent, z določenim številom decimalnih mest.

N ali n Številka Formatiranje števila z ločilom za tisočice in določenim število decimalnih mest.

F ali f Float Formatiranje števila kot decimalnega števila, z določenim število decimalnih mest.

D ali d Cifre Formatiranje celega števila z določenim število cifer.

E ali e Eksponent Formatiranje števila v eksponentni obliki z določenim število decimalnih mest.

G ali g Splošno Formatiranje števila v decimalni ali eksponentni obliki odvosno od tega, katera oblika je bolj primerna.

Nekaj primerov: decimal znesek=1547.20m; string noviznesek=znesek.ToString("c"); //Rezultat: €1.547,20 decimal obresti=0.023m; string obrestnamera=obresti.ToString("p1");//v stringu bo zapisano eno decimalno mesto, //Rezultat: 2,3% float vrednost = 15000; string novavrednost = vrednost.ToString("n0");//v stringu bo zapisano 0 decimalnih mest //Rezultat: 15.000 decimal skupaj=432818.678m; string vseskupaj = skupaj.ToString("f2");//v stringu bosta zapisani 2 decimalni mesti, število //bo zaokroženo. Rezultat: 432818,68 decimal cena = 145345.23m; string novacena = vrednost.ToString("n2");//izpis bo formatiran na tisočice, v stringu bosta //zapisani 2 decimalni mesti. Rezultat: 145.345,23

Pri formatiranju števil si lahko pomagamo tudi z metodo Format razred String (pozor, String z veliko začetnico!!!). Nekaj primerov; //Z metodo Format lahko formatiramo lahko več števil hkrati. Zapis 0:c pomeni, da se bo prvo število (oznaka 0) formatiralo s šifro c string novacena = String.Format("{0:c}", 1547.2m); //Rezultat 1.547,20 € //Zapis 0:c pomeni, da se bo prvo število (oznaka 0) formatiralo s šifro c, zapis 1:p pa //pomeni, da se bo drugoo število (oznaka 1) formatiralo s šifro p – torej kot procent //Rezultat naslednjega formata: Znesek: 1.547,20 €, Procent: 12,00% string novacena = String.Format("Znesek: {0:c}, Procent: {1:p}", 1547.2m,0.12); string obrestnamera = String.Format("{0:p1}", .023m); //Rezultat 2,3% string novavrednost=String.Format("{0:n0}",15000); //Rezultat 15.000 string vseskupaj = String.Format("{0:f3}", 432.8175); //Rezultat 432,818

Običajne (custom) oznake/kode za formatiranje števil Če nam standardno formatiranje števil ne zadošča za oblikovanje formata, ki ga želimo, lahko kreiramo svoj lasten format. Pri takem formatiranju uporabimo običajne (custom) oznake za formatiranje.

Page 30: PROŠNJA - uranic.tsckr.siuranic.tsckr.si/VISUAL C#/VISUAL C#.pdf · MICROSOFT VISUAL C# .NET Srečo Uranič . Visual C# . ... Za začetek novega projekta v okolju Visual C# Standard

Visual C# . NET

Algoritmi in programski jeziki – 4.letnik

30

Tako v metodi ToString, kot v metodi Format lahko z uporabo treh sekcij, ki so med seboj ločene z znakom podpičje določimo, kako naj bo formatirano pozitivno število, kako negativno število in kako naj bo formatirano število 0. Seznam običajnih oznak (kod) za formatiranje števil;

Oznaka Pomen

0 Mesto rezervirano za cifro 0.

# Mesto rezervirano za cifro.

. Decimalna pika.

, Decimalno ločilo.

% Mesto rezervirano za znak procent.

; Ločilo posamezne sekcije.

decimal znesek = -1547.20m; //negativno število string znesekS=znesek.ToString("€#,##0.00"); //Rezultat -€1.547,20 string znesekS1=znesek.ToString(" €#,##0.00"); //Rezultat - €1.547,20 znesek = 1547.20m; //pozitivno število //PRIKAZ SEKCIJ v formatiranju //če je število, ki ga formatiramo pozitivno, se upošteva prvi format (prva sekcija), če je //negativno druga sekcija, cicer pa tretja sekcija. Ločilo za sekcije je znak ";"; string znesekS1 = znesek.ToString("€#,##0.00;(-#,##0.00);Nič");//Rezultat: €1.547,20 string znesekS2 = String.Format("{0:€#,##0.00;(-#,##0.00);Nič}",znesek); //Rezultat: €1.547,20 decimal znesek1 = -1547.20m; //negativno število string znesekS3 = znesek1.ToString("€#,##0.00;(-#,##0.00);Nič");//Rezultat: (-1.547,20) string znesekS4 = String.Format("{0:€#,##0.00;(-#,##0.00);Nič}", znesek1); //Rezultat: (-1.547,20) //Rezultat naslednjega formata: Znesek: €1.547,20, Procent: 12,00% string novacena1 = String.Format("Znesek: {0:€#,##0.00}, Procent: {1:p}", 1547.2m, 0.12); decimal znesek2 = 0; string znesekS5 = znesek2.ToString("€#,##0.00;(#,##0.00);Nič"); //Rezultat: Nič

Vaja: Uporabo osnovnih matematičnih operatorjev in pretvarjanje med njimi bomo sedaj prikazali z naslednjo vajo. Kreirajmo nov projekt z imenom Aritmeticni_Opetarorji in na obrazec postavimo gradnike, tako kot prikazuje naslednja slika: Gradnika tipa Label

Gradnika tipa TextBox GroupBox RadioButton (radijski gumb) TextBox( lastnost ReadOnly=True)

Gradnika tipa Button

Page 31: PROŠNJA - uranic.tsckr.siuranic.tsckr.si/VISUAL C#/VISUAL C#.pdf · MICROSOFT VISUAL C# .NET Srečo Uranič . Visual C# . ... Za začetek novega projekta v okolju Visual C# Standard

Visual C# . NET

Algoritmi in programski jeziki – 4.letnik

31

Prvemu izmed petih radijskih gumbov ( gumbu z napisom + vsota) spremenimo lastnost Checked v True – v njem se pokaže pikica (gumb je izbran!). Napisati moramo še nekaj metod:

• Gumb Zapri ima funkcijo zapiranja projekta (poznamo že iz prejšnje vaje) • Ob kliku na gumb Izračunaj pa naj se zgodi dogodek Click tega gumba: private void button1_Click(object sender, EventArgs e) { int levo, desno;//spremenljivki označujeta števili v levem in desnem vnosnem polju float rezultat = 0; //spremenljivka, v katero bomo izračunali rezultat levo = Convert.ToInt32(textBox1.Text); //levo vnosno polja spremenimo v celo število desno = Convert.ToInt32(textBox2.Text);//desno vnosno polja sprem. v celo število if (radioButton1.Checked) //z if stavkom preverimo, kateri od gumbov je bil kliknjen { textBox3.Text = textBox1.Text+" + "+textBox2.Text; rezultat = levo + desno; } else if (radioButton2.Checked) { textBox3.Text = textBox1.Text + " - " + textBox2.Text; rezultat = levo - desno; } else if (radioButton3.Checked) { textBox3.Text = textBox1.Text + " * " + textBox2.Text; rezultat = levo * desno; } else if (radioButton4.Checked) { if (desno != 0) { textBox3.Text = textBox1.Text + " / " + textBox2.Text; rezultat = levo / desno; } else MessageBox.Show("Deljenje z 0 je prepovedano!"); //sporočilno okno } else { textBox3.Text = textBox1.Text + " % " + textBox2.Text; rezultat = levo % desno; } textBox4.Text = Convert.ToString(rezultat); //rezultat pretvorimo v string

}

• Ob kliku na katerikoli radijski gumb pa naj se zgodi dogodek s stavkoma, ki pobrišeta dosedanji vsebini

gradnikov textBox3 (prikaz operandov in operacije) in textBox4 (prikaz rezultata).

private void radioButton1_Click(object sender, EventArgs e) { textBox3.Text = ""; //prazen string = pobrišemo dosedanjo vsebino textBox4.Text = ""; }

POZOR: dogodek, ki se zgodi ob kliku na radijski gumb ne pišemo za vsak radijski gumb posebej, ampak le za prvi gumb, pri vseh ostalih pa v okni Properties -> Dogodki -> Click na desni strani izberemo že napisan dogodek, saj je ta povsem enak za vseh pet radijskih gumbov.

Vaja: Kreirajmo projekt, v katerem bo prikazana uporaba switch stavka, formariranje in zaokroževanje. Prikazana je tudi uporaba gradnika ComboBox.

Page 32: PROŠNJA - uranic.tsckr.siuranic.tsckr.si/VISUAL C#/VISUAL C#.pdf · MICROSOFT VISUAL C# .NET Srečo Uranič . Visual C# . ... Za začetek novega projekta v okolju Visual C# Standard

Visual C# . NET

Algoritmi in programski jeziki – 4.letnik

32

ComboBox (ime gradnika cBVrsta). Odprimo lastnost Items (klik na tripičje) in vnesimo vrednosti (npr R, C, Ostali – eno pod drugo) Gradniki TextEdit (imena tBZnesek, tBProcent, tBZnesekPopusta in tBZneseksPopustom)

Gumbu izračunaj priredimo dogodek Click; private void bIzracunaj_Click(object sender, EventArgs e) { //znesek vnešen v tekstovno polje prenesemo v spremenljivko decimal znesek = Convert.ToDecimal(tBZnesek.Text); //procent popusta je odvisen od velikosti zneska decimal procent = 0m;//znak m je oznaka da gre za denarni znesek - money //vrsto stranke dobimo iz gradnika ComboBox string vrstastranke=cBVrsta.Text; //ugotovimo procent popusta glede na vrsto stranke in znesek switch (vrstastranke) { case "R": if (znesek >= 500) { procent = .2m; //popust je 20% } else if (znesek >= 250 && znesek < 500) { procent = .15m; //popust je 15% } else if (znesek >= 100 && znesek < 250) { procent = .1m; //popust je 10% } break; case "C": procent=.2m; //popust je 20% break; default: procent=.05m; //popust je 5% break; } //izračun in prirejanje vrednosti za znesekpopusta in znesek s popustom decimal znesekpopusta = znesek * procent; //Lahko uporabimo tudi metodo za zaokroževanje, ki pripada razredu Math //Zaokrožimo na 2 decimalki: decimal znesekpopusta = Math.Round(znesek * procent, 2); decimal znesekspopustom = znesek - znesekpopusta; //formatiranje vrednosti in izpis v njihovih tekstovnih poljih //oznaka p1 pomeni, da bo ob tekstu izpisam še znak % tBProcent.Text = procent.ToString("p1"); //oznaka c pomeni, da bo ob tekstu izpisana v Windowsih nastavljena valuta tBZnesekPopusta.Text = znesekpopusta.ToString("c"); tBZneseksPopustom.Text = znesekspopustom.ToString("c"); //žarišče prenesemo v tekstovno polje tBZnesek tBZnesek.Focus(); }

Ko uporabnik spremeni vrsto stranke, moramo pobrisati dosedanje izračunane vrednosti. Za to poskrbi dogodek TextChanged, ki se zgodi, ko uporabnik spremeni tekst v gradniku ComboBox.

Page 33: PROŠNJA - uranic.tsckr.siuranic.tsckr.si/VISUAL C#/VISUAL C#.pdf · MICROSOFT VISUAL C# .NET Srečo Uranič . Visual C# . ... Za začetek novega projekta v okolju Visual C# Standard

Visual C# . NET

Algoritmi in programski jeziki – 4.letnik

33

private void cBVrsta_TextChanged(object sender, EventArgs e) { tBProcent.Text = ""; tBZnesekPopusta.Text = ""; tBZneseksPopustom.Text = ""; }

Formatiranje objektov tipa DateTime Za delo z datumi in časi C# uporablja strukturo DateTime. Več o tem tipu in njegovi uporabi bomo zvedeli v poglavju o dogodku ki ga proži ura. Na tem mestu si oglejmo kako lahko objekte tipa DateTime formatiramo oz. prikažemo v obliki stringa. Za formatiranje datuma in časa uporabljamo metodo Format razreda String. Metoda deluje podobno kot pri formatiranju števil, pri čemer pa moramo seveda uporabiti standardne ali pa običajne šifre za objekte tipa DateTime. Tabela standardnih šifer za zapis objektov tipa DateTime:

Šifra / Koda Opis

d Kratka oblika datum.

D Dolga oblika datuma.

t Kratka oblika zapisa časa.

T Dolga oblika zapisa časa.

f Dolga oblika zapisa datuma, kratka oblika zapisa časa.

F Dolga oblika zapisa datuma, dolga oblika zapisa časa.

g Kratka oblika zapisa datuma, kratka oblika zapisa časa.

G Kratka oblika zapisa datuma, dolga oblika zapisa časa.

Tabela običajnih šifer za zapis objektov tipa DateTime:

Šifra / Koda Opis

d Dan v mesecu brez vodilnih ničel.

dd Dan v mesecu z vodilnimi ničlami.

ddd Skrajšano ime dneva.

dddd Polno ime dneva.

M Mesec brez vodilne ničle.

MM Mesec z vodilno ničlo .

MMM Skrajšano ime meseca.

MMMM Polno ime meseca.

Page 34: PROŠNJA - uranic.tsckr.siuranic.tsckr.si/VISUAL C#/VISUAL C#.pdf · MICROSOFT VISUAL C# .NET Srečo Uranič . Visual C# . ... Za začetek novega projekta v okolju Visual C# Standard

Visual C# . NET

Algoritmi in programski jeziki – 4.letnik

34

y Leto zapisano z dvema ciframa brez vodilne ničle .

yy Leto zapisano z dvema ciframa z vodilni ničlo.

yyyy Leto zapisano z vsemi štirimi ciframi.

/ Datumsko ločilo.

h Ure brez vodilne ničle.

hh Ure z vodilno ničlo.

H Ura pri 24 urni uri brez vodilnih ničel.

HH Ura pri 24 urni uri z vodilnimi ničlami.

m Minute brez vodilnih ničel.

mm Minute z vodilnimi ničlami.

s Sekunde brez vodilnih ničel.

ss Sekunde z vodilnimi ničlami.

f Deli sekunde (en znak f za vsako decimalno mesto).

t Prvi karakter za del dneva (dopoldan/popoldan).

tt Popolni označevalec dela dneva (dopoldan/popoldan).

: Časovno ločilo.

Nekaj primerov: //recimo da je današnji datum 20. avgust, ura pa 13, 55 minut in 25 sekund DateTime datum = DateTime.Now; string danasnji = datum.ToString("d");Rezutat: 20.8.2007 string danasnji1 = datum.ToString("D"); Rezutat: 20.avgust 2007 string danasnji2 = datum.ToString("t");//Rezutat: 13:55 string danasnji3 = datum.ToString("T");;//Rezutat: 13:55:25 string danasnji4 = datum.ToString("ddd,MMM d,yyyy");//Rezutat : pon, avg 20,2007 string danasnji5 = datum.ToString("M/d/yy");//Rezutat: 8.20.07 string danasnji6 = datum.ToString("HH/mm/ss");//Rezutat: 13.55.25

Zanka foreach Zanka foreach je zanka, v kateri se ponavlja množica stavkov za vsak element neke tabele ali množice objektov. Zanke ne moremo uporabiti za spremembo elementov tabele, po kateri se sprehajamo, oz. za spremembo objektov. Na začetku foreach zanke je deklarirana spremenljivka poljubnega tipa, ki avtomatično pridobi vrednost posameznega elementa neke tabele, zaradi česar ima ta oblika zanke prednost pred klasičnim for stavkom za obdelavo neke tabele. Uporabimo jo lahko le za obdelavo cele tabele, ne pa tudi za obdelavo le njenega dela. Iteracija poteka vedno od indeksa 0 do indeksa Length -1, iteracija v obratni smeri pa &I možna. Splošna oblika foreach zanke: foreach ( tip spremenljivka in tabela) { ..stavki.. }

Page 35: PROŠNJA - uranic.tsckr.siuranic.tsckr.si/VISUAL C#/VISUAL C#.pdf · MICROSOFT VISUAL C# .NET Srečo Uranič . Visual C# . ... Za začetek novega projekta v okolju Visual C# Standard

Visual C# . NET

Algoritmi in programski jeziki – 4.letnik

35

Vaja: Kreirajmo projekt z imenom Foreach_DEMO, v katerem bomo prikazali nekaj primerov uporabe te zanke. V primeru bomo uporabili tudi gradnik MenuStrip, s katerim bomo kreirali preprosti meni. Gradnik MenuStrip se nahaja v oknu Toolbox v skupini gradnikov Menus & Toolbars. To je nevizuelni gradnik: ko ga želimo postaviti na obrazec se namesti v polje pod obrazcem, na vrhu obrazca pa se pokaže začetno stanje menija. Z oblikovanjem menija pričnemo tako, da kliknemo v okno menija z napisom Type Here in zapišemo tekst, ki ga želimo imeti v meniju. Tekst menija nato po želji dodajamo v horizontalni ali pa v vertikalni smeri. Urejevalnik menija je videti kot pravi meni, le da ima nakazana prazna mesta, kamor je mogoče pisati nove postavke. Več o menijih in o sestavljenih menijih bo prikazano v posebnem poglavju. Obrazec naj bo takle:

TextBox (lastnost Dock = fill)

S pomočjo menija so prikazani trije primeri uporabe zanke foreach. Koda, ki ustreza posameznim opcijam menija:

• Obdelava enodimenzionalne tabele celih števil:

private void obdelavaTabeleToolStripMenuItem_Click(object sender, EventArgs e) { //dinamično kreiranje tabele 10 celih števil int[] tab = new int[10]; Random Nakljucno_Celo = new Random(); //napolnimo jo z naključnimi vrednostmi for (int i = 0; i < 10; i++) tab[i] = Nakljucno_Celo.Next(100); textBox1.Clear(); textBox1.Text = "Enodimenzionalna Tabela celih števil - izpis z zanko foreach"; foreach (int stevilo in tab) textBox1.Text = textBox1.Text + " "+stevilo; }

• Obdelava dvodimenzionalne tabele celih števil:

private void obdelavaDvodimenzionalneTabeleCelihŠtevilToolStripMenuItem_Click(object sender, EventArgs e) { //dinamično kreiranje tabele 10x10 celih števil int[,] tab = new int[10,10]; Random Nakljucno_Celo = new Random(); //napolnimo jo z naključnimi vrednostmi for (int i = 0; i < 10; i++) for (int j = 0; j < 10; j++) tab[i,j] = Nakljucno_Celo.Next(100); textBox1.Clear(); textBox1.Text = "Dvodimenzionalna tabela celih števil - izpis z zanko foreach:"; int k = 0; foreach (int stevilo in tab)

Page 36: PROŠNJA - uranic.tsckr.siuranic.tsckr.si/VISUAL C#/VISUAL C#.pdf · MICROSOFT VISUAL C# .NET Srečo Uranič . Visual C# . ... Za začetek novega projekta v okolju Visual C# Standard

Visual C# . NET

Algoritmi in programski jeziki – 4.letnik

36

{ textBox1.Text = textBox1.Text + " "+stevilo.ToString("000"); k++; if ((k % 10) == 0) textBox1.Text = textBox1.Text + "\r\n";//"\r\n" pomeni prehod v novo vrstico } }

• Obdelava tabele stringov:

private void obdelavaTabeleStringovToolStripMenuItem_Click(object sender,EventArgs e) { //dinamično kreiranje in takojšnja inicializacija tabele stringov string[] tab= {"Peter", "Andrej", "Maja", "Jana", "Anja", "Petra", "Marko"}; textBox1.Clear(); textBox1.Text = "Tabela stringov - izpis z zanko foreach:"; foreach( string ime in tab) textBox1.Text=textBox1.Text+" "+ime+"\r\n"; }

Vaja: V naslednjem projektu bomo prikazali nekaj primerov deklaracije in uporabe enodimenzionalnih in dvodimenzionalnih tabel različnih tipov. Na obrazec postavimo glavni meni (gradnik MenuStrip) in ga oblikujmo takole:

Nevizuelni gradnik MenuStrip

Koda, ki pripada posameznim opcijam menija pa je takale: private void tabelaRealnihŠtevilToolStripMenuItem_Click(object sender, EventArgs e) { decimal[] stevila = new decimal[10]; string stringstevilo=""; Random naklj = new Random(); for (int i = 0; i < 10; i++) { //generiranje realnega števila med 0 in 1, zaokroženega na 3 decimalke stevila[i] = Math.Round((decimal)naklj.NextDouble(),3); stringstevilo+=stevila[i].ToString()+" "; } //Metoda GetLength vrne ustrezno dimenzijo tabele //tabela.GetLength(0) = število vrstic //tabela.GetLength(1) = število stolpcev - pri dvodimenzionalnih tabelah MessageBox.Show("Tabela 10 naključnih realnih števil (uporabili smo for zanko)

:\n\n"+stringstevilo+"\nDimenzija tabele=" +Convert.ToInt32(stevila.GetLength(0))); } private void tabelaRealnihŠtevilforeachZankaToolStripMenuItem_Click(object sender,EventArgs e)

Page 37: PROŠNJA - uranic.tsckr.siuranic.tsckr.si/VISUAL C#/VISUAL C#.pdf · MICROSOFT VISUAL C# .NET Srečo Uranič . Visual C# . ... Za začetek novega projekta v okolju Visual C# Standard

Visual C# . NET

Algoritmi in programski jeziki – 4.letnik

37

{ decimal[] stevila = new decimal[10]; string stringstevilo = ""; Random naklj = new Random(); for (int i = 0; i < 10; i++) { //generiranje realnega števila med 0 in 1, zaokroženega na 3 decimalke stevila[i] = Math.Round((decimal)naklj.NextDouble(), 3); } //še primer uporabe foreach zanke foreach (decimal stevilo in stevila) { stringstevilo += stevilo.ToString() + " "; } MessageBox.Show("Tabela 10 naključnih realnih števil (uporabili smo foreach zanko):\n\n" + stringstevilo); } private void tabelaNaključnihCelihŠtevilToolStripMenuItem_Click(object sender, EventArgs e) { int[,] cela = new int[10, 5]; string stringcela = ""; Random naklj = new Random(); for (int i = 0; i < 10; i++) for (int j = 0; j < 5; j++) { cela[i, j] = naklj.Next(100); stringcela += cela[i, j].ToString("0#") + " "; if (j == 4) stringcela += "\n"; } MessageBox.Show("Dvodimenzionalna tabela 10 naključnih celih števil med 0 in 100):\n\n" + stringcela + "\nVrstic= " + Convert.ToInt32(cela.GetLength(0))+",Stolpcev="+Convert.ToInt32(cela.GetLength(1))); } private void tabelaStringovToolStripMenuItem_Click(object sender, EventArgs e) { string vseknjige=""; string [,] knjige ={ {"VASP","ASP.NET Programming"}, {"JAVA","Beginning Java"}, {"C#","Visual Studio. NET"}}; for (int i = 0; i < knjige.GetLength(0); i++) { for (int j = 0; j < knjige.GetLength(1); j++) vseknjige += knjige[i, j].ToString()+"\t";//\t je tabulator!! vseknjige += "\n"; } MessageBox.Show("Seznam vseh knjig: \n\n" + vseknjige); } private void tabelaCelihŠtevilToolStripMenuItem_Click(object sender, EventArgs e) { int[] stevila ={ 1, 2, 3, 4, 5 }; string stringcela = ""; for (int i = 0; i < stevila.GetLength(0); i++) stringcela += stevila[i].ToString("#") + " "; MessageBox.Show("Enodimenzionalna tabela 5 celih števil:\n\n" + stringcela ); } private void tabelaStringovToolStripMenuItem1_Click(object sender, EventArgs e) { string[] imena ={ "Polde", "Maja", "Anja", "Tine", "Franci" }; string vsaimena = ""; Array.Sort(imena);//uredimo (sortiramo) seznam imen; for (int i = 0; i < imena.GetLength(0); i++) vsaimena += imena[i].ToString() + "\n"; vsaimena += "\n\n Še foreach zanka:\n\n"; foreach (string ime in imena) vsaimena += ime + "\n"; MessageBox.Show("Seznam vseh knjig: \n\n" + vsaimena); }

Page 38: PROŠNJA - uranic.tsckr.siuranic.tsckr.si/VISUAL C#/VISUAL C#.pdf · MICROSOFT VISUAL C# .NET Srečo Uranič . Visual C# . ... Za začetek novega projekta v okolju Visual C# Standard

Visual C# . NET

Algoritmi in programski jeziki – 4.letnik

38

Rezervirana beseda params Visual C# omogoča kreiranje metode, ki ji lahko za parameter posredujemo poljubno število parametrov (npr. elemente neke tabele), ki jih potem lahko v metodi obdelamo npr. s pomočjo zanke foreach. Rezervirana beseda params omogoča, da je število takih elementov v fazi načrtovanja metode še nedoločeno. Primer: //v glavi metode uporabimo rezervirano besedo params public static void obdelajTabelo(params int[] poljubnaTabelaCelihStevil) { int vsota = 0; foreach (int i in poljubnaTabelaCelihStevil) { vsota = vsota + i; } }

//klic metode obdelajTabelo – metodi posredujemo poljubno število parametrov obdelajTabelo(7, 8, 9, 10); //število parametrov je lahko poljubno, ker smo pri definiciji v //glavi metode uporabili rezervirano besedo params //deklaracija in inicilaizacija tabele 5 celih števil int[] tabelaCelihStevil = new int[5] { 1, 2, 3, 4, 5 };

//klic metode obdelajTabelo – metodi posredujemo tabelo celih števil obdelajTabelo(tabelaCelihStevil);

Page 39: PROŠNJA - uranic.tsckr.siuranic.tsckr.si/VISUAL C#/VISUAL C#.pdf · MICROSOFT VISUAL C# .NET Srečo Uranič . Visual C# . ... Za začetek novega projekta v okolju Visual C# Standard

Visual C# . NET

Algoritmi in programski jeziki – 4.letnik

39

Delo z zbirkami – Collections (ponovitev) Podobno kot tabele, je tudi zbirka (collection) objekt, ki lahko vsebuje enega ali pa več elementov. Za razliko od tabel, kjer moramo že ob definiciji napovedati njeno dolžino, pa zbirka &IMA fiksne dolžine. Zbirke so torej lahko spremenljive dolžine. Zbirke so lahko netipizirane (vsebujejo elemente različnih tipov) ali pa tipizirane: vsi elementi zbirke so enakega tipa.

&etipizirane zbirke Pred deklaracijo netipizirane zbirke moramo poskrbeti za vključitev ustreznega imenskega prostora using System.Collections;

Splošna deklaracija zbirke: ArrayList ime_zbirke = new ArrayList(); Elemente dodajamo v zbirko s pomočjo metode Add(). Ime_zbirke.Add(7); //v zbirko smo dodali nov element – tip elementa ni pomemben

Do posameznih elementov zbirke dostopamo preko indeksa (tako kot pri tabelaričnih spremenljivkah). Začetni indeks je enak 0! Za razliko od tabelaričnih spremenljivk pa preko indeksa elementa zbirke ni dovoljena kar poljubna aritmetika! Primer napačnega prirejanja in pravilnega prirejanja: Ime_zbirke[1]=Ime_zbirke[0]+100; //NAPAKA!!!!!!!! – aritmetični operatorji niso dovoljeni Ime_zbirke[1]=Ime_zbirke[0]; //OK Ime_zbirke[1]= "Danes je lep dan! "; //OK

Primer: ArrayList stevila = new ArrayList(); stevila.Add(3); stevila.Add(7); stevila.Add("test"); //prevajanje normalno, a pri uporabi v nadaljevanju (for zanka) pride do //napake int vsota=0; //Count je metoda zbirke, ki vrne trenutno število elementov zbirke for (int i=0;i<stevila.Count;i++) { int stevilo=(int)stevila[i];//potrebna eksplicitna konverzija (int) vsota+=stevilo; }

Vaja: /*Dan je poljuben stavek. Posamezne besede iz tega stavka prepišimo v zbirko z imenom besede, nato pa te besede izpiši s pomočjo foreach zanke - vsaka beseda v svoji vrstici */ ArrayList besede = new ArrayList(); string stavek = "Danes je pa res en lep dan."; //metoda split vse znake med dvema presledkoma shrani v ustrezen element objekta besede besede.AddRange(stavek.Split(' '));

Page 40: PROŠNJA - uranic.tsckr.siuranic.tsckr.si/VISUAL C#/VISUAL C#.pdf · MICROSOFT VISUAL C# .NET Srečo Uranič . Visual C# . ... Za začetek novega projekta v okolju Visual C# Standard

Visual C# . NET

Algoritmi in programski jeziki – 4.letnik

40

string vseBesede=""; foreach (string beseda in besede) //izpis posameznih besed, vsaka beseda v novi vrstici vseBesede= vseBesede+"\n"; MessageBox.Show(vseBesede);

Vaja: /*Kreiraj zbirko 1000 naključnih realnih števil med 0 in 1000 z dvema decimalkama. Napiši funkcijo, ki ugotovi in vrne nejvečje število v zbirki Napiši funkcijo, ki sešteje in vrne samo decimalke vseh števil v zbirki*/ //funkcija, ki ugotovi in izpiše največje število v zbirki static void najvecje(ArrayList stevila) { double najv=0; //Count je metoda zbirke, ki vrne trenutno število elementov zbirke for (int i = 0; i < stevila.Count;i++ ) if ((double)stevila[i] > najv) najv = (double)stevila[i]; //vsak element zbirke moramo pretvoriti v double } //funkcija, ki sešteje in vrne vsoto vseh decimalk zbirke static double vsotadec(ArrayList stevila) { double vsota = 0; for (int i = 0; i < stevila.Count; i++) /*decimalke posameznega števila dobimo tako, da od števila odštejemo njegov celi del (funkcija truncate vrne celi del števila)*/ //rezultat še zaokrožimo na dve decimalki vsota = vsota + Math.Round((double)stevila[i] - Math.Truncate((double)stevila[i]),2); return vsota; } static void Zbirka() { //POZOR - imenski prostor ->>>>> using System.Collections; ArrayList stevila = new ArrayList(); Random naklj = new Random(); for (int i=0;i<1000;i++) //v zbirko dodamo 1000 naključnih števil stevila.Add(Math.Round(naklj.NextDouble()*1000,2)); //klic funkcije, ki poišče največje število v zbirki najvecje(stevila); //klic funkcije, ki vrne vsoto decimalk zbirke MessageBox.Show("Vsota vseh decimalk zbirke je " + vsotadec(stevila)); }

Tipizirane zbirke Pred deklaracijo tipizirane zbirke moramo poskrbeti za vključitev ustreznega imenskega prostora using System.Collections.Generic;

Tipizirane zbirke imajo dve prednosti pred netipiziranimi. Prva je ta, da preverjajo tip vsakega elementa posebej že pri prevajanju, kar zagotavlja, da pri izvajanju programa ne pride do napak tipa Run Time Error. Druga prednost pa je seveda ta, da se na ta način zmanjša čas potreben za pretvarjanje podatkov iz zbirke (casting). Tabela najpogosteje uporabljenih zbirk:

Zbirka Razlaga

List<T> Zbirka uporablja indeks za dostop do elementov, podobno kot tabela. Zelo je uporabna pri sekvenčnem dostopu do elementov zbirke. Lahko pa je neučinkovita pri vstavljanju elementov na sredino zbirke.

SortedList<K, V> Uporablja ključ za pridobivanje vrednosti. Ključ je lahko poljuben objekt. Zbirka je lahko neučinkovita pri sekvenčnem dostopanju do posameznih elementov zbirke, je pa

Page 41: PROŠNJA - uranic.tsckr.siuranic.tsckr.si/VISUAL C#/VISUAL C#.pdf · MICROSOFT VISUAL C# .NET Srečo Uranič . Visual C# . ... Za začetek novega projekta v okolju Visual C# Standard

Visual C# . NET

Algoritmi in programski jeziki – 4.letnik

41

zelo učinkovita pri vstavljanju novih elementov na sredino zbirke.

Queue<T> Uporablja metode za dodajanje in brisanje elementov.

Stack<T> Uporablja metode za dodajanje in brisanje elementov.

Primer: List<int> stevila = new List<int>();//deklaracija zbirke celih števil //v zbirko lahko dodajamo le cela števila, njihovo število je poljubno! stevila.Add(3); stevila.Add(5); //stevila.add("Test"); //prevajalnik bi tule javil napako! int vsota = 0; //Count je metoda zbirke, ki vrne trenutno število elementov zbirke for (int i = 0; i < stevila.Count; i++) { int stevilo = stevila[i];//eksplicitna konverzija NI potrebna vsota += stevilo; }

Najpogosteje uporabljena zbirka je zbirka List. Nekaj primerov deklaracije takšnih zbirk: //zbirka stringov List <string> naslovi=new List<string>(); //zbirka decimalnih števil List<decimal> cene = new List<decimal>(); //zbirka treh stringov. Seveda pa to ne pomeni, da stringov ne more biti več. A vsaka //prekoračitev navedene dimenzije podvoji kapaciteto seznama! List<string> kratice = new List<string>(3);

&ajpomembnejše lastnosti in metode zbirke List

Indeks Razlaga

[indeks] Dostop do posameznega elementa zbirke. Indeks prvega elementa zbirje je tako kot pri tabelah enak 0.

Lastnost Razlaga

Capacity Nastavitev števila elementov ki jih lahko zbirka List hrani.

Count Pridobivanje števila elementov v zbirki.

Lastnost Razlaga

Add(objekt) Dodajanje elementa na konec seznama in vračanje njegovega indeksa.

Clear() Odstranitev vseh elementov iz seznama, lastnost Count postane enaka 0.

Contains(objekt) Logična vrednost, ki ponazarja, ali seznam vsebuje navedeni objekt.

Insert(indeks,objekt) Vrivanje elementa v seznam na mesto z navedenim indeksom.

Remove(objekt) Odstranitev prve pojavitve navedenega objekta iz seznama.

RemoveAt(indeks) Odstranitev elementa z navedenim indeksom iz seznama.

BinarySearch(objekt) Iskanje navedenega objekta v seznamu in vračanje njegovega indeksa.

Sort() Urejanje elementov seznama v naraščajočem zaporedju.

Primer: List<string> priimki = new List<string>(3); //seznam treh stringov priimki.Add("Ming"); priimki.Add("Lakovič");

Page 42: PROŠNJA - uranic.tsckr.siuranic.tsckr.si/VISUAL C#/VISUAL C#.pdf · MICROSOFT VISUAL C# .NET Srečo Uranič . Visual C# . ... Za začetek novega projekta v okolju Visual C# Standard

Visual C# . NET

Algoritmi in programski jeziki – 4.letnik

42

priimki.Add("Taylor"); priimki.Add("House");//kapaciteta se podvoji na 6 elementov priimki.Add("Menendez"); priimki.Add("Murach"); priimki.Add("Socrates");//kapaciteta se podvoji na 12 elementov //urejanje seznama priimki.Sort(); string vsiPriimki = ""; for (int i = 0; i < priimki.Count; i++) { vsiPriimki += priimki[i] + "\n"; } //urejen seznam vseh priimkov prikažemo v sporočilnem oknu MessageBox.Show(vsiPriimki);

Še nekaj primerov: //definicija tabele decimalnih števil decimal[] novecene ={3275.68m, 4378.12m, 54123.34m, 100.00m }; //deklaracija seznama decimalnih števil List<decimal> cene = new List<decimal>(); //v seznam dodamo vsa decimalna števila iz tabele novecene foreach (decimal d in novecene) cene.Add(d); decimal cena1 = cene[0];//dostop do prvega elementa zbirke cene.Insert(0, 2345.11m);//vstavljanje nove cene na začetek seznama cena1 = cene[0]; //cena1 dobi novo vrednost 2345.11 decimal cena2=cene[1]; //cena2 dobi vrednost 3275.68 cene.RemoveAt(1); //odstranimo DRUGI elemenet (prvi elemeni ima indeks 0!) iz seznama cena2 = cene[1]; ////cena2 dobi vrednost 4378.12 decimal x = 100.00m; //če v zbirki najdemo znesek 100.00, da odstranimo iz zbirke if (cene.Contains(x)) { cene.Remove(x); ("Znesek odstranjen iz seznama!"); }

Uporaba zbirk tipa vrsta (queue) in stack je podobna, a imata obe vrsti zbirk svoje posebnosti in zaradi tega nista tako pogosto uporabljeni.

Page 43: PROŠNJA - uranic.tsckr.siuranic.tsckr.si/VISUAL C#/VISUAL C#.pdf · MICROSOFT VISUAL C# .NET Srečo Uranič . Visual C# . ... Za začetek novega projekta v okolju Visual C# Standard

Visual C# . NET

Algoritmi in programski jeziki – 4.letnik

43

Varovalni bloki – obravnava napak in izjem (Exceptions) Tako kot v vsakdanjem življenju se tudi pri pisanju programov oz. pri njihovem delovanju pojavljajo napake. Nekateri razvijalci, predvsem začetniki, skušajo napake odkriti in popraviti tako, da definirajo eno ali več posebnih globalnih spremenljivk in potem spremljati njihove vrednosti, ter na ta način skušati odkriti napake. Taka rešitev je seveda napačna in se je moramo izogibati. Visual C#, pa tudi drugi objektno orientirani jeziki kot orodje za odkrivanje napak, pa tudi za izboljšanje delovanja uporabljajo t.i. izjeme (exceptions). Izjeme so torej objektno orientirana rešitev za obdelavo napak v programih. Idejna rešitev za obdelavo izjem je v tem, da ločimo kodo, ki predstavlja tok programa in kodo za obdelavo napak. Na ta način postaneta obe kodi lažji za razumevanje, saj se ne prepletata. Za obdelavo izjem pozna Visual C# naslednje bloke:

• blok za obravnavo prekinitev try…catch …, • večkratni varovalni blok try … catch … catch … • brezpogojni varovalni blok try … finally … .

Blok za obravnavo prekinitev: try … catch … Kodo, ki bi jo sicer napisali v delu programa ali pa npr. v neki metodi, zapišemo v varovalnem bloku try (blok je vsak del kode napisane med oklepajema { in } ). Drugi del začenja besedica catch in v bloku zapišemo enega ali več stavkov za obdelavo izjem oz. napak (t.i. catch handlers). Če katerikoli stavek znotraj bloka try povzroči izjemo (če pride do napake), se normalni tok izvajanja programa prekine (normalni tok izvajanja pomeni, da se posamezni stavki izvajajo od leve proti desni, stavki pa se izvajajo eden za drugim, od vrha do dna), program pa se nadaljuje v bloku catch, v katerem pa moramo seveda napako ustrezno obdelati. Primer:

običajna programska koda koda za obdelavo napake

Metode Convert.ToInt32(..), Convert.ToString(..) skušajo napraviti ustrezne pretvorbe iz stringa v celo število oz iz realnega števila v string, pri čemer pa seveda lahko pride do napake (npr. če je v vnosnih poljih textBox1 oz. textBox2 kakršenkoli znak, ki je različen od znakov '0' do '9'). Do napake lahko pride tudi v stavku, kjer je operacija za deljenje, saj se lahko zgodi, da je drugi operand enak 0. Varovalni blok to napako prestreže in izvede se stavek (oz. stavki) v bloku catch.

� POZOR: Če v stavkih znotraj bloka try pride do napake (izjeme), se program na tem mestu ne prekine, ampak se ostali stavki znotraj bloka try ne izvedejo, začnejo pa se izvajati stavki za obdelavo izjem (napak), ki so znotraj bloka catch. Če pa do napake v bloku try ne pride, se stavki v bloku catch NE izvedejo!

try { int levo = Convert.ToInt32(textBox1.Text); int desno = Convert.ToInt32(textBox2.Text); float rezultat = levo / desno; textBox4.Text = Convert.ToString(rezultat); } catch (System.Exception caught){ // Vrsto napake izpišemo v gradniku textBox textBox4.Text = caught.Message; }

Page 44: PROŠNJA - uranic.tsckr.siuranic.tsckr.si/VISUAL C#/VISUAL C#.pdf · MICROSOFT VISUAL C# .NET Srečo Uranič . Visual C# . ... Za začetek novega projekta v okolju Visual C# Standard

Visual C# . NET

Algoritmi in programski jeziki – 4.letnik

44

V zgornjem primeru je uporabljena kar splošna metoda za prestrezanje napake (System.Exception ); le-ta se odzove/odkrije na vsako izjemo (napako), tudi tako, ki se je morda sploh ne zavedamo. V takem primeru lahko blok catch uporabimo tudi brez parametrov takole: Seveda pa obstajajo tudi metode za obdelavo posameznih vrst napak: System.FormatException (to metodo uporabimo na primer za obdelavo uporabnikovih napačnih vnosov podatkov), System.DivideByZeroException, System.EArgument.Exception, …). Vsaka od teh metod vrne ustrezno obvestilo o napaki, to obvestilo pa smo v zgornjem primeru izpisali v gradniku TextBox. Obvestilo o napaki je v tem primeru v angleščini; v kolikor pa želimo uporabnikom ponuditi prijazna obvestila o napaki, jih je napisati posebej, npr. takole: Tak način je za začetnika tudi najbolj priporočljiv in v nadaljevanju ga bomo tudi največkrat uporabljali.

Večkratni varovalni blok za obravnavo prekinitev try … catch … catch … Različne napake seveda proizvedejo različne vrste izjem. Recimo, da imamo operacijo deljenja, pri kateri se lahko zgodi, da je drugi operand enak 0. Izjema, ki se pri tem zgodi, se imenuje DivideByZeroException. V takem primeru lahko napišemo večkratni catch blok, enega za drugim. Najprej ujamemo najnižji razred izjem, nazadnje pa najvišjega: try { int levo = Convert.ToInt32(textBox1.Text); int desno = Convert.ToInt32(textBox2.Text); float rezultat = levo / desno; textBox4.Text = Convert.ToString(rezultat); } catch (System.FormatException caught){ MessageBox.Show("Napaka pri pretvarjanju podatkov!!!"); } catch (System.DivideByZeroException caught){ MessageBox.Show("Napaka pri deljenju z 0"); }

� POZOR: Seznam vseh možnih izjem dobimo, če v Debug meniju kliknemo opcijo Exceptions.

try { int levo = Convert.ToInt32(textBox1.Text); int desno = Convert.ToInt32(textBox2.Text); float rezultat = levo / desno; textBox4.Text = Convert.ToString(rezultat); } catch (System.Exception caught){ MessageBox.Show("Napaka v podatkih!!!"); }

try { // stavki, kjer lahko pride do napake } catch { // obdelava napake . . . }

Page 45: PROŠNJA - uranic.tsckr.siuranic.tsckr.si/VISUAL C#/VISUAL C#.pdf · MICROSOFT VISUAL C# .NET Srečo Uranič . Visual C# . ... Za začetek novega projekta v okolju Visual C# Standard

Visual C# . NET

Algoritmi in programski jeziki – 4.letnik

45

Vaja: V naslednji vaji imamo vnosno polje, v katerega uporabnik vnese poljubno celo število. Ob kliku na gumb se posamezne cifre tega število izpisujejo v drugem vnosnem polju tako, kot to prikazuje slika na obrazcu. Ker uporabnik pri vnosu lahko napravi napako (lahko vnese nenumerične podatke, kar pomeni, da bo pri pretvorbi v številko prišlo do napake), bomo celotno kodo za pretvarjanje postavili v varovalni blok. Kreirajmo nov projekt z imenom VarovalniBloki in na obrazec postavimo gradnike, tako kot prikazuje naslednja slika:

TextBox: gradnika poimenujmo tbStevilo in tbKoraki Gumba na obrazcu poimenujmo bPrikazKorakov in bZakljucek

Ob kliku na gumb Prikaži posamezne korake sedaj priredimo dogodek z naslednjo kodo in ustreznim varovalnim blokom: private void bPrikazKorakov_Click(object sender, EventArgs e) { try { int celostevilo = Convert.ToInt32(tbStevilo.Text); tbKoraki.Text = ""; string tekoci = ""; do { //pretvorba posameznega vnesenega znaka-cifre v cifro kot število //glede na ASCII tabelo znakov //(int) '0' je ASCII koda za znak '0': (int) '0' = 48 int kodacifre = (int)'0' + celostevilo % 10; char znak = Convert.ToChar(kodacifre); tekoci = znak + tekoci; tbKoraki.Text += tekoci + "\r\n"; //nova vrstica celostevilo /= 10; } while (celostevilo != 0); } catch (System.OverflowException caught) //lahko tudi samo OverflowException caught { //vneseno število je preveliko MessageBox.Show("Napaka !"); } catch (System.Exception caught) //lahko tudi samo Exception caught { //napaka pri pretvarjanju števila v string MessageBox.Show("Napaka pri pretvorbi - poskusi znova!"); } }

V primeru smo uporabili do while stavek, ki ima povsem enako obliko, kot jo poznamo iz C++. Seveda pa bi večkratni varovalni blok lahko nadomestili z enim samim, splošnim varovalnim blokom:

Page 46: PROŠNJA - uranic.tsckr.siuranic.tsckr.si/VISUAL C#/VISUAL C#.pdf · MICROSOFT VISUAL C# .NET Srečo Uranič . Visual C# . ... Za začetek novega projekta v okolju Visual C# Standard

Visual C# . NET

Algoritmi in programski jeziki – 4.letnik

46

private void bPrikazKorakov_Click(object sender, EventArgs e) { try { int celostevilo = Convert.ToInt32(tbStevilo.Text); tbKoraki.Text = ""; string tekoci = ""; do { //pretvorba posameznega vnesenega znaka-cifre v cifro kot število //glede na ASCII tabelo znakov //(int) '0' je ASCII koda za znak '0': (int) '0' = 48 int kodacifre = (int)'0' + celostevilo % 10; char znak = Convert.ToChar(kodacifre); tekoci = znak + tekoci; tbKoraki.Text += tekoci + "\r\n"; //nova vrstica celostevilo /= 10; } while (celostevilo != 0); } catch (System.Exception caught) //splošni varovalni blok, lahko tudi samo catch { MessageBox.Show("Napaka!"); }

Hierarhija izjem Izjeme v C# imajo svojo hierarhijo. Najvišja je izjema System.Exception, ki ima še nekaj podizjem:

• Exception //najvišji razred izjem o SystemException

� OutOfMemoryException � IOException � &ullReferenceException

o ApplicationException Če pri snovanju programske rešitve ne vemo, do katere izjeme bi lahko prišlo, večinoma uporabimo kar System.Exception, ki vsebuje vse možne izjeme.

Brezpogojni varovalni blok try … finally … Stavki v bloku catch se izvedejo samo ob prekinitvi (napaki), sicer pa se ne izvedejo. Kadar pa za del kode želimo, da se izvede v vsakem primeru, uporabimo brezpogojni varovalni blok finally.

Programska koda, ki bi jo napisali tudi če ne bilo varovalnega bloka; Blok kode, ki se izvede vedno, ne glede ali je v zgornjem bloku prišlo do napake ali ne!

V praksi pogosto uporabljamo tudi kombinacijo obeh metod: blok za obravnavo prekinitev vgnezdimo v brezpogojni varovalni blok: try { // stavki, kjer lahko pride do napake } catch { // obdelava napake }

try { . . . } finally { . . . }

Page 47: PROŠNJA - uranic.tsckr.siuranic.tsckr.si/VISUAL C#/VISUAL C#.pdf · MICROSOFT VISUAL C# .NET Srečo Uranič . Visual C# . ... Za začetek novega projekta v okolju Visual C# Standard

Visual C# . NET

Algoritmi in programski jeziki – 4.letnik

47

finally {

// blok, ki se izvede vedno, ne glede na napako }

Uporaba rezerviranih besed Checked in Unchecked Pri računanju s celimi števili se lahko zgodi, da postane vrednost spremenljivke prevelika za tip int ali celo za tip long. V takem primeru ne dobimo obvestila o napaki (ne pride do izjeme), ampak je le rezultat napačen. Takim napakam se lahko izognemo tako, da eksplicitno navedemo, da želimo spremljati (obdelovati) tudi take napake. V tem primeru uporabimo besedico checked, oz. obratno unchecked. int stevilo = System.Int32.MaxValue; //največje možno celo število checked { int boNapaka = stevilo++; //IZJEMA - napaka MessageBox.Show("Tole sporočilno okno se ne bo nikoli izvedlo"); }

Lahko pa uporabimo blok unchecked in s tem stavki v bloku ne bodo aritmetično preverjani, kar pomeni, da v primeru prekoračitve maksimalne vrednosti celega števila ne bo prišlo do izjeme (napake). int stevilo = System.Int32.MaxValue; //največje možno celo število unchecked { int boNapaka = stevilo++; //semantična napaka, rezultat ne bo pravilen MessageBox.Show("Tole sporočilno okno se bo izvedlo"); }

� POZOR: rezervirani besedi checked in unchecked lahko uporabimo le za nadzor celoštevilskega računanja, ne pa tudi računanja z decimalno vejico. Pri računanju z decimalno vejico nikoli ne pride do izjeme vrste OverflowException, ampak večinoma dobimo samo napačen rezultat.

Rezervirani besedi checked in unchecked lahko uporabimo tudi za kontrolo prekoračitve v aritmetičnih izrazih: int niIzjeme = unchecked(System.Int32.MaxValue + 1); int jezjema = checked(System.Int32.MaxValue + 1);

Pisanje lastnih izjem – uporaba rezervirane besede throw

V C# obstaja tudi možnost pisanja lastnih izjem. To možnost bomo samo omenili in razložili na primeru, več o pisanju lastnih izjem pa bomo izvedeli, ko bomo imeli s programiranjem v C# že več znanja. Recimo, da smo napisali metodo imeMeseca, ki ima en sam parameter tipa int, vrne pa ime ustreznega meseca. Npr, klic imeMeseca(1) naj vrne ''Januar''. Vprašanje pa je, kaj naj funkcija vrne, če sprejme argument manjši od 1 ali večji od 12. Najboljša rešitev je, da metoda v tem primeru ne vrne ničesar in da pride do izjeme (Exception). .&ET platforma vsebuje cel kup že napisanih razredov ki so ustvarjeni prav z namenom da izvržejo vse mogoče izjeme. Najpogosteje bomo za take primere, kot je opisani, našli že obstoječi razred, ki v zadovoljivi meri rešuje naš problem in ga seveda lahko uporabimo. Za opisani primer bo to razred System.ArgumentOutOfRangeException. Primer: //klic metode, v kateri smo napisali lastno izjemo MessageBox.Show(imeMeseca(55));

Page 48: PROŠNJA - uranic.tsckr.siuranic.tsckr.si/VISUAL C#/VISUAL C#.pdf · MICROSOFT VISUAL C# .NET Srečo Uranič . Visual C# . ... Za začetek novega projekta v okolju Visual C# Standard

Visual C# . NET

Algoritmi in programski jeziki – 4.letnik

48

//Metoda public static string imeMeseca(int mesec) { try { switch (mesec) { case 1: return "januar"; case 2: return "februar"; . . . case 12: return "december"; default: throw new ArgumentOutOfRangeException("Napačen mesec","PONOVI!"); } } catch (ArgumentOutOfRangeException outOfRange) { return (outOfRange.Message); }

Ker smo metodo imeMeseca klicali s parametrom 55, bomo glede na našo lastno izjemo dobili takole obvestilo:

� POZOR: Kadar v posamezni veji switch stavka uporabimo stavek return, stavek break &I

potreben. . Vaja: Poglejmo si še en primer uporabe varovalnega bloka, obenem pa bomo spoznali še gradnik ToolTip – postavitev tega gradnika na obrazec omogoča, da se pri premiku miške nad nek gradnik na obrazcu, pod gradnikom v okvirčku izpiše določeno obvestilo, oziroma namig. Kreirajmo nov projekt z imenom Menjalnica in na obrazec postavimo gradnike, tako kot prikazuje naslednja slika: Gradnik ToolTip je nevizuelni gradnik, zato se po postavitvi na obrazec prikaže v polju pod obrazcem (ime gradnika = toolTip1).

Vsakemu gradniku na obrazcu, za katerega želimo v fazi izvajanja programa prikazati komentar/pomoč oz. namig, moramo sedaj v lastnost ToolTip on toolTip1 zapisati ustrezen komentar oz. namig. Če se bomo v fazi izvajanja programa z miško za trenutek zaustavili nad določenim gradnikom (npr. nad vnosnim poljem), se bo v okvirčku pod kazalnikom miške prikazal prej zapisan komentar oz. namig.

Page 49: PROŠNJA - uranic.tsckr.siuranic.tsckr.si/VISUAL C#/VISUAL C#.pdf · MICROSOFT VISUAL C# .NET Srečo Uranič . Visual C# . ... Za začetek novega projekta v okolju Visual C# Standard

Visual C# . NET

Algoritmi in programski jeziki – 4.letnik

49

Zapišimo še kodo, ki ustreza zgornjemu projektu.

• Dogodek Load obrazca in pred njim deklaracija treh spremenljivk, za tri tečaje, ki jih želimo uporabiti

double USD = 1.3694, HRK = 7.3102,CHF = 1.6501; private void Form1_Load(object sender, EventArgs e) { tEUSD.Text = Convert.ToString(USD); //tečaj zapišemo v vnosno polje za USD tECHF.Text = Convert.ToString(CHF); //tečaj zapišemo v vnosno polje za CHF tEHRK.Text = Convert.ToString(HRK); //tečaj zapišemo v vnosno polje za HRK tEZnesek.Text = "0"; //začetna vsebina polja za vnos zneska }

• Ob kliku na radijske gumbe ali pa pri vsaki spremembi tečaja katerekoli valute naj se vsebina polja (ime gradnika TextEdit je tERezultat), v katerem je zapisan končni znesek, pobriše.

private void groupBox3_Enter(object sender, EventArgs e) { tERezultat.Text = ""; }

• Ob kliku na gumb Izračun pa naj se glede na izbrano valuto v grupi radijskih gumbov in glede na vneseni tečaj v ustreznem gradniku TextEdit, kjer so zapisani tečaji, izračuna ustrezen znesek in se nato zapiše v gradnik TextEdit z imenom tERezulat. Ker pa pri konverziji zneskov in izračunu lahko pride do napake, smo celotno kodo te metode postavili v varovali blok.

� POZOR: Pri vnosu decimalnih števil moramo paziti: decimalno ločilo je VEJICA in NE pika. Če bomo vnesli piko, metoda Convert.ToDouble ne bo javila napake. Znak pika se bo enostavno ignoriral, zaradi česar bo seveda pretvorjeni znesek toliko večji!!!

private void button1_Click(object sender, EventArgs e) { double tecaj=0; string valuta; try { if (radioButton1.Checked) { tecaj = Convert.ToDouble(tEUSD.Text); //pretvorba v double valuta = "USD"; } else if (radioButton2.Checked) { tecaj = Convert.ToDouble(tECHF.Text); //pretvorba v double valuta = "CHF"; } else { tecaj = Convert.ToDouble(tEHRK.Text); //pretvorba v double valuta = "HRK"; } double rezultat = Convert.ToDouble(tEZnesek.Text) * tecaj; //izračun //rezultat formatirajmo na dve decimalki tERezultat.Text = rezultat.ToString("###,##0.00") + " " + valuta; } catch { //obvestilo v primeru kakršnekoli napake MessageBox.Show("Napaka v podatkih ali v preračunu!"); } }

V zgornji metodi smo rezultat, ki smo ga spremenili v zaporedje znakov/string formatirali: tERezultat.Text = Convert.ToString("###,##0.00",rezultat)

Page 50: PROŠNJA - uranic.tsckr.siuranic.tsckr.si/VISUAL C#/VISUAL C#.pdf · MICROSOFT VISUAL C# .NET Srečo Uranič . Visual C# . ... Za začetek novega projekta v okolju Visual C# Standard

Visual C# . NET

Algoritmi in programski jeziki – 4.letnik

50

Razlaga formata: na mestih, kjer je v formatu znak # bodo izpisane cifre le v primeru, da je znesek tako velik, sicer pa na tem mestu v izpisu ne bo ničesar. Znak vejica v formatu bo na izpisu predstavljala pika, znak pika v formatu pa bo na izpisu predstavljala decimalna vejica. Znesek 500000,00 bo torej na izpisu (zaradi formatiranja) prikazan kot 500.000,00. Znak 0 v formatu pomeni, da bo na tem mestu v vsakem primeru izpisana cifra, v skrajnem primeru bo izpisana cifra 0.

Page 51: PROŠNJA - uranic.tsckr.siuranic.tsckr.si/VISUAL C#/VISUAL C#.pdf · MICROSOFT VISUAL C# .NET Srečo Uranič . Visual C# . ... Za začetek novega projekta v okolju Visual C# Standard

Visual C# . NET

Algoritmi in programski jeziki – 4.letnik

51

Pisanje lastnih metod v Visual C#

Deklaracija in definicija metode Z metodami smo se seveda srečali že pri osnovah jezika C#. Metode so pravzaprav funkcije, ki jih napišemo v nekem razredu. Metoda je sestavljena iz glave in iz telesa. Glavo metode sestavlja:

• Tip metode (void, int, float, string, …), ki obenem pomeni tudi informacijo o tem, kaj metoda vrača. Metode tipa void ne vračajo ničesar (nimajo return stavka).

• Ime metode (npr izracunDavka) je ime, ki ga bomo uporabili za klic te metode. Pri poimenovanju metod se moramo držati istega pravila kot pri poimenovanju spremenljivk oz. ostalih identifikatorjev. Tako npr. ime metode izracun$Davka ni dovoljeno, ker vsebuje nedovoljen znak $.

• Seznam parametrov – določa tipe in število parametrov, ki jih metoda lahko prejme, napisati pa jih moramo znotraj okroglih oklepajev. Parametre pišemo tako, da najprej navedemo tip posameznega parametra, nato pa njegovo ime. Če je parametrov več, je med posameznimi parametri vejica. Metoda je lahko tudi brez parametrov – v tem primeru zapišemo samo oklepaja ().

Telo metode je sestavljeno iz vrstic kode, ki se izvedejo ob klicu te metode. Celotno telo metode omejujeta zavita oklepaja { … }. Primer metode tipa float z dvema parametroma tipa float:

glava metode telo metode

S stavkom return povemo, kaj metoda vrača. Vrednost, ki jo metoda vrača, mora biti istega tipa kot je tip metode. Če je torej npr. funkcija tipa int, mora biti tudi vrednost, ki jo funkcija vrača enakega tipa. Stavek return naj bi bil napisan na koncu metode, saj povzroči, da se metoda konča, Vsi stavki, ki so zapisani za besedico return se ne bodo izvedli (razen seveda, če se nahajajo v drugem bloku!). Še primer metode tipa void, ki ne vrača ničesar:

Besedica void torej označuje metodo, ki ne vrača ničesar. V metodah tipa void zaradi tega stavka return ne pišemo, lahko pa uporabimo variacijo stavka return. Če namreč na nekem mestu želimo takojšno prekinitev oz. zaključek metode, lahko zapišemo besedico return ki ji takoj sledi podpičje:

Takojšnja prekinitev/zaključek metode

public float izracunDavka(double znesek, float stopnja ) { //... //stavki... //... return ...; }

public void prikaz(int odgovor) { //... //stavki... //... }

public void prikaz(int odgovor) { ... if (...) return; ... }

Page 52: PROŠNJA - uranic.tsckr.siuranic.tsckr.si/VISUAL C#/VISUAL C#.pdf · MICROSOFT VISUAL C# .NET Srečo Uranič . Visual C# . ... Za začetek novega projekta v okolju Visual C# Standard

Visual C# . NET

Algoritmi in programski jeziki – 4.letnik

52

� POZOR: V C++ smo bili navajeni, da smo pisali t.i. globalne metode. V C# pa moramo vse metode, ki jih naredimo sami, deklarirati in definirati znotraj razreda, sicer se naša koda ne bo prevedla. Glede informacij o zaščiti (private, public, .. veljajo enaka pravila kot smo jih spoznali pri osnovah jezika C# ali pa C++.

Klic metode Metodo napišemo zato, da jo bomo nekje poklicali. Metodo pokličemo zaradi tega, da izvede operacije, ki smo jih napisali v telesu metode. Če smo metodo napisali tako, da v glavi vsebuje parametre, moramo seveda le-te navesti tudi pri klicu te metode. (razen v primeru rezervnih parametrov!). Metoda, ki ni tipa void poleg tega seveda vrača nek rezultat, zaradi tega jo moramo klicati v stavku. Metodo, ki je tipa void kličemo v samostojnem stavku.

� POZOR: Pri vsakem klicu katerekoli metode v C# moramo na koncu zapisati okrogla

oklepaja (), tudi če metoda nima parametrov! Naredimo sedaj projekt, v katerem bomo napisali svojo prvo lastno metodo. Metodi bomo posredovali dva podatka tipa float, rezultat, ki ga bo metoda vrnila pa bo prav tako tipa float. Vaja: Kreirajmo nov projekt z imenom Metoda in na obrazec postavimo gradnike, tako kot prikazuje naslednja slika:

TextBox – ime gradnika je tBZnesek ComboBox – ime gradnika je cBStopnja. Pri lastnosti Items kliknimo na tripičje in vnesimo števila 20,00 8,5 in 0 – vsako število v svojo vrstico. TextBox – ime gradnika je tBZnesekZDavkom, lastnost ReadOnly= True

Ob kliku na gumb z napisom Izračunaj (ime gradnika je bIzracun) naj se izvede dogodek bIzracunaj: private void bIzracunaj(object sender, EventArgs e) { //uporabimo varovalni blok try { float znesek = Convert.ToSingle(tBZnesek.Text); float stopnja = Convert.ToSingle(cBStopnja.Text); //klic lastne metode izracundavka float davek = izracunDavka(znesek, stopnja); tBZnesekZDavkom.Text = Convert.ToString(znesek+davek); } catch { MessageBox.Show("Napaka v podatkih!"); } }

Lastno metodo izracunDavka moramo seveda še napisati. Napišimo jo kjerkoli znotraj razreda Form1. private float izracunDavka(float znesek, float stopnja )

Page 53: PROŠNJA - uranic.tsckr.siuranic.tsckr.si/VISUAL C#/VISUAL C#.pdf · MICROSOFT VISUAL C# .NET Srečo Uranič . Visual C# . ... Za začetek novega projekta v okolju Visual C# Standard

Visual C# . NET

Algoritmi in programski jeziki – 4.letnik

53

{ float davek = znesek*(stopnja/100); return davek; }

Vaja: V naslednjem primeru bomo pokazali uporabo gradnika LinkLabel. Le-ta omogoča dodajanje spletnih linkov v našo Windows aplikacijo. Gradnik LinkLabel se sicer lahko uporablja enako kot navadna labela, poleg tega pa lahko kot link na določeno spletno stran, datoteko, mapo ali aplikacijo uporabimo le del teksta, ki je prikazan na gradniku LinkLabel. Poglejmo si nekaj najpomembnejših lastnosti gradnika LinkLabel

Lastnost Opis

ActiveLinkColor Določimo kakšna naj bo barva gradnika/teksta ko ga kliknemo. Privzeta barva je rdeča.

LinkArea Določimo, kateri del teksta LinkLabel - e je mišljen kot del linka.

LinkBehavior Določimo obnašanje linka, npr. kako se le-ta obnaša, ko nadenj postavimo miško.

LinkColor Določimo originalno barvo linka, preden je kliknjen prvič. Privzeta barva je modra.

LinkVisited Če true, je link prikazan kot da je bil že obiskan (barva se spremeni v barvo nastavljeno z lastnostjo VisitedLinkColor. Privzeta vrednost je false.

Text Tekst na labeli.

UseMnemonic Če true, potem se znak & in lastnost Text obnašata kot bližnjica za dostop preko tipkovnice.

VisitedLinkColor Določa barvo obiskanih linkov. Privzeta barva je Color.Purple.

Med dogodki gradnika LinkLabel pa omenimo le najpomembnejšega: to je dogodek LinkClicked, ki določa, kaj naj se zgodi, ko uporabnik izbere to labelo.

Kreirajmo sedaj projekt LinkLabelDEMO. Na obrazec postavimo tri gradnike LinkLabel. Imena gradnikov LinkLabel so lLRaziskovalec, lL&ajdi in lL&otepad

Lastnost LinkArea ima nastavljeno podlastnost Start na 19 in Length na 13

Realizirajmo še ustrezne dogodke LinkClicked.

• Dogodek LinkClicked labele lLRaziskovalec:

private void lLRaziskovalec_LinkClicked(object sender, LinkLabelLinkClickedEventArgs e)

Page 54: PROŠNJA - uranic.tsckr.siuranic.tsckr.si/VISUAL C#/VISUAL C#.pdf · MICROSOFT VISUAL C# .NET Srečo Uranič . Visual C# . ... Za začetek novega projekta v okolju Visual C# Standard

Visual C# . NET

Algoritmi in programski jeziki – 4.letnik

54

{ lLRaziskovalec.LinkVisited = true; System.Diagnostics.Process.Start("C:\\"); }

• Dogodek LinkClicked labele lL&ajdi:

private void lLNajdi_LinkClicked(object sender, LinkLabelLinkClickedEventArgs e) { lLNajdi.LinkVisited = true; System.Diagnostics.Process.Start("IExplore", "http://www.najdi.si"); }

• Dogodek LinkClicked labele lL&otepad: napisali smo še lastno metodo zazeni(), ki dobi za parameter

ime poljubnega programa in ga poskuša zagnati. Uporabili smo tudi varovalni blok za obvestilo o napaki!

private void lLNotepad_LinkClicked(object sender, LinkLabelLinkClickedEventArgs e) { lLNotepad.LinkVisited = true; zazeni("notepad"); //klic lastne metode } //lastna metoda za zagon poljubne aplikacije/programa private void zazeni(string ime) { try { System.Diagnostics.Process.Start(ime); } catch { MessageBox.Show("Napaka pri odpiranju programa "+ime); } }

Bloki (ponovitev/nadgradnja iz osnov jezika C# oz. C++) Blok je del programa, ki ga omejujeta zaviti oklepaj in zaviti zaklepaj. Spremenljivke, ki jih deklariramo v blokih (ali pa metodah) lahko uporabljamo v vseh stavkih, ki sledijo tej deklaraciji. Ko se blok (metoda) zaključi, spremenljivka izgine – ugasne. Take spremenljivke se imenujejo lokalne spremenljivke, ker so pač lokalne glede na ta blok oz. metodo. To pa pomeni, da ne moremo uporabljati lokalnih spremenljivk za izmenjavo podatkov med bloki oz. metodami. Primer:

Konec bloka (in tudi metode) – spremenljivka sprem ugasne, zaradi česar v naslednjem bloku ni dosegljiva (prevajalnik javi napako!)

class Primer { void metoda() { int sprem; . . . } void drugametoda() { sprem=40; //napaka pri prevajanju . . . } }

Page 55: PROŠNJA - uranic.tsckr.siuranic.tsckr.si/VISUAL C#/VISUAL C#.pdf · MICROSOFT VISUAL C# .NET Srečo Uranič . Visual C# . ... Za začetek novega projekta v okolju Visual C# Standard

Visual C# . NET

Algoritmi in programski jeziki – 4.letnik

55

Bloki znotraj razreda Blok lahko naredimo tudi znotraj razreda. Katerakoli spremenljivka, ki jo naredimo znotraj telesa razreda, pripada temu razredu. Spremenljivka, definirana znotraj razreda, se imenuje polje (pravimo ji tudi član razreda, ali pa komponenta razreda). Za razliko od lokalnih spremenljivk, pa se polja lahko uporabljajo za izmenjavo informacij med metodami. Primer:

Spremenljivka sprem je deklarirana kot polje razreda, zaradi česar je dosegljiva (vidna) v vseh metodah znotraj tega razreda

� POZOR: Spremenljivke moramo v metodah deklarirati preden jih uporabimo. Za polje (še enkrat: polje je spremenljivka razreda, oz, član razreda!!!) pa je logika drugačna. Metoda lahko uporabi polje preden je polje deklarirano – vse potrebno opravi prevajalnik.

Vaja: Kreirajmo projekt Investiranje, ki prikazuje uporabo obrestno obrestnega računa. Vsak mesec vlagamo določen znesek, obresti so letne in glede na število let vlaganja nam program izračuna končni znesek.

gradniki TextBox (imena gradnikov tBZnesek, tBObresti, tBLeta in tBKoncni)

Ob kliku na gumb Izračunaj se izvede metoda Click tega gumba private void bIzracunaj_Click(object sender, EventArgs e) { try { if (VeljavniPodatki())//metoda za preverjanje pravilnosti vnosa podatkov { decimal znesek = Convert.ToDecimal(tBZnesek.Text); decimal procent = Convert.ToDecimal(tBObresti.Text);//letne obresti v procentih int leta = Convert.ToInt32(tBLeta.Text); int mesecev = leta * 12;

class Primer { void metoda() { sprem = 42; //OK . . . } void drugametoda() { sprem = 40; //OK . . . } int sprem; }

Page 56: PROŠNJA - uranic.tsckr.siuranic.tsckr.si/VISUAL C#/VISUAL C#.pdf · MICROSOFT VISUAL C# .NET Srečo Uranič . Visual C# . ... Za začetek novega projekta v okolju Visual C# Standard

Visual C# . NET

Algoritmi in programski jeziki – 4.letnik

56

decimal mesecneobresti = procent / 12 / 100; //mesečne obr. pretvorjene v dec. število //klic lastne metode IzracunajKoncni - izračun končnega zneska, skupaj z obrestmi decimal koncniznesek = IzracunajKoncni(znesek, mesecneobresti, mesecev); tBKoncni.Text = koncniznesek.ToString("c"); } } catch (Exception ex) { MessageBox.Show(ex.Message+"\n"+ex.GetType().ToString()+"\n"+ex.StackTrace,"Izjema"); } }

Dogodek Click uporablja dve metodi, ki ju moramo še napisati. To sta metodi za preverjanje pravilnosti vnosa podatkov (metoda VeljavniPodatki) in metoda za izračun končnega zneska (metoda IzracunajKoncni). Metoda VeljavniPodatki kliče še nekaj metod, ki so prav tako naše lastne metode (metode Vnesen, VnosOK, VnosVMejah in LetaInt). public bool VeljavniPodatki() { //metoda vrne tru, če so vsi vnosi pravilni //pravilnost vnosa preverjajo metode Vnesen, VnosOK, VnosVMejah in LetaInt //če vse te metode vrnejo true, je tudi skupna vrednost true (veznik && = logični IN) return Vnesen(tBZnesek, "Mesečni znesek") && VnosOK(tBZnesek, "Mesečni znesek") && VnosVMejah(tBZnesek, "Mesečni znesek", 0, 100000000) && Vnesen(tBObresti, "Letni procent obresti") && VnosOK(tBObresti, "Letni procent obresti") && VnosVMejah(tBObresti, "Letni procent obresti", 0, 20) && Vnesen(tBLeta, "Število let") && LetaInt(tBLeta, "Število let") && VnosVMejah(tBLeta, "Število let", 0, 40); } public bool Vnesen(TextBox textbox, string ime) { if (textbox.Text == "") { //prvi string v metodi MessageBox se izpiše v oknu, drugi pa v naslovni vrstici okna MessageBox.Show("Podatek "+ime+" je obezen!","Napaka pri vnosu"); textbox.Focus(); //ta gradnik dobi žarišče ali fokus return false; } return true; } public bool VnosOK(TextBox textbox, string ime) { try { Convert.ToDecimal(textbox.Text); return true; } catch (FormatException) { MessageBox.Show(ime+" mora biti decimalno število!","Napaka pri vnosu"); textbox.Focus(); return false; } } public bool LetaInt(TextBox textbox, string ime) { try { Convert.ToInt32(textbox.Text); return true; } catch (FormatException)

Page 57: PROŠNJA - uranic.tsckr.siuranic.tsckr.si/VISUAL C#/VISUAL C#.pdf · MICROSOFT VISUAL C# .NET Srečo Uranič . Visual C# . ... Za začetek novega projekta v okolju Visual C# Standard

Visual C# . NET

Algoritmi in programski jeziki – 4.letnik

57

{ MessageBox.Show("Polje "+ime+" mora biti celoštevilčno!","Napaka pri vnosi"); textbox.Focus(); return false; } } public bool VnosVMejah(TextBox textbox, string ime, decimal min, decimal max) { decimal stevilo = Convert.ToDecimal(textbox.Text); if (stevilo<min||stevilo>max) { MessageBox.Show(ime+" mora biti v mejah med "+min.ToString() +" in "

+max.ToString()+".","Napaka pri vnosu"); textbox.Focus(); return false; } return true; } public decimal IzracunajKoncni(decimal znesek, decimal mesecneobresti, int mesecev) { decimal koncni = 0m; //0m pomeni gre za denarno vrednost (tip money) for (int i = 0; i < mesecev; i++) { //obrestno obrestni račun koncni = (koncni + znesek) * (1 + mesecneobresti); } return koncni; }

Page 58: PROŠNJA - uranic.tsckr.siuranic.tsckr.si/VISUAL C#/VISUAL C#.pdf · MICROSOFT VISUAL C# .NET Srečo Uranič . Visual C# . ... Za začetek novega projekta v okolju Visual C# Standard

Visual C# . NET

Algoritmi in programski jeziki – 4.letnik

58

Razredi in objekti

Razred - class Pojem razred (class) je temeljni pojem objektno (predmetno) orientiranega programiranja. Kreirati nov razred pomeni sistematično urediti podatke in informacije, ter manipulacije nad njimi v neko pomensko celoto. Takšno urejanje podatkov in informacij je nekaj, kar je običajen pojav tudi v vsakdanjem življenju in ne velja le za programiranje. Kot primer iz vsakdanjega življenja vzemimo pojem avto: vsi avtomobili imajo nekaj skupnih zmožnosti (lahko jih usmerjamo oz. vodimo, lahko jih zaustavimo, pospešujemo, itd. ) in nekaj skupnih lastnosti oz. atributov ( imajo volanski obroč, pogonski motor, kolesa itd.). Ko torej pomislimo na avto, takoj vemo, da gre za pojem oz. objekte ki si delijo prej omenjene skupne lastnosti in zmožnosti. Klasificiranje oz. razvrščanje pojmov v neko celoto je torej temeljna spretnost, ki je lastna vsem ljudem in brez katere si težko zamišljamo, kako bi ljudje razmišljali in komunicirali med seboj. Prav zaradi tega so tudi programerji prišli na idejo, da bi posamezne pojme, njihove lastnosti (atribute) in operacije nad njimi, združili v neko celoto, ki so jo poimenovali razred – class. Prav to pa je natanko tisto, kar nam ponujajo moderno zasnovani objektno orientirani programski jeziki, kot je npr. Microsoft Visual C#. Omogočajo definicijo novih razredov, ki v sebi združujejo lastnosti (atribute oz. podatke) in operacije nad njimi oz obnašanje (metode). Sveta trojica predmetno usmerjenega programiranja so pojmi enkapsulacija, dedovanje in polimorfizem

Enkapsulacija

Glavni del pojma enkapsulacija predstavlja besedica kapsula: enkapsulacija pomeni postaviti nekaj v kapsulo. Za kapsulo je značilno, da ima vidno (javno) površino in nevidno (zasebno) notranjost, v kateri se nekaj nahaja. Vsak predmet ali pojem si lahko ponazorimo s kapsulo. Površino predstavljajo podatki in operacije, ki jih lahko od zunaj spreminjamo. Notranjost pa so zasebni deli predmeta, ki od zunaj niso dostopni. Tudi v svetu programiranja poznamo dve pomembni uporabi oz razlagi pojma enkapsulacija:

• Princip, kako v enoto združimo podatke in metode, ki operirajo nad temi podatki. Gre torej za kombinacijo metod in podatkov znotraj razreda, z drugimi besedami razvrščanju oz. klasifikaciji;

• Notranje podatke in procese navzven skrijemo – gre torej za kontrolo dostopa do metod in podatkov v kapsuli, z drugimi besedami za kontrolo uporabe razreda.

Razred – class je programska struktura, ki zagotavlja sintakso in semantiko za podporo teh dveh dveh temeljnih načel enkapsulacije. Kot primer kreirajmo razred krog, ki vsebuje eno metodo (za izračun ploščine kroga) in eno lastnost oz. podatek (polmer kroga). Deklaracijo novega razreda začnemo z rezervirano besedico class, ki ji sledi ime razreda, nato pa še telo razreda, zapisano med dvema zavitima oklepajema. V telesu razreda so metode (npr. metoda za ploščino), spremenljivke, ki jim pravimo tudi polja razreda ( npr. polmer kroga) in pa lastnosti (properties) – o lastnostih bo več zapisanega kasneje. class krog { double Ploscina() //metoda razreda { return Math.PI * polmer * polmer; } double polmer; //polje razreda }

Tako deklariran razred krog pa nima praktične uporabe, saj nismo upoštevali drugega načela enkapsulacije: dostopnost. Potem, ko smo enkapsulirali metode in polje znotraj razreda, smo pred temeljno odločitvijo, kaj naj

Page 59: PROŠNJA - uranic.tsckr.siuranic.tsckr.si/VISUAL C#/VISUAL C#.pdf · MICROSOFT VISUAL C# .NET Srečo Uranič . Visual C# . ... Za začetek novega projekta v okolju Visual C# Standard

Visual C# . NET

Algoritmi in programski jeziki – 4.letnik

59

bo javno, kaj pa zasebno. Vse kar smo zapisali med zavita oklepaja v razredu, spada v notranjost razreda, vse kar pa je pred prvim oklepajem in za zadnjim oklepajem pa je zunaj razreda. Z besedicama public, private in protected pa lahko kontroliramo, katere metode in polja bodo dostopna tudi od zunaj:

• Metoda ali polje je mišljeno kot privatno (private), kadar je dostopno le znotraj razreda. Tako polje oz. metodo deklariramo tako, da pred tipom metode oz. polja postavimo besedico private.

• Metoda ali polje je mišljeno kot javno (public), kadar je dostopno tako znotraj, kot tudi izven razreda. Tako polje oz. metodo deklariramo tako, da pred tipom metode oz. polja postavimo besedico public..

• Metoda ali polje je mišljeno kot zaščiteno (protected), kadar je vidno le znotraj razreda, ali pa v podedovanih (izpeljanih) razredih.

Deklaracijo našega razreda krog sedaj napišimo še enkrat. Tokrat bomo metodo Ploscina deklarirali kot javno metodo, polje polmer pa kot privatno polje. Metodo Ploscina bomo v tem primeru lahko poklicali oz. uporabili tudi izven razreda, neposrednega dostopa do polja polmer pa ne bomo imeli. class Krog { public double Ploscina() //javna metoda razreda { return Math.PI * polmer * polmer; } private double polmer; //zasebno polje razreda }

Če metode ali polja ne deklariramo kot public ali pa private, bo privzeto, da je metoda oz. polje private. Seveda pa velja, da če so vse metode in polja znotraj razreda privatne, razred nima praktične uporabe. Če hočemo razred, ki smo ga definirali tudi uporabiti, moramo kreirati novo spremenljivko tega tipa, oz. kot temu pravimo v svetu objektnega programiranje, kreirati moramo novo instanco (primerek) razreda, ki ji pravimo tudi objekt. Novo spremenljivko tipa Krog lahko deklariramo tako kot vsako drugo spremenljivko. Naredimo primerjavo med kreiranjem in inicializacijo spremenljivk osnovnih (primitivnih) podatkovnih tipov in spremenljivk izpeljanih iz razredov (objektov). Osnovna sintaksa je enaka: int i; //deklaracija spremenljike i, ki je celoštevilčnega tipa Krog K //deklaracija spremenljivke K ki je tipa Krog (razred) Če hočemo uporabiti vrednost katerekoli spremenljivke, moramo biti prepričani, da ta spremenljivka že ima vrednost. int i; //deklaracija spremenljike i, celoštevilčnega tipa MessageBox.Show(i); //NAPAKA – uporaba neinicilaizirane spremenljivke

To pravilo velja seveda za vse spremenljivke, ne glede na njihov tip. Krog K //deklaracija spremenljivke k, ki je tipa Krog (razred) MessageBox.Show(K); //NAPAKA – uporaba neinicilaizirane spremenljivke

Spremenljivke osnovnih podatkovnih tipov seveda lahko inicializiramo enostavno takole: int i=10; //deklaracija in inicilaizacija spremenljike i, celoštevilčnega tipa MessageBox.Show(i); //OK!! //ali pa takole int j; j = 10; MessageBox.Show(j); //OK!! Spremenljivkam, ki so izpeljane (ustvarjene) iz razreda pa vrednosti ne moremo prirejati tako kot spremenljivkam osnovnih (vrednostih) tipov. Uporabiti moramo rezervirano besedo new in za naš razred poklicati ustrezen konstruktor. Novo spremenljivko (nov objekt oz. novo instanco) razreda Krog torej ustvarimo s pomočjo rezervirane besede new takole:

Page 60: PROŠNJA - uranic.tsckr.siuranic.tsckr.si/VISUAL C#/VISUAL C#.pdf · MICROSOFT VISUAL C# .NET Srečo Uranič . Visual C# . ... Za začetek novega projekta v okolju Visual C# Standard

Visual C# . NET

Algoritmi in programski jeziki – 4.letnik

60

Krog K = new Krog();/*ustvarili smo nov objekt razreda Krog: pri tem smo uporabili privzeti konstruktor – to je metodo Krog(), ki ima enako ime kot razred. */ //Ali pa takole Krog K; K = new Krog();

Primer: class Zgradba//deklaracija razreda Zgradba z dvema javnima poljema. Razred nima nobene metode. { public int kvadratura; public int stanovalcev; } public void metoda() { Zgradba hiša = new Zgradba(); //nov objekt razreda Zgradba Zgradba pisarna = new Zgradba(); //nov objekt razreda Zgradba int kvadraturaPP; // kvadratura na osebo hiša.stanovalcev = 4; hiša.kvadratura = 2500; pisarna.stanovalcev = 25; pisarna.kvadratura = 4200; kvadraturaPP = hiša.kvadratura / hiša.stanovalcev; MessageBox.Show("Hiša ima:\n " + hiša.stanovalcev + " stanovalcev\n " + hiša.kvadratura + " skupna kvadratura\n " + kvadraturaPP + " kvadratura na osebo"); kvadraturaPP = pisarna.kvadratura / pisarna.stanovalcev; MessageBox.Show("Pisarna ima:\n " + pisarna.stanovalcev + " stanovalcev\n " + pisarna.kvadratura + " skupna kvadratura\n " + kvadraturaPP + " kvadratura na osebo"); }

Vaja: Kreirajmo razred oseba s štirimi polji (ime, priimek, letnik in višina), ter dvema metodama: metodo za prirejanje

podatkov posameznim poljem in metodo za izpis podatkov. Vrednosti polj vnesi v gradnika tipa TextBox, ob kliku na gumb pa te vrednosti prenesi v nov objekt, obenem pa vrednosti dodaj v gradnik tipa ListBox. Med posamezna polja postavi ločilo »|«.

class Oseba { //razred ima štiri zasebna polja private string ime; private string priimek; private int letnik; private int visina; //razred ima tudi dve javni metodi public void VpisiOsebo(string ime, string priimek, int letnik, int visina) {

Page 61: PROŠNJA - uranic.tsckr.siuranic.tsckr.si/VISUAL C#/VISUAL C#.pdf · MICROSOFT VISUAL C# .NET Srečo Uranič . Visual C# . ... Za začetek novega projekta v okolju Visual C# Standard

Visual C# . NET

Algoritmi in programski jeziki – 4.letnik

61

//ker imajo parametri metode enako ime kot polja razreda, smo za dostop do polj razreda //uporabili rezervirano besedo this - ta pomeni referenco na konkreten primerek razreda //(objekt), oz. referenco na polje konkretnega objekta this.ime = ime; this.priimek = priimek; this.letnik = letnik; this.visina = visina; } public void IzpisiOsebo() { MessageBox.Show("Ime: " + ime + "\nPriimek: " + priimek + "\nLetnik: " + letnik + "\nVišina: " + visina + "\n"); } } public Form1() { InitializeComponent(); } private void button1_Click(object sender, EventArgs e) { try { Oseba nova = new Oseba();//nova instanca (primerek) razreda Oseba nova.VpisiOsebo(textBox1.Text, textBox2.Text, Convert.ToInt32(textBox3.Text), Convert.ToInt32(textBox4.Text)); listBox1.Items.Add(textBox1.Text + "|" + textBox2.Text + "|" + Convert.ToInt32(textBox3.Text) + "|" + Convert.ToInt32(textBox4.Text)); textBox1.Clear(); textBox2.Clear(); textBox3.Clear(); textBox4.Clear(); nova.IzpisiOsebo(); } catch { MessageBox.Show("Napaka v podatkih!"); } }

Vaja: Kreiraj razred Pravokotnik z dvema poljema (dolžina in višina pravokotnika) in dvema metodama (metoda za

izračun ploščine in metoda za izračun obsega pravokotnika. Nato ustvari nov objekt tipa Pravokotnik, določi stranice pravokotnika in s pomočjo metod razreda Pravokotnik izračunaj njegovo ploščino in obseg. Gradnik DataGridView ( imena stolpcev so Stranicaa, Stranicab, Ploscina, Obseg in Diagonala)

public class Pravokotnik { public double a,b; //stranici pravokotnika public void postavi(double xx, double yy) //metoda za inicializacijo stranic { a=xx; b=yy; } public double ploscina() { return (a*b);

Page 62: PROŠNJA - uranic.tsckr.siuranic.tsckr.si/VISUAL C#/VISUAL C#.pdf · MICROSOFT VISUAL C# .NET Srečo Uranič . Visual C# . ... Za začetek novega projekta v okolju Visual C# Standard

Visual C# . NET

Algoritmi in programski jeziki – 4.letnik

62

} public double obseg() { return (2*a+2*b); } public double diagonala() { return (Math.Round(Math.Sqrt(Math.Pow(a,2)+Math.Pow(b,2)),2)); } } public Form1() { InitializeComponent(); } int stPrav=0; //skupno število pravokotnikov Random naklj = new Random(); //generator naključnih števil private void Form1_Load(object sender, EventArgs e) { Pravokotnik P=new Pravokotnik(); P.postavi(naklj.Next(1,11), naklj.Next(1,11)); stPrav++; textBox1.Text = Convert.ToString(P.a); textBox2.Text = Convert.ToString(P.b); //V dataGridView1 dodamo novo vrstico dataGridView1.Rows.Add(Convert.ToString(P.a),Convert.ToString(P.b,) Convert.ToString(P.ploscina()),Convert.ToString(P.obseg()), Convert.ToString(P.diagonala())); } private void button2_Click(object sender, EventArgs e) { int najv = 0,naja=0,najb=0,a,b; int vrstic = dataGridView1.Rows.Count-1; //število zasedenih vrstic v dataGridView1 for (int i=0;i<vrstic;i++)//obdelamo celotno tabelo (vse vrstice gradnika dataGridView1) { a = Convert.ToInt32(dataGridView1.Rows[i].Cells["Stranicaa"].Value.ToString()); b = Convert.ToInt32(dataGridView1.Rows[i].Cells["Stranicab"].Value.ToString()); if (a * b > najv)//če najdemo večjo ploščino si zapomnimo stranice { najv = a * b; naja = a; najb = b; } } MessageBox.Show("Največji pravokotnik ima stranici "+naja+" in "+najb); }

Konstruktor

Konstruktor je metoda razreda, ki se uporablja za kreiranje novega primerka (instance) razreda. &jegovo ime je vedno enako kot je ime njegovega razreda. Namen konstruktorja pa je, da poskrbi za inicializacijo polj novo kreiranega objekta. Če konstruktorja ne napišemo sami, ga avtomatično za nas skreira prevajalnik. Z drugimi besedami, originalni razred Krog, za katerega smo zgoraj napisali eno samo metodi in eno polje, v resnici vsebuje še eno metodo: to je nevidni konstruktor, ki poskrbi za inicializacijo polja polmer. Ta metoda je povsem enaka, kot če bi napisali celoten razred takole: class Krog { public Krog() { polmer = 0.0; } public double Ploscina() { return Math.PI * polmer * polmer; }

Page 63: PROŠNJA - uranic.tsckr.siuranic.tsckr.si/VISUAL C#/VISUAL C#.pdf · MICROSOFT VISUAL C# .NET Srečo Uranič . Visual C# . ... Za začetek novega projekta v okolju Visual C# Standard

Visual C# . NET

Algoritmi in programski jeziki – 4.letnik

63

private double polmer; }

Konstruktor, ki ga avtomatično generira prevajalnik, je vedno public, nima nobenega tipa (niti void), nima argumentov, vrednosti numeričnih polj postavi na 0, polja tipa bool postavi na false, vsem referenčnim poljem (spremenljivkam) pa priredi vrednost null. (null &I neka vrednost, ampak je stanje, ki nam pove, da neka spremenljivka (ali pa objekt) nima nobene vrednosti, oz. da je vrednost neznana, neobstoječa) Ko je nov objekt inicializiran, lahko dostopamo do njegovih polj in uporabljamo njegove metode. Do javnih (public) polj (lastnosti razreda) in javnih metod dostopamo s pomočjo operatorja pika, tako kot pri strukturah. Krog K = new Krog(); MessageBox.Show(K.Ploscina()); /*Izpis bo seveda enak 0, ker je privzeti konstruktor polju polmer avtomatično dodelil vrednost 0!*/

Primer: Deklarirajmo razred Kegljanje, ki bo imel zasebno polje naziv in javno dvodimenzionalno tabelo keglji. Razred naj ima konstruktor, ki inicializira nazv, ter ustvari dvodimenzionalno tabelo z dvema vrsticama in tremi stolpci.

Vsak tekmovalev naj ima namreč 2 krat po tri mete, podrte keglje pa bomo shranjevali v to tabelo. Napišimo še metodo za izračun in vrnitev skupnega števila podrtih kegljev. Ustvarili bomo dva objekta, za vodenje evidence o podrtih kegljih za oba tekmovalca.

public class Kegljanje { private string naziv; //zasebno poljo razreda public int[,] keglji; //napoved javne tabele razreda public Kegljanje(string naziv) //konstruktor inicializira polja razreda { this.naziv = naziv; keglji=new int[2,3]; } public string vrniNaziv() { return naziv; } public int SkupajKegljev() { int skupaj=0; for (int i = 0; i < 2; i++) for (int j = 0; j < 3; j++) skupaj = skupaj + keglji[i, j]; return skupaj; } } Kegljanje tekmovalec1, tekmovalec2; //napoved novih objektov public Form1() { InitializeComponent(); } private void Form1_Load(object sender, EventArgs e) { tekmovalec1 = new Kegljanje("Andreja"); tekmovalec2 = new Kegljanje("Polona"); groupBox1.Text = tekmovalec1.vrniNaziv();

Page 64: PROŠNJA - uranic.tsckr.siuranic.tsckr.si/VISUAL C#/VISUAL C#.pdf · MICROSOFT VISUAL C# .NET Srečo Uranič . Visual C# . ... Za začetek novega projekta v okolju Visual C# Standard

Visual C# . NET

Algoritmi in programski jeziki – 4.letnik

64

groupBox2.Text = tekmovalec2.vrniNaziv(); } private void button1_Click(object sender, EventArgs e) { try { //podatke za prvega tekmovalca prenesem v objekt tekmovalec1 tekmovalec1.keglji[0,0]=Convert.ToInt32(textBox1.Text); tekmovalec1.keglji[0,1]=Convert.ToInt32(textBox2.Text); tekmovalec1.keglji[0,2]=Convert.ToInt32(textBox3.Text); tekmovalec1.keglji[1,0]=Convert.ToInt32(textBox4.Text); tekmovalec1.keglji[1,1]=Convert.ToInt32(textBox5.Text); tekmovalec1.keglji[1,2]=Convert.ToInt32(textBox6.Text); //podatke za drugega tekmovalca prenesem v objekt tekmovalec2 tekmovalec2.keglji[0,0]=Convert.ToInt32(textBox7.Text); tekmovalec2.keglji[0,1]=Convert.ToInt32(textBox8.Text); tekmovalec2.keglji[0,2]=Convert.ToInt32(textBox9.Text); tekmovalec2.keglji[1,0]=Convert.ToInt32(textBox10.Text); tekmovalec2.keglji[1,1]=Convert.ToInt32(textBox11.Text); tekmovalec2.keglji[1,2]=Convert.ToInt32(textBox12.Text); MessageBox.Show(tekmovalec1.vrniNaziv() + ": " + tekmovalec1.SkupajKegljev() +"kegljev\n"+tekmovalec2.vrniNaziv() + ": " +tekmovalec2.SkupajKegljev()+ " kegljev"); } catch { MessageBox.Show("Napaka v podatkih!"); } }

Preobloženi (overloaded) konstruktorji

Poglejmo si še enkrat prvotno deklaracijo razreda Krog, ki ima deklarirano eno privatno polje (polmer) in javno metodo Ploscina(). class Krog { public double Ploscina() { return Math.PI * polmer * polmer; } private double polmer; }

Deklarirajmo novo spremenljivko tipa Krog, ji priredimo vrednost novega objekta Krog, nato pa pokličimo metodo Ploscina: Krog K = new Krog(); MessageBox.Show(K.Ploscina());

Privzeti konstruktor v vsakem primeru postavi polmer na 0 (ker je polmer private, ga tudi ne moremo spremeniti), zaradi česar bo tudi ploščina objekta Krog vsakič enaka 0. Rešitev tega problema je v dejstvu, da je konstruktor prav tako metoda (sicer metoda posebne vrste), za metode pa velja, da so lahko preobložene (preobložene metode so metode, ki imajo enako ime, razlikujejo pa se v številu ali pa tipu parametrov). Z drugimi besedami, napišemo lahko svoj lasten konstruktor z vrednostjo polmera kot parametrom. Novi konstruktor ima seveda enako ime kot razred, ne vrača pa ničesar. Razred Krog bo sedaj izgledal takole: class Krog { public Krog(double inicPolmer) //naš lasten konstruktor { polmer = inicPolmer; } public double Ploscina() { return Math.PI * polmer * polmer; } private double polmer; }

Page 65: PROŠNJA - uranic.tsckr.siuranic.tsckr.si/VISUAL C#/VISUAL C#.pdf · MICROSOFT VISUAL C# .NET Srečo Uranič . Visual C# . ... Za začetek novega projekta v okolju Visual C# Standard

Visual C# . NET

Algoritmi in programski jeziki – 4.letnik

65

V primeru, da za nek razred napišemo lasten konstruktor velja, da prevajalnik ne generira privzeti konstruktor. Če pa smo napisali lasten konstruktor, ki sprejme en parameter in bi kljub temu želeli poklicati tudi privzeti konstruktor, ga moramo napisati sami. Pri tem pa moramo biti pozorni na dejstvo, da bodo vrednosti polj, ki jih v konstruktorju ne bomo inicializirali, še vedno ostala implicitno inicializirana na 0, false ali pa null.

Kopirni (copy) konstruktorji

Kopirni konstruktor je konstruktor, ki kreira nov objekt tako, da kopira polja nekega že obstoječega objekta istega tipa. Primer: public class Cas { // konstruktor public Cas(DateTime dt) { Leto = dt.Year; Mesec = dt.Month; Dan = dt.Day; }

// kopirni konstruktor public Cas(Cas obstojeciObjekt) { Leto = obstojeciObjekt.Leto; Mesec = obstojeciObjekt.Mesec; Dan = obstojeciObjekt.Dan; } // zasebna polja razreda Cas int Leto; int Mesec; int Dan; }

// kreiranje objektov DateTime trenutniCas = DateTime.Now;

Cas t = new Cas(trenutniCas); // kreiranje objekta t s pomočjo konstruktorja Cas t3 = new Cas(t); // kreiranje objekta t3 s pomočjo kopirnega konstruktorja

Statični (static) konstruktorji

Statični konstruktor je konstruktor, ki se izvede še preden je iz tega razreda izpeljan katerikoli objekt. Kdaj se bo statični konstruktor izvedel ne moremo vnaprej natančno vedeti, vemo pa, da se bo zagotovo izvedel nekje med zagonom našega programa in pred kreiranjem prve instance razreda, v katerem je ta konstruktor napisan. Pred statičnim konstruktorjem nikoli ne napišemo nivoja dostopnosti (besedic private, public, ..), ampak le besedico static. Ker gre za statično metodo (tudi konstruktor je metoda) v njem ne moremo dostopati do nestatičnih članov, ampak le do statičnih članov. Primer: public class Sporocilo { public static string Naslov = "Knjiga"; static Sporocilo() { Naslov= "Visual C#"; } ... }

Za zagon statičnega konstruktorja ne potrebujemo nobenega objekta, saj lahko do njega dostopamo kar preko imena razreda, npr.:

Page 66: PROŠNJA - uranic.tsckr.siuranic.tsckr.si/VISUAL C#/VISUAL C#.pdf · MICROSOFT VISUAL C# .NET Srečo Uranič . Visual C# . ... Za začetek novega projekta v okolju Visual C# Standard

Visual C# . NET

Algoritmi in programski jeziki – 4.letnik

66

MessageBox.Show(Sporocilo.Naslov); //Izpis v oknu: Visual C#

Polja tipa readonly

Včasih želimo narediti javno polje, za katerega želimo, da ga uporabnik ne more ali ne sme spremeniti. V takem primeru označimo da je polje readonly. Primer: public class DanasnjiDatum {

static DanasnjiDatum() // statični konstruktor { DateTime dt = DateTime.Now; Leto = dt.Year; Mesec = dt.Month; Dan = dt.Day; } // zasebna readonly polja razreda public static readonly int Leto; public static readonly int Mesec; public static readonly int Dan; }

Ker so polja readonly njihove vrednosti ne moremo spremeniti. MessageBox.Show("Leto: + DanasnjiDatum.Leto); //izpis trenutne letnice

DanasnjiDatum.Leto = 2009; //NAPAKA: polje Leto je readonly

Destruktorji

Destruktor je metoda, ki počisti za objektom. Destruktor se torej izvede, ko objekt odmre ( npr. ko se zaključi nek blok, ali pa se zaključi neka metoda, v kateri je bil objekt ustvarjen) – pokliče ga torej smetar (Garbage Collector – glej poglavje Garbage Collector). Destruktor torej sprosti pomnilniški prostor objekta (prostor, ki ga zasedajo njegovi podatki). Destruktor ima enako kot konstruktor, enako ime kot razred sam in nima tipa. Ne sprejema argumentov, za razliko od konstruktorja pa pred destruktorjem stoji še znak '~ '. Med konstruktorjem in destruktorjem pa obstaja še ena velika razlika. Medtem ko je konstruktorjev določenega objekta lahko več, je destruktor vedno samo eden. Pomembno je tudi to, da destruktorji za razliko od konstruktorjev ne obstajajo v strukturah – obstajajo le v razredih. class Krog { ... //deklaracija polj public Krog() //prvi konstruktor { ... } public Krog(double inicPolmer) //drugi konstruktor { ... }

~ Krog() //destruktor { ... } }

Glede destruktorjev je torej pomembno sledeče : • Destruktor je metoda razreda, ki ima isto ime kot razred in dodan prvi znak '~ '.

Page 67: PROŠNJA - uranic.tsckr.siuranic.tsckr.si/VISUAL C#/VISUAL C#.pdf · MICROSOFT VISUAL C# .NET Srečo Uranič . Visual C# . ... Za začetek novega projekta v okolju Visual C# Standard

Visual C# . NET

Algoritmi in programski jeziki – 4.letnik

67

• Destruktor je metoda razreda, ki nič ne vrača ( pred njo ne sme stati niti void!) in nič ne sprejme. • Razred ima lahko samo en destruktor. • Destruktor se izvede, ko se objekt uniči.

Dogovor o poimenovanju

Pri poimenovanju javnih polj in metod se skušajmo držati pravila, ki ga imenujemo PascalCase (ker je bilo prvič uporabljeno v programskem jeziku Pascal). Imena javnih polj in metod naj se začenjajo z veliko črko (v zgornjem primeru Ploscina). Pri poimenovanju zasebnih polj in metod pa se skušajmo držati pravila, ki ga imenujemo camelCase. Imena zasebnih polj in metod naj se začenjajo z malo črko (v zgornjem primeru polmer). Pri tako dogovorjenem poimenovanju je ena sama izjema. Imena razredov naj bi se začenjala z veliko črko. Ker pa se mora ime konstruktorja natančno ujemati z imenom razreda, se mora torej tudi ime konstruktorja v vsakem primeru začeti z veliko črko, ne glede na to ali je javen ali zaseben. Primer: Kot primer napišimo razred Tocka, ki naj ima dve zasebni polji x in y, ter dva lastna konstruktorja. Prvi naj bo

brez parametrov in v njegovem telesu le zapišimo stavek, ki bo v oknu izpisal, da je bil ta konstruktor poklican. Drugi konstruktor pa naj ima dva parametra, s katerima inicializiramo obe polji razreda. V telesu drugega konstruktorja napišimo stavek, ki

izpiše vrednosti obeh polj.

class Tocka { public Tocka()//Konstruktor brez parametrov { MessageBox.Show("Ustvarjen je bil nov objekt razreda Tocka.\nKlican je bil privzeti konstruktor!"); } public Tocka(int x, int y) //Preobloženi konstruktor z dvema parametroma { MessageBox.Show("Ustvarjen je bil nov objekt razreda Tocka.\nNova točka ima koordinati x = "+x+" , y = "+y); } private int x, y; } public Form1() { InitializeComponent(); } private void novaTočka00ToolStripMenuItem_Click(object sender, EventArgs e) { Tocka A = new Tocka(); //Klic konstruktorja brez parametrov } Random naklj = new Random(); private void novaTočkaXYToolStripMenuItem_Click(object sender, EventArgs e) { Tocka B = new Tocka(naklj.Next(-9,10),naklj.Next(-9,10)); //KLic konstruktorja z dvema //parametroma (naključni števili med -9 in +9) }

Page 68: PROŠNJA - uranic.tsckr.siuranic.tsckr.si/VISUAL C#/VISUAL C#.pdf · MICROSOFT VISUAL C# .NET Srečo Uranič . Visual C# . ... Za začetek novega projekta v okolju Visual C# Standard

Visual C# . NET

Algoritmi in programski jeziki – 4.letnik

68

private void konecToolStripMenuItem_Click(object sender, EventArgs e) { Close(); }

Vaja: /*Napišimo razred Zaposleni z enim samim zasebnim poljem (dohodek) in dvema konstruktorjema. Prvi konstruktor naj ima kot parameter letni dodek zaposlenega, drugi pa naj imam dva paramera: tedenski dohodek in število tednov. Napišimo še metodo za izpis skupnega dohodka*/ public class Zaposleni { private double dohodek; public Zaposleni(int letniDohodek) //konstruktur { dohodek = letniDohodek; } public Zaposleni(int tedenskiDohodek, int številoTednov)//preobloženi konstruktur { dohodek = tedenskiDohodek * številoTednov; } public double vrniDohodek() //metoda ki vrne vrednost zasebnega polja dohodek { return dohodek; } } private void button1_Click(object sender, EventArgs e) { Zaposleni Jane = new Zaposleni(20000); //nov objekt, izvede se prvi konstruktor Zaposleni Tim = new Zaposleni(550, 54); //nov objekt, izvede se drugi (preobloženi) //konstruktor MessageBox.Show("Jane ima letni dohodek : " + Jane.vrniDohodek()+" EUR" ); MessageBox.Show("Tim ima letni dohodek : " + Tim.vrniDohodek()+ " EUR"); }

Vaja: Tabela objektov: Naslednja vaja prikazuje, kako zgradimo tabelo objektov. Objekti so kvadri, izpeljani iz

razreda Kvader. Napisali smo preobloženi konstruktor, ki dobi za parametre robove kvadra. Dimenzije posameznih kvadrov smo zapisali še v gradnik ListBox. Ko je tabela polna (sproti štejemo objekte) lahko s pomočjo menija tabelo po želji obdelujemo.

class Kvader { private int a, b, c; //Razred ima tri zasebna polja public Kvader() //privzeti/osnovni konstruktor { a = 0; b = 0; c = 0; } public Kvader(int a, int b, int c) //Preobloženi onstruktor { this.a = a; this.b = b; this.c = c;

Page 69: PROŠNJA - uranic.tsckr.siuranic.tsckr.si/VISUAL C#/VISUAL C#.pdf · MICROSOFT VISUAL C# .NET Srečo Uranič . Visual C# . ... Za začetek novega projekta v okolju Visual C# Standard

Visual C# . NET

Algoritmi in programski jeziki – 4.letnik

69

} public int Prostornina() //Metoda za izračun prostornine kvadra { return a * b * c; } } public Form1() { InitializeComponent(); } static int dim = 10; //dimenzija tabele int indeks = 0; //indeks trenutnega objekta Kvader[] TabK; //Napoved tabele kvadrov Random naklj = new Random(); private void Form1_Load(object sender, EventArgs e) { label4.Text = "Dimenzija tabele je " + dim + ".\nNapolni jo s svojimi ali nakjučnimi vrednostmi!"; TabK = new Kvader[dim]; //Inicializacija tabele kvadrov listBox1.Items.Add("Indeks Robovi Kvadra"); //V ListBox zapišemo komentar (glavo) } private void button1_Click(object sender, EventArgs e) { try { int robA=Convert.ToInt32(textBox1.Text); int robB=Convert.ToInt32(textBox2.Text); int robC=Convert.ToInt32(textBox3.Text); TabK[indeks] = new Kvader(robA,robB,robC);//kreiranje objekta-upor. smo svoj konstruktor listBox1.Items.Add(indeks + ". | " + robA + " | " + robB + " | " + robC);//Podatke zapišemo tudi v ListBox indeks++; //povečamo indeks objektov textBox1.Clear();textBox2.Clear();textBox3.Clear(); //pobrišemo vsebino gradnikov if (indeks == 10) //tabela že polna? { MessageBox.Show("Tabela je polna"); button1.Enabled = false; //onemogočimo dodajanje novih objektov v tabelo button2.Enabled = false; //onemogočimo tudi genereiranje novih naključnih robov label4.Visible = false; //skrijem labelo menuStrip1.Enabled = true;//omogočimo postavke v meniju } } catch { MessageBox.Show("Napaka v podatkih"); } } private void button2_Click(object sender, EventArgs e) //generiranje naključnih robov { textBox1.Text=Convert.ToString(naklj.Next(1,10)); textBox2.Text = Convert.ToString(naklj.Next(1, 10)); textBox3.Text = Convert.ToString(naklj.Next(1, 10)); } private void kvaderZNajvečjoProstorninoToolStripMenuItem_Click(object sender, EventArgs e) { int Inajv = 0; //pa naj bo na začetku kvader z največjo prostornino kar tisti z indeksom 0 for (int i = 1; i < TabK.Length; i++) { if (TabK[i].Prostornina() > TabK[Inajv].Prostornina()) Inajv = i;//zapomnim si indeks kvadra } MessageBox.Show("Kvader z največjo prostornino:" + "\n------------------------------" + "\nIndeks : " + Inajv+ "\nProstornina: "+TabK[Inajv].Prostornina()); }

Page 70: PROŠNJA - uranic.tsckr.siuranic.tsckr.si/VISUAL C#/VISUAL C#.pdf · MICROSOFT VISUAL C# .NET Srečo Uranič . Visual C# . ... Za začetek novega projekta v okolju Visual C# Standard

Visual C# . NET

Algoritmi in programski jeziki – 4.letnik

70

Vaja: Gradnik TableLayoutPanel je omogoča prikaz, urejanje in preurejanje vsebine v mreži vrstic in stolpcev. Uporabljamo ga za lepši izgled in design uporabniškega vmesnika. Na obrazec ga lahko potegnemo neposredno iz orodjarne. Z nastavitvijo lastnosti AutoScroll na true, se lahko po gradniku tudi premikamo. Gradnik ima tudi zmožnost dinamične spremembe velikosti vrstic oz. celic v vseh smereh. Naslednji projekt prikazuje tabelo objektov (izpeljanih iz razreda Label) in njihov prikaz na obrazcu v gradniku TableLayoutPanel: The TableLayoutPanel control is a special panel control that arranges its contents in a grid in rows and columns. It helps in the design and layout of the user interface. It appears as a table of individual cells. A control can be dragged from the Toolbox and placed in a cell. It is also a scrollable container that provides scroll bars by setting the AutoScroll property to true. The control also has the ability to proportionally resize and the direction of expansion can also be controlled horizontally or vertically.

Izgled obrazca v fazi kreiranja Izgled obrazca v zagnanem programu

Granik TableLayouPanel poimenujemo tLP. Določimo mu naslednje lastnosti: ColumnCout = 5 RowCount = 5 Columns (Collection) :

Ob zagonu projekta se najprej izvede dogodek Load obrazca; //globalna objekta Label[] labela = new Label[25]; //deklaracija enodimenzionalne tabele objektov - label int[,] tabela = new int[5, 5]; //deklaracija dvodimenzionalne tabele celih števil private void Ftabela_Load(object sender, EventArgs e) { //inicializacija dvodimenzionalne tabele celih števil Random Nakljucno_Celo = new Random(); for (int i = 0; i < 5; i++) for (int j = 0; j < 5; j++) {

Page 71: PROŠNJA - uranic.tsckr.siuranic.tsckr.si/VISUAL C#/VISUAL C#.pdf · MICROSOFT VISUAL C# .NET Srečo Uranič . Visual C# . ... Za začetek novega projekta v okolju Visual C# Standard

Visual C# . NET

Algoritmi in programski jeziki – 4.letnik

71

//dvodimenzionalna tabela naključnih celih števil. Deklaracija tabele je pred metodo tabela[i, j] = Nakljucno_Celo.Next(10); } }

Ob prikazu obrazca pa se izvede metoda Show tega obrazca private void Ftabela_Shown(object sender, EventArgs e) { int k = 0; for (int i = 0; i < 5; i++) for (int j = 0; j < 5; j++) { //dinamično kreiranje novega objekta – labele z indeksom k labela[k] = new Label(); //labelini lastnosti Text priredimo zaporedno vrednost iz tabele celih števil labela[k].Text = Convert.ToString(tabela[i,j]); //labelo sedaj umestimo v ustrezno okence gradnika tLP = TableLayoutPanel this.tLP.Controls.Add(labela[k], i, j); k++; } }

Lastnost (Property)

Polja so znotraj razreda običajno deklarirana kot zasebna (private). Vrednosti jim priredimo tako, da zapišemo ustrezen konstruktor (konstruktorje), ali pa da napišemo posebno javno metodo (metode) za prirejanje vrednosti polj. Ostaja pa še tretji način, ki je najbolj razširjen – za vsako polje lahko definiramo ustrezno lastnost (property), s pomočjo katere dostopamo do posameznega polja, ali pa z njeno pomočjo prirejamo (nastavljamo) vrednosti polja. Lastnosti (properties) v razredih torej uporabljamo za inicializacijo oziroma dostop do polj razreda (objektov). Lastnost (property) je nekakšen križanec med spremenljivko in metodo. Pomen lastnosti je v tem, da ko jo beremo, ali pa vanjo pišemo, se izvede koda, ki jo zapišemo pri tej lastnosti. Branje in izpis (dostop) vrednosti je znotraj lastnosti realizirana s pomočjo rezerviranih besed get in set – imenujemo ju pristopnika (accessors). Accessor get mora vrniti vrednost, ki mora biti istega tipa kot lastnost (seveda pa mora biti lastnost istega tipa kot polje, kateremu je naemnjena), v accessor-ju set pa s pomočjo implicitnega parametra value lastnosti priredimo (nastavimo) vrednost. Navzven pa so lastnosti vidne kot spremenljivke, zato lastnosti v izrazih in prirejanjih uporabljamo kot običajne spremenljivke. Primer: class Demo { int polje; //zasebno polje public Demo() //konstruktor { polje = 0; } public int MojaLastnost //deklaracija lastnosti (property) razreda Demo { get //metoda get za pridobivaje vrednosti lastnosti polje { return polje ; } set //metoda set za prirejanje (nastavljanje) vrednosti lastnosti polje { polje = value; } } } private void button1_Click(object sender, EventArgs e) { Demo ob = new Demo();//nov objekt razreda Demo MessageBox.Show("Originalna vrednost ob.MojaLastnost: " + ob.MojaLastnost); ob.MojaLastnost = 100; MessageBox.Show("Vrednost ob.MojaLastnost: " + ob.MojaLastnost);

Page 72: PROŠNJA - uranic.tsckr.siuranic.tsckr.si/VISUAL C#/VISUAL C#.pdf · MICROSOFT VISUAL C# .NET Srečo Uranič . Visual C# . ... Za začetek novega projekta v okolju Visual C# Standard

Visual C# . NET

Algoritmi in programski jeziki – 4.letnik

72

ob.MojaLastnost = -10; //Prirejanje nove vrednosti -10 za ob.MojaLastnost MessageBox.Show("Vrednost ob.MojaLastnost: " + ob.MojaLastnost); }

Vaja: /*Deklarirajmo razred Oseba z dvema poljema (ime in priimek) ter javno metodo za nastavljanje vrednosti obeh polj. Razred naj ima tudi lastnost PolnoIme, za prirejanje in vračanje polnega imena osebe (imena in priimka). Ustvarimo nov objekt in demonstrirajmo uporabo lastnosti PolnoIme*/ class Oseba { private string priimek; //zasebno polje razreda Oseba private string ime; //zasebno polje razreda Oseba public void NastaviIme(string ime, string priimek) //javna metoda razreda { this.priimek = priimek; this.ime = ime; } public string PolnoIme //javna lastnost razreda { get { return ime + " " + priimek; } set { string zacasna = value; string[] imena = zacasna.Split(' '); //Celotno ime razdelimo na posamezne besede //in jih spravimo tabelo ime = imena[0]; //za ime vzamemo prvi string v tabeli priimek = imena[imena.Length - 1]; //za priimek vzamemo drugi string v tabeli } } } private void button1_Click(object sender, EventArgs e) { Oseba politik = new Oseba(); politik.NastaviIme("Nelson", "Mandela"); MessageBox.Show("Polno ime osebe je " + politik.PolnoIme); //Izpis: Polno ime osebe je // Neslon Mandela politik.PolnoIme = "George Walker Bush"; MessageBox.Show("Polno ime osebe je " + politik.PolnoIme); //Izpis: Polno ime osebe je // George Bush politik.PolnoIme = "France Prešeren"; MessageBox.Show("Polno ime osebe je " + politik.PolnoIme); //Izpis: Polno ime osebe je // France Prešeren }

Vaja: Za tabelarični prikaz podatkov pogosto uporabljamo gradnik DataGridView. Naslednja vaja prikazuje, kako lahko ta gradnik preko lastnosti DataSource povežemo z netipizirano zbirko objektov (ali pa s tabelo objektov).

Na obrazec postavimo gradnik MenuStrip z dvema opcijama: Obdelava-skupno število artikov in Artikli (ta ima še dve podopciji), preostali del obrazca pa zapolnimo z gradnikom DataGridView.

Kreirajmo razred artikel, ki mora vsebovati toliko lastnosti (property-jev), kolikor stolpcev želimo imeti v gradnikih DataGridView. private class artikel { public string naziv; int kolicina; double cena;

Page 73: PROŠNJA - uranic.tsckr.siuranic.tsckr.si/VISUAL C#/VISUAL C#.pdf · MICROSOFT VISUAL C# .NET Srečo Uranič . Visual C# . ... Za začetek novega projekta v okolju Visual C# Standard

Visual C# . NET

Algoritmi in programski jeziki – 4.letnik

73

//Konstruktor public artikel(string naziv, int kolicina, double cena) { this.naziv=naziv; this.kolicina=kolicina; this.cena=cena; } //Property-je potrebujemo zaradi povezovanja gradnika DataGridView public string Naziv { get { return naziv; } //Potrebujemo le get del } public int Kolicina { get { return kolicina; }//Potrebujemo le get del } public double Cena { get { return cena; }//Potrebujemo le get del } }

V dogodek Load obrazca zapišemo kodo, v kateri kreiramo netipizirano zbirko Artikli. Zbirko najprej napolnimo z nekaj objekti, nato pa povežemo z grdanikoma DataGridView. private void Form1_Load(object sender, EventArgs e) { //Kreiramo netipizirano zbirko Artikli ArrayList Artikli = new ArrayList(); //Zbirko napolnimo z nekaj objekti Artikli.Add(new artikel("KLadivo", 8, 6.45)); Artikli.Add(new artikel("Žaga", 5, 12.89)); Artikli.Add(new artikel("Omara", 9, 33.99)); Artikli.Add(new artikel("Radiator", 19, 45.77)); Artikli.Add(new artikel("Kolo", 2, 290.74)); Artikli.Add(new artikel("Stikalo", 30, 0.89)); Artikli.Add(new artikel("Polica", 4, 1.88)); //gradnik DataGridView1 preko lastnosti DataSource povežemo z zbirko Artikli dataGridView1.DataSource = Artikli; //Podatke lahko v DataGridView prenesemo tudi iz tabele objektov npr. /*artikel[] tabela = new artikel[] { new artikel("Plašč", 3,120.45), new artikel("Polo majica", 15, 33.99), new artikel("Pulover", 5, 50.99)}; dataGridView2.DataSource = tabela;*/ }

Opcija Obdelava-skupno število artikov v meniju (izračuna in izpis skupnega števila artiklov v tabeli).

private void obdelavaToolStripMenuItem_Click(object sender, EventArgs e) { int skupaj = 0; for (int i = 0; i < dataGridView1.Rows.Count; i++) skupaj = skupaj + Convert.ToInt32(dataGridView1.Rows[i].Cells[1].Value); MessageBox.Show("Skupaj artiklov: " + skupaj.ToString()); }

Opcija Artikli – Dodaj vrtsico: vrstico najprej dodamo v zbirko, nato najprej prekinemo in nato zopet

vzpostavimo mpovezavo med zbirko in gradnikom DataGridView. private void dodajToolStripMenuItem_Click(object sender, EventArgs e) { Artikli.Add(new artikel("ORODJE", 8, 6.45)); dataGridView1.DataSource = null; //Prekinemo povezavo z zbirko dataGridView1.DataSource = Artikli; //Ponovna povezava z ažurirano zbirko }

Opcija Artikli – Briši izbrano vrstico:

private void brišiIzbranoVrsticoToolStripMenuItem_Click(object sender, EventArgs e) { int indeks=dataGridView1.CurrentRow.Index; //Indeks izbrane vrstice

Page 74: PROŠNJA - uranic.tsckr.siuranic.tsckr.si/VISUAL C#/VISUAL C#.pdf · MICROSOFT VISUAL C# .NET Srečo Uranič . Visual C# . ... Za začetek novega projekta v okolju Visual C# Standard

Visual C# . NET

Algoritmi in programski jeziki – 4.letnik

74

Artikli.RemoveAt(indeks); dataGridView1.DataSource = null; dataGridView1.DataSource = Artikli; }

Projekt prevedemo in dobimo takle rezultat (dodano je še sporočilno okno, ki se prikaže ob kliku na

edino opcijo menija).

Kadar je gradnik DataGridView preko lastnosti DataSource povezan z nekim izvorom podatkov, je ažuriranje podatkov v celicah gradnika DataGridView onemogočeno. Ažuriranje lahko izvedemo tako, da vsebino celic najprej prenesemo v gradnike (npr. TextBox), jih tam ažuriramo, nato spremembe zapišemo najprej v zbirko, nato pa vsebino zbirke ponovno prikažemo v gradniku DataGridView. Vaja: Za ogled in spreminjanje lastnosti (property) nekega objekta je v Visual C# namenjen gradnik PropertyGrid. V

naslednji vaji je kreiran razred oseba, iz njega pa izpeljan objekt P1. Na obrazec smo postavili gradnik PropertyGrid, ki smo mu priredili objekt P1. Visual C# ponuja tudi možnost oblikovanja kategorij v PropertyGridu, kar lahko dosežemo z opcijami CategoryAttribute znotraj deklaracije razreda.

public class oseba { private string ime = ""; private string priimek = ""; private int starost = 0; private string e_Naslov=""; public oseba() { } //prazen konstruktor /*Z CategoryAttribute specificiramo ime katergorije, v katero želimo grupirati lastnost ali dogodek, kadar je le-ta prikazan v gradniku PropertyGrid*/ [CategoryAttribute("Osnovni podatki"), DescriptionAttribute("Ime osebe")] public string Ime { get { return ime; } set { ime = value; } } [CategoryAttribute("Osnovni podatki"), DescriptionAttribute("Priimek osebe")] public string Priimek { get { return priimek; } set { priimek = value; } } [CategoryAttribute("Ostali podatki"), DescriptionAttribute("Starost osebe")] public int Starost { get { return starost; } set { starost = value; } } [CategoryAttribute("Ostali podatki"), DescriptionAttribute("Elektronski naslov")] public string E_Naslov

Page 75: PROŠNJA - uranic.tsckr.siuranic.tsckr.si/VISUAL C#/VISUAL C#.pdf · MICROSOFT VISUAL C# .NET Srečo Uranič . Visual C# . ... Za začetek novega projekta v okolju Visual C# Standard

Visual C# . NET

Algoritmi in programski jeziki – 4.letnik

75

{ get { return e_Naslov; } set { e_Naslov = value; } } } public Form1() { InitializeComponent(); } private void Form1_Load(object sender, EventArgs e) { // Inicializacija testnega razreda in nastavitev podatkov oseba P1 = new oseba(); //kreiramo nov objekt //polja objekta lahko inicializiramo preko njegovih lastnosti P1.Ime = "Anja"; P1.Priimek = "Person"; P1.Starost = 25; P1.E_Naslov = "[email protected]"; //objekt priredimo gradniku property grid propertyGrid1.SelectedObject = P1; }

Za posamezno polje lahko napišemo le eno lastnost, v kateri s pomočjo stavkov get ali set dostopamo oz. nastavljamo vrednosti posameznega polja. Besedici get ali set lahko izpustimo in dobimo write-only ali read-only lastnosti. Primer: class MojRazred { double A = 3; //zasebno polje double B = 4; //zasebno polje public double MojaVrednost //MojaVrednost je ReadOnly: vsebuje le get, ne pa tudi set { get { return A * B; } //lastnost vrne vrednost produkta obeh polj } } private void button1_Click(object sender, EventArgs e) { MojRazred c = new MojRazred(); //nov objekt tipa MojRazred MessageBox.Show("MojaVrednost: "+c.MojaVrednost); //prikaz vrednosti lastnosti //MojaVrednost }

Statične metode

Za lažje razumevanje pojma statična metoda, si oglejmo metodo Sqrt razreda Math. Če pomislimo na to, kako smo jo v vseh dosedanjih primerih uporabili (poklicali), potem je v teh klicih nekaj čudnega. Metodo Sqrt smo namreč vselej poklicali tako, da smo pred njo navedli ime razreda (Math.Sqrt) in ne tako, da bi najprej naredili nov objekt tipa Math, pa potem nad njim poklicali metodo Sqrt. Kako je to možno? Pogosto se bomo srečali s primeri, ko metode ne bodo pripadale objektom (instancam) nekega razreda. To so uporabne metode, ki so tako pomembne, da so neodvisne od kateregakoli objekta. Metoda Sqrt je tipičen primer take metode. Če bila metoda Sqrt običajna metoda objekta izpeljanega iz nekega razreda, potem bi za njeno uporabo morali najprej kreirati nov objekt tipa Math, npr takole: Math m = new Math(); double d = m.Sqrt(42.24);

Tak način uporabe metode pa bi bil neroden. Vrednost, ki jo v tem primeru želimo izračunati in uporabiti, je namreč neodvisna od objekta. Podobno je pri ostalih metodah tega razreda (npr. Sin, Cos, Tan, Log, …). Razred

Page 76: PROŠNJA - uranic.tsckr.siuranic.tsckr.si/VISUAL C#/VISUAL C#.pdf · MICROSOFT VISUAL C# .NET Srečo Uranič . Visual C# . ... Za začetek novega projekta v okolju Visual C# Standard

Visual C# . NET

Algoritmi in programski jeziki – 4.letnik

76

Math prav tako vsebuje polje PI (iracionalno število Pi), za katerega uporabo bi potemtakem prav tako potrebovali nov objekt. Rešitev je v t.i. statičnih poljih oz.metodah. V C# morajo biti vse metode deklarirane znotraj razreda. Kadar pa je metoda ali pa polje deklarirano kot statično (static), lahko tako metodo ali pa polje uporabimo tako, da pred imenom polja oz. metode navedemo ime razreda. Metoda Sqrt (in seveda tudi druge metode razreda Math) je tako znotraj razreda Math deklarirana kot statična, takole: class Math {

public static double Sqrt(double d) { . . . } }

Zapomnimo pa si, da statično metodo ne kličemo tako kot objekt. Kadar definiramo statično metodo, le-ta nima dostopa do kateregakoli polja definiranega za ta razred. Uporablja lahko le polja, ki so označena kot static (statična polja). Poleg tega, lahko statična metoda kliče le tiste metode razreda, ki so prav tako označene kot statične metode. Ne-statične metode lahko, kot vemo že od prej, uporabimo le tako, da najprej kreiramo nov objekt. Primer: Napišimo razred točka in v njem statično metodo, katere naloga je le ta, da vrne string, v katerem so zapisane osnovni podatki o tem razredu class Tocka { public static string Navodila()//Statična metoda { string stavek="Vsaka točka ima dve koordinati/polji: x je abscisa, y je ordinata!"; return stavek; } }

//Zato, da pokličemo statično metodo oz. statično polje NE POTREBUJEMO OBJEKTA!!! //Primer klica npr. ob nekem odzivnem dogodku: MessageBox.Show(Tocka.Navodila());

Statična polja

Tako kot obstajajo statične metode, obstajajo tudi statična polja. Včasih je npr. je potrebno, da imajo vsi objekti določenega razreda dostop do istega polja. To lahko dosežemo le tako, da tako polje deklariramo kot statično. Primer: class Test { public const double Konstanta = 20;//Statično polje } //Zato, da pokličemo statično metodo oz. statično polje NE POTREBUJEMO OBJEKTA!!! MessageBox.Show Test.Konstanta);//klic statičnega polja

Vaja: class Nekaj { public static int stevilo=0; //javno statično polje – inicializacija je obvezna public Nekaj() //Konstruktor

Page 77: PROŠNJA - uranic.tsckr.siuranic.tsckr.si/VISUAL C#/VISUAL C#.pdf · MICROSOFT VISUAL C# .NET Srečo Uranič . Visual C# . ... Za začetek novega projekta v okolju Visual C# Standard

Visual C# . NET

Algoritmi in programski jeziki – 4.letnik

77

{ stevilo++; //število objektov se je povečalo } public void Hello() { if (stevilo>3) { MessageBox.Show("Sedaj smo pa že več kot trije! Skupaj nas je že "+stevilo); } switch (stevilo) { case 1 : MessageBox.Show("V tej vrstici sem sam !!"); break; case 2 : MessageBox.Show("Aha, še nekdo je tukaj, v tej vrstici sva dva!!"); break; case 3 : MessageBox.Show("Opala, v tej vrstici smo že trije."); break; } } } private void button1_Click(object sender, EventArgs e) { Nekaj a=new Nekaj(); //konstruktor za a a.Hello(); { Nekaj b=new Nekaj(); //konstruktor za b b.Hello(); { Nekaj c=new Nekaj(); //konstruktor za c c.Hello(); { Nekaj d=new Nekaj(); //konstruktor za d d.Hello(); } } } } //Konec programa-> Garbage Collector poskrbi za uničenje vseh objektov

Polje razreda je lahko statično a se njegova vrednost ne more spremeniti: pri deklaraciji takega statičnega polja zapišemo besedico const (const = Constant – konstanta). Besedica static pri deklaraciji konstantnega polja &I potrebna, pa je polje še vedno statično. Konstantno polje je torej avtomatično statično in je tako dostopno preko imena razreda in ne preko imena objekta! Vrednost statičnega polja se &E da spremeniti. Tako je npr. deklarirana konstanta PI razreda Math, tako so npr. tudi deklarirana polja razreda Color! Primer: class Test { public static double kons1 = 3.14; //Statično polje public const double kons = 3.1416; //Konstantno polje je avtomatično tudi statično . . . } //Zato, da pokličemo statično polje NE POTREBUJEMO OBJEKTA!!! MessageBox.Show(Test.kons1+"\n"+ Test.kons);//klic statičnega polja

Test.kons1= Test.kons1 + 1; //OK -> Statično polje LAHKO spremenimo Test.kons= Test.kons + 1; //NAPAKA -> Konstantnega polja ne moremo spremeniti

Indekserji - Indexers

Pokazali smo že, da do zasebnih polj lahko dostopamo in jih spreminjamo s pomočjo lastnosti/Property-jev. Drugi način pa je uporaba indekserjev – indexers. Indekser omogoča, da do vrednosti polj znotraj objektov lahko dostopamo tudi preko indeksov tega objekta - z objektom tako lahko upravljamo podobno kot s tabelo. Primer:

Page 78: PROŠNJA - uranic.tsckr.siuranic.tsckr.si/VISUAL C#/VISUAL C#.pdf · MICROSOFT VISUAL C# .NET Srečo Uranič . Visual C# . ... Za začetek novega projekta v okolju Visual C# Standard

Visual C# . NET

Algoritmi in programski jeziki – 4.letnik

78

class stevila { private int st1, st2, st3,st4;//razred ima štiri zasebna polja enakega tipa //indekser omogoča dostop in nastavljanje polj istega tipa public int this[int index] { get { switch (index) { case 0: return st1; case 1: return st2; case 2: return st3; default:return 0; } } set { switch (index) { case 0: st1 = value; break; case 1: st2 = value; break; case 2: st3 = value; break; } } } }

private void button1_Click(object sender, EventArgs e) { //nov objekt izpeljan iz razreda stevila; vsa polja dobijo vrednost 0 stevila ocene=new stevila(); //zasebna polja lahko spremenimo s pomočjo indeksa ocene[0]= 1; //ocene[0] se nanaša na zasebno polje st1 ocene[1] = 2; //ocene[1] se nanaša na zasebno polje st2 ocene[2] = 3; //ocene[2] se nanaša na zasebno polje st3 MessageBox.Show(ocene[0]+" "+ocene[1]+" "+ocene[2]); //izpis 1 2 3 0 }

Indekserji torej omogočajo indeksiranje članov/instanc razreda in struktur na podoben način, kot je to pri tabelah. Predstavljajo razširitev lastnosti – property-jev, le da njihovi pristopniki (accesors) uporabljajo parametre. S pomočjo pristopnika (accessor) get nam indekser vrača vrednost nekega polja, s pomočjo accessor-ja set pa vrednost polju prireja. Za definicijo indekserja uporabljamo rezervirano besedico this. Indeks (parameter) v indekserju je lahko poljubnega tipa, indekserji pa so lahko tudi preobloženi. Lahko ima tudi več kot en parameter, npr. pri dvodimenzionalnih tabelah. Uvedba indekserja dobi torej še poseben pomen tedaj, kadar so polja tabelaričnega tipa. Primer: Razred Temperatura naj vsebuje tabelo polj z imenom temperature – to naj bo tabela desetih polj tipa float. Do teh polj bomo lahko dostopali in jim spreminjali vrednosti s pomočjo indekserja. class Temperatura { // Tabela temperatur v stopinjah Celzija private float[] temperature = new float[10] { 26.2F, 26.7F, 26.5F, 26.9F, 28.8F, 21.3F, 25.9F, 32.1F, 29.2F, 27.5F }; // Lastnost Length vrne velikost tabele temperatur public int Length { get { return temperature.Length; } }

// Deklaracija Indekserja - Če bo index izven območja tabele, pride do izjeme public float this[int index] { get { return temperature[index]; }

Page 79: PROŠNJA - uranic.tsckr.siuranic.tsckr.si/VISUAL C#/VISUAL C#.pdf · MICROSOFT VISUAL C# .NET Srečo Uranič . Visual C# . ... Za začetek novega projekta v okolju Visual C# Standard

Visual C# . NET

Algoritmi in programski jeziki – 4.letnik

79

set { temperature[index] = value; } } }

Prikažimo še primer uporabe zgornjega indekserja. Kreirajmo nov objekt temp1 izpeljan iz razreda Temperatura in s pomočjo indekserja najprej spremenimo dva podatka v tej tabeli, nato pa s pomočjo indekserja tabelo še izpišimo. Temperatura temp1 = new Temperatura(); // Uporaba indexerjevega pristopnika (accessor-ja) set temp1[3] = 58.3F; temp1[5] = 60.1F; // Uporaba indexerjevega pristopnika (accessor-ja) get string izpis = ""; for (int i = 0; i < temp1.Length; i++) izpis += temp1[i] + "\n"; MessageBox.Show(izpis); // rezultat prikažemo v sporočilnem oknu

Vaja: /*Kreirajmo razred Slovarcek, ki naj vsebuje zasebno tabelo polj, statično polje za

ugotavljanje velikosti te tabele, konstruktor za inicializacijo tabele polj, ter indekser za dostop in spreminjanje elementov te tabele.*/ public class Slovarcek { private string[] besede = new string[velikost]; static public int velikost = 10;//statično polje->dostopno bo preko imena razreda public Slovarcek() // Konstruktor { for (int x = 0; x < velikost; x++ ) besede[x] = String.Format("Beseda{0}", x); } //Deklaracija indekserja public string this[int index] { get { string tmp; if( index >= 0 && index <= velikost-1 )//preverimo, če je indeks znotraj tabele tmp = besede[index]; else tmp = ""; return ( tmp ); } set { if (index >= 0 && index <= velikost - 1)//preverimo, če je indeks znotraj tabele besede[index] = value; } } } //še implementacija razreda in indekserja, ki je v našem primeri vezana na dogodek Load obrazca, na katerem imamo gradnik ListBox z imenom listBox1. private void Form1_Load(object sender, EventArgs e) { //kreiramo nov objekt. Obenem se izvede konstruktor, ki inicializita tabelo polj Slovarcek moj = new Slovarcek(); //poljem z indeksi od 3 do 7 s pomočjo indekserja priredimo nove vrednosti moj[3] = "====="; moj[4] = "Tri"; moj[5] = "nove"; moj[6] = "besede!"; moj[7] = "=====";

Page 80: PROŠNJA - uranic.tsckr.siuranic.tsckr.si/VISUAL C#/VISUAL C#.pdf · MICROSOFT VISUAL C# .NET Srečo Uranič . Visual C# . ... Za začetek novega projekta v okolju Visual C# Standard

Visual C# . NET

Algoritmi in programski jeziki – 4.letnik

80

//tabelo polj objekta moj dodamo v ListBox z imenom listBox1 for (int x = 0; x < Slovarcek.velikost; x++) listBox1.Items.Add(moj[x]); }

Vaja: Naslednji primer prikazuje, kako lahko s pomočjo operacij nad biti in indekserja spremenljivko tipa int uporabljamo kot tabelo 32 bitov. Na ta način bomo lahko posamezne bite znotraj 32 bitnega celega števila nastavljali kar s pomočjo indeksa. Bit z indeksom 6 znotraj celega števila biti, bomo tako nastavili na true kar s stavkom: int biti[6] = true;

Indekser nam bo tudi omogočil, da poljubno celo število izpišemo v binarni obliki, torej kot zaporedje ničel in enk! struct bitniInt //indekser lahko zapišemo tudi v strukturi { private int biti; //zasebno polje public bitniInt(int zacVredBitov) //konstruktor { biti = zacVredBitov; } //indekser bo omogočil dostop in nastavitev vrednosti posameznim bitom znotraj celega št. public bool this[int index] { get { return (biti &(1 << index))!=0;} set { if (value) //postavimo bit na 1, če je vrednost true, sicer 0 biti |= (1 << index); else biti &= (1 << index); } } }

//primer uporabe int test=1; bitniInt biti=new bitniInt(test); //kreiramo nov objekt izpeljan iz strukture bitniInt MessageBox.Show("Vrednost polja biti je " + biti.biti);//izpis: Vrednost polja biti je 1 //s pomočjo indekserja lahko pridobivamo vrednosti posameznega bita znotraj spremenljivke tipa //int, posameznim bitom lahko tudi nastavljamo vrednosti bool bit6=biti[6]; //pridobivanje vrednosti tipa bool pri bitu št. 6 biti[1]=true;//nastavimo 1. bit na true (biti postane 00000000000000000000000000000011) biti[2] = true;//nastavimo 2. bit na true(biti postane 00000000000000000000000000000111 = 7 //desetiško) //primer uporabe našega indekserja za izpis celega števila v binarni obliki string bitniTest=""; for (int i = 31; i >= 0; i--) { if (biti[i] == true) bitniTest += "1"; else bitniTest += "0"; } MessageBox.Show(biti.biti + " v binarnem zapisu = " + bitniTest);//3 v binarnem zapisu je //00000000000000000000000000000011

Dedovanje (Inheritance) – izpeljani razredi

Kaj je to dedovanje

Page 81: PROŠNJA - uranic.tsckr.siuranic.tsckr.si/VISUAL C#/VISUAL C#.pdf · MICROSOFT VISUAL C# .NET Srečo Uranič . Visual C# . ... Za začetek novega projekta v okolju Visual C# Standard

Visual C# . NET

Algoritmi in programski jeziki – 4.letnik

81

Dedovanje (Inheritance) je ključni koncept objektno orientiranega programiranja. Smisel in pomen dedovanja je v tem, da iz že zgrajenih razredov skušamo zgraditi bolj kompleksne, ki bodo znali narediti nekaj uporabnega. Dedovanje je torej orodje, s katerim se izognemo ponavljanju pri definiranju različnih razredov, ki pa imajo več ali manj značilnosti skupnih. Opredeljuje torej odnos med posameznimi razredi. Vzemimo pojem sesalec iz biologije. Kot primer za sesalce vzemimo npr. konje in kite. Tako konji kot kiti počnejo vse kar počnejo sesalci nasploh (dihajo zrak, skotijo žive mladiče, …), a prav tako pa imajo nekatere svoje značilnosti (konji imajo npr. štiri noge, ki jih kiti nimajo, imajo kopita, …, obratno pa imajo npr. kiti plavuti, ki pa jih konji nimajo, ..). V Microsoft C# bi lahko za ta primer modelirali dva razreda: prvega bi poimenovali Sesalec, in drugega Konj, in obenem deklarirali, da je Konj podeduje (inherits) Sesalca. Na ta način bi med sesalci in konjem vzpostavili povezavo v tem smislu, da so vsi konji sesalci (obratno pa seveda ne velja!). Podobno lahko deklariramo razred z imenom Kit, ki je prav tako podedovan iz razreda Sesalec. Lastnosti, kot so npr, kopita ali pa plavuti pa lahko dodatno postavimo v razred Konj oz. razred Kit.

Bazični razredi in izpeljani razredi

Sintaksa, ki jo uporabimo za deklaracijo, s katero želimo povedati, da razred podeduje nek drug razred, je takale: class IzpeljaniRazred : BazičniRazred { . . . }

Izpeljani razred deduje od bazičnega razreda. Za razliko od C++, lahko razred v C# deduje največ en razred in ni možno dedovanje dveh ali več razredov. Seveda pa je lahko razred, ki podeduje nek bazični razred, zopet podedovan v še bolj kompleksen razred. Primer: Radi bi napisali razred, s katerim bi lahko predstavili točko v dvodimenzionalnem koordinatnem sistemu. Razred poimenujmo Tocka: class Tocka //bazični razred { public Tocka(int x, int y) //konstruktor { //telo konstruktorja } //telo razreda Tocka }

Sedaj lahko definiramo razred za tridimenzionalno točko z imenom Tocka3D, s katerim bomo lahko delali objekte, ki bodo predstavljali točke v tridimenzionalnem koordinatnem sistemu in po potrebi dodamo še dodatne metode: class Tocka3D : Tocka //razred Tocka3D podeduje razred Tocka { //telo razreda Tocka3D – tukaj zapišemo še dodatne metode tega razreda! }

Klic konstruktorja bazičnega razreda

Vsi razredi imajo vsaj en konstruktor (če ga ne napišemo sami, nam prevajalnik zgenerira privzeti konstruktor). Izpeljani razred avtomatično vsebuje vsa polja bazičnega razreda, a ta polja je potrebno ob kreiranju novega objekta inicializirati. Zaradi tega mora konstruktor v izpeljanem razredu poklicati konstruktor svojega bazičnega razreda. V ta namen se uporablja rezervirana besedica base: class Tocka3D : Tocka ////razred Tocka3D podeduje razred Tocka { public Toca3D(int z) :base(x,y) //klic bazičnega konstruktorja Tocka(x,y)

Page 82: PROŠNJA - uranic.tsckr.siuranic.tsckr.si/VISUAL C#/VISUAL C#.pdf · MICROSOFT VISUAL C# .NET Srečo Uranič . Visual C# . ... Za začetek novega projekta v okolju Visual C# Standard

Visual C# . NET

Algoritmi in programski jeziki – 4.letnik

82

{ //telo konstruktorja Tocka3D } //telo razreda Tocka3D }

Če bazičnega konstruktorja v izpeljanem razredu ne kličemo eksplicitno (če vrstice :base(x,y) ne napišemo!), bo prevajalnik avtomatično zgeneriral privzeti konstruktor. Ker pa vsi razredi nimajo privzetega konstruktorja (v veliko primerih napišemo lastni konstruktor), moramo v konstruktorju znotraj izpeljanega razreda obvezno najprej klicati bazični konstruktor (rezervirana besedica base). Če klic izpustimo (ali ga pozabimo napisati) bo rezultat prevajanja compile-time error: class Tocka3D : Tocka //razred Tocka3D podeduje razred Tocka { public Tocka3D(int z) //NAPAKA - POZABILI smo klicati bazični konstruktor razreda Tocka { //telo konstruktorja Tocka3D } //telo razreda Tocka3D }

Določanje oz. prirejanje razredov

Poglejmo še, kako lahko kreiramo objekte iz izpeljanih razredov. Kot primer vzemimo zgornji razred Tocka in iz njega izpeljani razred Tocka3D. class Tocka //bazični razred { //telo razreda Tocka }

class Tocka3D: Tocka //razred Tocka3D podeduje razred Tocka { //telo razreda Tocka3D }

Iz razreda Tocka izpeljimo še dodatni razred Daljica class Daljica:Tocka //razred Daljica podeduje razred Tocka { //telo razreda Daljica }

Kreirajmo sedaj nekaj objektov: Tocka A = new Tocka();

Tocka3D B = A; //NAPAKA - različni tipi Tocka3D C = new Tocka3D ();

Tocka D = C; //OK!

&ove metode – operator new

Razredi lahko vsebujejo več ali manj metod in slej ko prej se lahko zgodi, da se pri dedovanju v izpeljanih razredih ime metode ponovi – v izpeljanem razredu torej napišemo metodo, katere ime, število in tipi parametrov se ujemajo z metodo bazičnega razreda. Pri prevajanju bomo zato o tem dobili ustrezno opozorilo - warning. Metoda v izpeljanem razredu namreč v tem primeru prekrije metodo bazičnega razreda. Če npr. napišemo razred Tocka in nato iz njega izpeljemo razred Tocka3D, class Tocka //bazni razred {

Page 83: PROŠNJA - uranic.tsckr.siuranic.tsckr.si/VISUAL C#/VISUAL C#.pdf · MICROSOFT VISUAL C# .NET Srečo Uranič . Visual C# . ... Za začetek novega projekta v okolju Visual C# Standard

Visual C# . NET

Algoritmi in programski jeziki – 4.letnik

83

private int x, y; //polji razreda Tocka public void Izpis() //metoda za izpis koordinat razreda Tocka { MessageBox.Show( "Koordinate točke:\nx = "+x + "\ny = " + y); } } class Tocka3D : Tocka //razred Tocka3D je izpeljan iz razreda Tocka { private int z; //dodatno polje razreda Tocka3D //Metoda Izpis prepiše istoimensko metodo razreda Tocka public void Izpis() { MessageBox.Show("z = "+z+"\n"); } }

nam bo prevajalnik zgeneriral opozorilo, s katerim nas obvesti, da metoda Tocka3D.Izpis prekrije metodo Tocka.Izpis:

Program se bo sicer prevedel in tudi zagnal, a opozorilo moramo vzeti resno. Če namreč napišemo razred, ki bo podedoval razred Tocka3D, bo uporabnik morda pričakoval, da se bo pri klicu metode Izpis pognala metoda bazičnega razreda, a v našem primeru se bo zagnala metoda razreda Tocka3D. Problem seveda lahko rešimo tako, da metodo Izpis v izpeljanem razredu preimenujemo (npr Izpis1), še boljša rešitev pa je ta, da v izpeljanem razredu eksplicitno povemo, da gre za NOVO metodo – to storimo z uporabo operatorja new. class Tocka //bazni razred { private int x, y; //polji razreda Tocka public void Izpis() //metoda za izpis koordinat razreda Tocka { MessageBox.Show( "Koordinate točke:\nx = "+x + "\ny = " + y); } } class Tocka3D : Tocka //razred Tocka3D je izpeljan iz razreda Tocka { private int z; //dodatno polje razreda Tocka3D //Z operatorjem new napovemo, da ima razred Tocka3D SVOJO LASTNO metodo Izpis new public void Izpis() { MessageBox.Show("z = "+z+"\n"); } }

Vaja: /*Napiši razred krog z zasebnim poljem polmer in lastnostmi Premer, Obseg in Kvadratura. Iz

razreda nato izpelji razred Krogla, dodaj razredu lastno metodo Kvadratura in novo metodo

Volumen*/

class Krog //Bazični razred { private double polmer;//zasebno polje razreda Krog public double Polmer //lastnost za dostop do polja polmer { get { if (polmer < 0) return 0.00; else return polmer; }

Page 84: PROŠNJA - uranic.tsckr.siuranic.tsckr.si/VISUAL C#/VISUAL C#.pdf · MICROSOFT VISUAL C# .NET Srečo Uranič . Visual C# . ... Za začetek novega projekta v okolju Visual C# Standard

Visual C# . NET

Algoritmi in programski jeziki – 4.letnik

84

set { polmer = value;} } public double Premer //lastnost, ki vrne premer kroga { get { return Polmer * 2; } } public double Obseg //lastnost, ki vrne obseg kroga { get { return Premer * Math.PI; } } public double Kvadratura //lastnost, ki vrne ploščino kroga { get { return Math.PI*Math.Pow(Polmer,2); } } } /*Kreirajmo razred Krogla ki naj podeduje razred Krog. Razred Krogla bo podedoval polje polmer, podedoval bo lastnosti Premer in Obseg, imel bo SVOJO lastnost za izračun kvadrature, poleg tega pa še novo lastnost za izračun prostornine krogle */

class Krogla : Krog //Razred Krogla deduje razred Krog { new public double Kvadratura //z operatorjem new smo označili, da ima razred krog SVOJO //lastno lastnost kvadratuta { get { return 4 * Math.PI*Math.Pow(Polmer,2); } } public double Volumen //nova lastnost razreda Krogla { get { return 4 * Math.PI * Math.Pow(Polmer,2) / 3; } } }

//implementacija obeh razredov ob klicu neke odzivne metode: Krog c = new Krog();//nov objekt razreda Krog c.Polmer = 25.55; Krogla s = new Krogla();//nov objekt razreda Krogla s.Polmer = 25.55; MessageBox.Show("Karakteristike kroga\nPolmer: " + c.Polmer + "\nPremer: " + c.Premer + "\nObseg: " + c.Obseg + "\nKvadratura: " + c.Kvadratura+ "\n\nKarakteristike krogle\nPolmer: " + s.Polmer + "\nPremer: " + s.Premer + "\nObseg: " +s.Obseg+ "\nKvadratura: " + s.Kvadratura+"\nVolumen: +s.Volumen);

Virtualne metode

Pogosto želimo metodo, ki smo je napisali v bazičnem razredu, v višjih (izpeljanih) razredih skriti in napisati novo metodo, ki pa bo imela enako ime in enake parametre. Eden izmed načinov je uporaba operatorja new za tako metodo, drug način pa je z uporabo rezervirane besede virtual. Metodo, za katero želimo že v bazičnem razredu označiti, da jo bomo lahko v nadrejenih razredih nadomestili z novo metodo (jo prekriti z drugo metodo z istim imenom), označimo kot virtualno (virtual), npr.: //virtualna metoda v bazičnem razredu – v izpeljanih razredih bo lahko prerit(override) public virtual void Koordinate() {...}

V nadrejenem razredu moramo v takem primeru pri metodi z enakim imenom uporabiti rezervirano besedico override, s katero povemo, da bo ta metoda prekrila/prepisala bazično metodo z enakim imenom in enakimi parametri. //izpeljani razred – besedica override pomeni, da smo s to metodo prekrili bazično metodo public override void Koordinate() { ...}

Ločiti pa moramo razliko med tem, ali neka metoda prepiše bazično metodo (override) ali pa jo skrije. Prepisovanje metode (overriding) je mehanizem, kako izvesti drugo, novo implementacijo iste metode – virtualne in override metode so si v tem primeru sorodne, saj se pričakuje, da bodo opravljale enako nalogo, a

Page 85: PROŠNJA - uranic.tsckr.siuranic.tsckr.si/VISUAL C#/VISUAL C#.pdf · MICROSOFT VISUAL C# .NET Srečo Uranič . Visual C# . ... Za začetek novega projekta v okolju Visual C# Standard

Visual C# . NET

Algoritmi in programski jeziki – 4.letnik

85

nad različnimi objekti (izpeljanimi iz bazičnih razredov, ali pa iz podedovanih razredov). Skrivanje metode (hiding) pa pomeni, da želimo neko metodo nadomestiti z drugo – metode v tem primeru niso povezane in lahko opravljajo povsem različne naloge. Primer: Naslednji primer prikazuje zapis virtualne metode Koordinate() v bazičnem razredu in override metode Koordinate() v izpeljanem razredu. class Tocka //bazični razred { private int x, y; //polji razreda Tocka //metoda Koordinate je virtualna, kar pomeni, da jo lahko prepišemo (override) public virtual void Koordinate() //metoda za izpis koordinat razreda Tocka { MessageBox.Show( "Koordinate točke:\nx = "+x + "\ny = " + y); } } class Tocka3D : Tocka //razred Tocka3D je izpeljan iz razreda Tocka { private int z; //dodatno polje razreda Tocka3D //Metoda Koordinate je označena kot override - prepiše istoimensko metodo razreda Tocka public override void Koordinate() { base.Koordinate(); //klic bazične metode Koordinate razreda Tocka MessageBox.Show("z = "+z+"\n"); } }

Prekrivne (Override) metode

Kadar je bazičnem razredu neka metoda označena kot virtualna (virtual), jo torej lahko v nadrejenih razredih prekrijemo/povozimo (override). Pri deklaraciji takih metod (pravimo jim tudi polimorfne metode) z uporabo rezerviranih besed virtual in override, pa se moramo držati nekaterih pomembnih pravil:

• Metoda tipa virtual oz. override NE more biti zasebna (ne more biti private). • Obe metodi, tako virtualna kot override morata biti identični: imeti morata enako ime, enako število in

tip parametrov in enak tip vrednosti, ki jo vračata. • Obe metodi morata imeti enak dostop. Če je npr. virtualna metoda označena kot javna (public), mora

biti javna tudi metoda override. • Prepišemo (prekrijemo/povozimo) lahko le virtualno metodo. Če metoda ni označena kot virtualna in

bomo v nadrejenem razredu skušali narediti override, bomo dobili obvestilo o napaki. • Če v nadrejenem razredu ne bomo uporabili besedice override, bazična metoda ne bo prekrita. To pa

hkrati pomeni, da se bo tudi v izpeljanem razredu izvajala metoda bazičnega razreda in ne tista, ki smo napisali v izpeljanem razredu.

• Če neko metodo označimo kot override, jo lahko v nadrejenih razredih ponovno prekrijemo z novo metodo.

Vaja: Razred Tocka in razred Tocka3D, ki je izpeljan iz razreda Tocka sta implementirana v naslednjem primeru: class Tocka //bazni razred { private int x, y; //polji razreda Tocka public Tocka(int x,int y) //konstruktor { this.x = x; this.y = y;

Page 86: PROŠNJA - uranic.tsckr.siuranic.tsckr.si/VISUAL C#/VISUAL C#.pdf · MICROSOFT VISUAL C# .NET Srečo Uranič . Visual C# . ... Za začetek novega projekta v okolju Visual C# Standard

Visual C# . NET

Algoritmi in programski jeziki – 4.letnik

86

} //metoda Koordinate je virtualna, kar pomeni, da jo lahko preobložimo (naredimo override) public virtual void Koordinate() //metoda za izpis koordinat razreda Tocka { MessageBox.Show( "Koordinate točke:\nx = "+x + "\ny = " + y); } } class Tocka3D : Tocka //razred Tocka3D je izpeljan iz razreda Tocka { private int z; //dodatno polje razreda Tocka3D public Tocka3D(int x,int y,int z) //konstruktor - parametra x in y potrebujemo za : base(x,y) //dedovanje bazičnega konstruktorja razreda Tocka { this.z = z; } //Metoda Koordinate prepiše istoimensko metodo razreda Tocka public override void Koordinate() { base.Koordinate(); //klic bazične metode Koordinate razreda Tocka MessageBox.Show("z = "+z+"\n } } static void Main(string[] args) { Tocka A = new Tocka(1,1); //Nov objekt razreda Tocka A.Koordinate(); //klic metode Koordinate razreda Tocka Tocka3D A3D = new Tocka3D(1, 2, 3); //Nov objekt razerda Tocka3D A3D.Koordinate(); //klic preobložene metode Koordinate razreda Tocka3D }

Vaja: /*Napišimo razred krog z zasebnim poljem polmer in virtualno metodo ploscina, nato pa še razred kolobar. Razred kolobar naj deduje razred krog, dodano naj ima še eno zasebno polje notranjiPolmer in svojo lastno(override) metodo polščina*/ class krog //bazni razred { private int polmer; //polje razreda krog //metoda ploscina je virtualna, kar pomeni, da jo lahko preobložimo (override) public virtual double ploscina() { return Math.PI * polmer * polmer; } public int Polmer //lastnost(property) razreda krog, za dostop in inic. polja polmer { get { return polmer; } set { polmer = value; } } } class kolobar : krog //razred kolobar podeduje razred krog { //ker kolobar deduje krog, že pozna polje polmer private int notranjiPolmer; //dodatno polje razreda kolobar //metoda ploscina prepiše istoimensko metodo bazičnega razreda krog public override double ploscina() { return Math.PI * (Polmer * Polmer - notranjiPolmer*notranjiPolmer); } //ker kolobar deduje krog, že pozna njegovo lastnost Polmer //dodatna lastnost (property) razreda kolobar, za dostop in inic. polja notranjiPolmer public int NotranjiPolmer

Page 87: PROŠNJA - uranic.tsckr.siuranic.tsckr.si/VISUAL C#/VISUAL C#.pdf · MICROSOFT VISUAL C# .NET Srečo Uranič . Visual C# . ... Za začetek novega projekta v okolju Visual C# Standard

Visual C# . NET

Algoritmi in programski jeziki – 4.letnik

87

{ get { return notranjiPolmer; } set { notranjiPolmer = value; } } }

Vaja: //Deklarirajmo razred Oseba, nato pa razred Kmetovalec, ki naj podeduje razred Oseba. Razred Oseba naj ima polje ime, razred Kmetovalec pa še dodatno polje velikostPosesti. Za oba razreda napišimo tudi konstruktor in virtualno metodo ToString za izpis podatkov o posameznem objektu!*/ public class Oseba //bazični razred { public string ime; //bazično polje public Oseba(string ime) //bazični konstruktor { this.ime = ime; } public virtual string ToString() //virtualna metoda bazičnega polja { return "Ime=" + ime; } } public class Kmetovalec : Oseba //razred Kmetovalec deduje razred Oseba { int velikostPosesti; //dodatno polje razreda Kmetovalec public Kmetovalec(string ime, int velikostPosesti) //konstruktor razreda Kmetovalec : base(ime) //podeduje bazično polje ime { this.velikostPosesti = velikostPosesti; } public override string ToString() //metoda ToString prekrije bazično metodo ToString { return base.ToString() + "; Kvadratnih metorv = " + velikostPosesti.ToString(); } }

Naloge:

Napiši razred Kocka tako, da bo izpeljan iz razreda Kvadrat. Razreda naj poleg svojih podatkov vsebujeta še privzeti konstruktor, metode za postavitev vrednosti podatkov in metode za izračun površine, obsega in volumna.

Sestavite razred PodatkovniNosilec, ki predstavlja medij za shranjevanje podakov. Vsebuje naj

informacije o kapaciteti medija (celo število, enota je bajt), izdelovalcu (niz), osnovni ceni (celo število, enota je evroCent) in stopnji obdavčitve (celo število, enota je procent). Vsebovati mora vsaj javne metode:

• public PodatkovniNosilec() - konstruktor, ki ustvari nosilec kapacitete 650Mb, proizvajalca

"neznan", s ceno 100 centov in 20% davkom. • public PodatkovniNosilec(string ime, int kapaciteta, int cena, int davek) - konstruktor s parametri. • public void NastaviKapaciteto(int bajti) - nastavi kapaciteto medija v bajtih. Če je kapaciteta

negativna, naj se ne spremeni.

Page 88: PROŠNJA - uranic.tsckr.siuranic.tsckr.si/VISUAL C#/VISUAL C#.pdf · MICROSOFT VISUAL C# .NET Srečo Uranič . Visual C# . ... Za začetek novega projekta v okolju Visual C# Standard

Visual C# . NET

Algoritmi in programski jeziki – 4.letnik

88

• public void NastaviIme(string ime) - nastavi ime izdelovalca medija. Če je ime enako null, naj se ne spremeni.

• public void NastaviCeno(int cena) - nastavi ceno. Če je negativna, naj se ne spremeni. • public void NastaviObdavcitev(int pocent) - nastavi obdavčitev v procentih. Če so procenti izven

meja 0-100, naj se ne stara vrednost ne spremeni. • public int VrniOsnovnoCeno() - vrne osnovno ceno medija (brez davka). • public double VrniDavek() - koliko davka je potrebno plačati za ta medij (v evrih). • public double VrniProdajnoCeno() - vrne osnovno ceno + davek (v evrih). • public string VrniIme() - vrne ime proizvajalca medija. • public double VrniKapaciteto(char enota) - vrni kapaciteto v enoti, ki jo določa znak enota:

o 'b' - bajti o 'k' - kilobajti o 'M' - megabajti o 'G' - gigabajti Pazi na to, da je en kilobajt 1024 (in ne 1000) bajtov!

• public override string ToString() - vrne niz, ki smiselno opisuje razred. Sestavi tudi testni program, v katerem preveriš delovanje tvojega razreda. Podatke za posamezen PodatkovniNosilec zapisuj v neg DataGridView.

Osnova vseh razredov - Object Vsem razredom v C#, ne glede na vrsto, je skupno, da je njihov skupni osnovni razred System.Object. Sem so všteti tudi osnovni in vsi ostali vrednostni tipi (tudi int, float, decimal, …). Bazni razred je neposreden predhodnik (starš) izpeljanega razreda. Bazni razred je lahko osnova naslednjim izpeljanim razredom, pri čemer lahko nastane pravo hierarhično drevo. Izvorni oz. temeljni razred (root) pa je najvišji razred takega hierarhičnega drevesa. V C# je torej temeljni razred Object. Takšno poimenovanje je mogoče nekoliko zmedeno, dokler si v njem ne predstavljamo naokoli obrnjenega drevesa s koreninami (root) na vrhu in izpeljanimi razredi spodaj. Zaradi tega tudi pravimo, da je bazni razred nad izpeljanim razredom. Osnovni razred Object prav tako pozna nekaj metod, ki jih izpeljani razredi lahko uporabijo, bodisi v originalu, bodisi jih prekrijejo s svojimi metodami (override). Te metode so: Metode razreda Object

Metoda Razlaga

Equals() Oceni, ali sta dva objekta enaka ali ne

GetHashCode() Omogoča objektom, da si zagotovijo svojo lastno hash funkcijo, za delo z zbirkami (collections)

GetType() Omogoča dostop do tipa nekega objekta

ToString() Pretvorba objekta v string

Finalize() Čiščenje pomnilnika, zasedena z objektom

MemberwiseClone() Kreiranje kopije objekta; metode naj ne bi nikoli implementirali v naše razrede

Boxing in Unboxing Boxing in Unboxing je proces, ki vrednostnim tipom omogoča da jih lahko obravnavamo kot referenčne tipe. Neko spremenljivko vrednostnega tipa tako lahko pomočjo procesa boxing »boksnemo« v objekt, kasneje pa jo s pomočjo procesa unboxing zopet potegnemo iz objekta ven. Proces Boxing je implicitna konverzija vrednostnega tipa v objektnega. Metoda najprej dodeli del pomnilnika na kopici novi instanci razreda Object, nato pa vanjo skopira vrednost ustrezne vrednostne spremenljivke, ki se sicer nahaja na skladu.

Page 89: PROŠNJA - uranic.tsckr.siuranic.tsckr.si/VISUAL C#/VISUAL C#.pdf · MICROSOFT VISUAL C# .NET Srečo Uranič . Visual C# . ... Za začetek novega projekta v okolju Visual C# Standard

Visual C# . NET

Algoritmi in programski jeziki – 4.letnik

89

Primer procesa boxing na celoštevilski spremenljivki: Sklad Kopica Celoštevilska spremenljivka i

123 int i = 123; objekt o; i (prestavljen na kopico)

boxing int

123 123 object o = i; Metoda za izpis podatkov Console.WriteLine() ali pa metoda MessageBox.Show() pričakujeta za parameter nek objekt in ne celo število, ali kaj podobnega. Da pa lahko s pomočjo teh dveh metod izpisujemo oz prikazujemo tudi vrednosti spremenljivk vrednostnih tipov, se pred izpisom avtomatično izvede proces boxing, nad tako nastalim objektom pa se še avtomatično izvede metoda toString(). Na ta način lahko tudi sami pišemo metode, ki sprejmejo za parameter nek objekt po vrednosti ali pa po referenci in metoda bo vseeno delala. Pri pretvarjanju nekega objekta nazaj v vrednostni tip (unboxing), pa moramo to storiti explicitno. Upoštevati moramo dva koraka:

• Prepričani moramo biti, da je instanca objekta, ki ga želimo spremeniti nazaj v spremenljivko vrednostnega tipa, res nastala s procesom boxing

• Vrednost instance objekta moramo kopirati v neko spremenljivko vrednostnega tipa

Primer procesa unboxing na celoštevilski spremenljivki: Sklad Kopica Celoštevilska spremenljivka i

123 int i = 123; objekt o; i (prestavljen na kopico)

boxing int

123 123 object o = i; Celoštevilska spremenljivka j

123 int j = (int)o; //proces unboxing

Page 90: PROŠNJA - uranic.tsckr.siuranic.tsckr.si/VISUAL C#/VISUAL C#.pdf · MICROSOFT VISUAL C# .NET Srečo Uranič . Visual C# . ... Za začetek novega projekta v okolju Visual C# Standard

Visual C# . NET

Algoritmi in programski jeziki – 4.letnik

90

Operacijo unboxing običajno vključimo v varovalni blok, saj na ta način lahko preverimo ali je operacija boxing uspela – če je namreč vrednost spremenljivke, ki nastane po operaciji unboxing enaka null, operacija ni uspela (pride do izjeme tipa InvalidCastException).

Polimorfizem - mnogoličnost V izpeljanih razredih se srečamo še z enim temeljnim pojmom objektno orientiranega programiranja – to je pojem polimorfizem oz. mnogoličnost. Pojem polimorfizem označuje princip, da lahko različni objekti razumejo isto sporočilo in se nanj odzovejo vsak na svoj način. Pomeni tudi, da je ista operacija lahko implementirana na več različnih načinov oz. zanjo obstaja več metod. Dedovanje in polimorfizem sta lastnosti, ki predstavljata osnovo objektnega programiranja in omogočata hitrejši razvoj in lažje vzdrževanje programske kode. Kot primer za prikaz polimorfizma deklarirajmo razred OsnovniRazred, ki ima eno samo metodo z imenom Slika. Ker želimo, da bo vsak objekt, ki bo izpeljan iz tega razreda (tudi tisti v podedovanih – izpeljanih razredih) ohranil sebi lastno obnašanje ob klicu metode Slika, moramo uporabiti polimorfno redefinicijo. Polimorfno redefinicijo omogočimo z že znano definicijo virtualne metode. V telesu te metode bomo za vajo in zaradi enostavnosti prikazali le neko sporočilno okno! public class OsnovniRazred //temeljni razred {

public virtual void Slika() //polimorfna redefinicija – omogočimo jo z virtualno metodo { MessageBox.Show("Osnovni objekt!"); } }

Ker smo metodo Slika definirali kot virtualno, smo s tem napovedali, da bodo izpeljani objekti lahko uporabljali svojo (prekrivno oz. override) metodo Slika, ki pa bo imela enako ime. Razred OsnovniRazred bo naš temeljni razred, iz katerega bomo tvorili izpeljane razrede in v njih tvorili nove objekte. Ker je metoda Slika virtualna to pomeni, da lahko v izpeljanih razredih to metodo prekrijemo (override) z metodo, ki bo imela enako ime a drugačen pomen (drugačno vsebino). Napišimo sedaj še tri razrede, ki naj bodo izpeljani iz razreda OsnovniRazred in ki imajo svojo metodo Slika. Pred tipom takih metod mora stati besedice override, ki označuje, da se bodo objekti izpeljani iz teh razredov na to metodo odzivali vsak na svoj način. Osnovni pogoj pa je, da imajo take override metode enako raven zaščite (npr. vse so public) , enako ime in enake parametre kot jih ima osnovna virtualna metoda v bazičnem razredu. public class Crta : OsnovniRazred //Crta je razred, ki podeduje razred OsnovniRazred { public override void Slika() //preobložena metoda razreda Crta { MessageBox.Show("Črta."); //Telo override metode je seveda lahko drugačno!!! } } public class Krog : OsnovniRazred //Tudi Krog je razred, ki podeduje razred OsnovniRazred { public override void Slika() { MessageBox.Show("Krog."); //Telo override metode je seveda lahko drugačno!!! } } public class Kvadrat: OsnovniRazred //Tudi Kvadrat je razred, ki podeduje razred OsnovniRazred { public override void Slika() //Telo override metode je seveda lahko drugačno!!! { MessageBox.Show("Kvadrat."); } }

Page 91: PROŠNJA - uranic.tsckr.siuranic.tsckr.si/VISUAL C#/VISUAL C#.pdf · MICROSOFT VISUAL C# .NET Srečo Uranič . Visual C# . ... Za začetek novega projekta v okolju Visual C# Standard

Visual C# . NET

Algoritmi in programski jeziki – 4.letnik

91

Poglejmo sedaj, kako bi te štiri razrede sedaj uporabili in na primeru izpeljanih objektov prikazali princip polimorfizma. V ta namen kreirajmo tabelo objektov. Ime tabele je dObj, tabela pa naj bo inicializirana tako, da so v njej lahko štiri objekti tipa OsnovniRazred. OsnovniRazred[] dObj = new OsnovniRazred[4];//tabela objektov

Ker so razredi Crta, Krog in Kvadrat izpeljani iz bazičnega razreda OsnovniRazred, jih lahko priredimo isti tabeli dObj. Če te zmožnosti ne bi bilo, bi morali za vsak nov objekt, izpeljan iz kateregakoli od teh štirih razredov, kreirati svojo tabelo. Dedovanje pa nam omogoča, da se vsak od izpeljanih objektov obnaša tako kot njegov bazični razred. Naslednjo kodo lahko zapišemo npr. v dogodek Click nekega gumba, ali pa neko opcijo menija. dObj[0] = new Crta(); //konstruktor objekta dObj[0] dObj[1] = new Krog(); //konstruktor objekta dObj[1] dObj[2] = new Kvadrat(); //konstruktor objekta dObj[2] dObj[3] = new OsnovniRazred();//konstruktor objekta dObj[3] foreach (OsnovniRazred objektZaRisanje in dObj) { objektZaRisanje.Slika(); //klic metode Slika ustreznega objekta }

Ko je tabela inicializirana, lahko npr. s foreach zanko pregledamo vsakega od objektov v tabeli. Zaradi načela polimorfizma se v zagnanem programu vsak objekt obnaša po svoje, pač odvisno od tega, iz katerega razreda je bil izpeljan. Ker smo v izpeljanih razredih prepisali virtualno metodo Slika , se ta metoda v izpeljanih objektih izvaja različno, pač glede na njeno definicijo v izpeljanih razredih. Pri vsakem prehodu zanke bomo tako dobili drugačno sporočilno okno.

Uporaba označevalca protected

Uporaba označevalcev private in public predstavlja obe skrajni možnosti dostopa do članov razreda (oz. objekta). Javna polja in metode so dostopne vsem, zasebna polja in metode pa so dostopne le znotraj razreda. Pogosto pa je koristno, da bazični razred dovoljuje izpeljanim razredom, da le-ti dostopajo do nekaterih bazičnih članov, obenem pa ne dovoljujejo dostopa razredom, ki niso del hierarhije. V takem primeru uporabimo za dostop označevalec protected. Nadrejeni razred tako lahko dostopa do člana bazičnega razreda, ki je označen kot protected, kar praktično pomeni, da bazični član, ki je označen kot protected, v izpeljanem razredu postane javen (public). Javen je tudi v vseh višjih razredih. V primeru, ko pa nek razred ni izpeljani razred, pa nima dostopa do članov razreda, ki so označeni kot protected – znotraj razreda, ki ni izpeljani razred, je torej član razreda, ki je označen kot protected enak zasebenemu članu (private).

Vmesnik – Interface

Sintaksa in pomen vmesnikov

Čeprav je dedovanje za razredi zelo močan mehanizem, pa se prava moč in sposobnost dedovanja pokaže pri dedovanju za vmesniki (interface). Vmesnik nam omogoča, da ime metode povsem ločimo od njene implementacije. Vmesniki nam resnično omogočajo, da ločimo »kaj« od »kako« in to na način, da nam povedo samo kakšno je ime, tip vrnjenega rezultata in kakšni so parametri metod, natančna implementacija metod pa ni bistvo vmesnika. Vmesnik (interface) je podatkovna struktura podobna razredom, le da pri deklaraciji vmesnika uporabimo rezervirano besedo interface. Prav tako kor razred ali struktura vsebuje deklaracije oz. napovedi (signature) metod, lastnosti (property-jev) ali dogodkov, za razliko od razredov ali struktur pa v vmesniku NIKOLI ne določamo načina dostopa (pred imeni metod ne napišemo public, private ali protected). Telo metode v

Page 92: PROŠNJA - uranic.tsckr.siuranic.tsckr.si/VISUAL C#/VISUAL C#.pdf · MICROSOFT VISUAL C# .NET Srečo Uranič . Visual C# . ... Za začetek novega projekta v okolju Visual C# Standard

Visual C# . NET

Algoritmi in programski jeziki – 4.letnik

92

vmesniku nadomestimo z dvopičjem. Nezaželeno je, da v vmesnikih pišemo kakršnakoli polja (tudi statična ne!), prav tako v vmesnikih ne pišemo konstruktorjev in destruktorjev. V vmesnikih prav tako ne moremo gnezditi drugih tipov (npr. naštevnih tipov, razredov, drugih vmesnikov ali pa struktur). Vmesnika tudi ne moremo podedovati iz nekega razreda ali strukture, lahko pa vmesnik podeduje enega ali več osnovnih vmesnikov. Vmesnik tako predstavlja neko abstraktno definicijo in ga v programih ne uporabljamo neposredno, ampak kot sestavni del imenskih prostorov oz. razredov.

Implementacija vmesnikov

Vmesnik implementiramo (uporabimo) tako, da deklariramo nov razred ali strukturo, ki podeduje ta vmesnik, nato pa implementiramo vse metode, ki so napovedane v vmesniku. Pri tem moramo seveda upoštevati, da se morajo implementirane metode po imenu, tipu in parametrih natanko ujemati z metodami vmesnika. Da se izognemo nevšečnostim, moramo v razredu, ki deduje vmesnik, pred imeni metod, ki jih implementiramo iz vmesnika, napisati še ime vmesnika. Pred tako metodo pa ni potrebno pisati označevalca za dostop, saj so vse metode, ki so implementirane iz nekega vmesnika, privzeto javne - public. Razred, v katerem je implementiran nek vmesnik lahko eksplicitno implementira/podeduje posamezne člane vmesnika. Eksplicitno implementiran/podedovan član ni dostopen preko instance tega razreda, ampak le preko instance vmesnika. Primer: interface ISimbol //vmesnik { string ime(); } class Simbol : ISimbol //razred Simbol podeduje vmesnik ISimbol { private string naziv;

string ISimbol.ime()// implementacija metode ime() deklarirane v vmesniku ISimbol { return "Polde"; } } private void Form1_Load(object sender, EventArgs e) { Simbol novi=new Simbol(); //nov objekt izpeljan iz razreda Simbol ISimbol Inovi = (ISimbol)novi; //Eksplicitna konverzija: podedovana metoda bo dostopna //preko vmesnika this.Text = Inovi.ime();//metodo ime() kličemo preko vmesnika }

Delo z več vmesniki

Razred ima lahko en sam bazični razred (lahko deduje iz enega samega bazičnega razreda), lahko pa implementira neomejeno število vmesnikov, pri čemer pa moramo v takem razredu implementirati vse metode bazičnih vmesnikov. V primeru, da nek razred deduje iz več kot enega vmesnika, je sintaksa naslednja: class Razred : vmesnik, vmesnik2

Prav tako lahko vmesnik deduje od drugega vmesnika. Vaja: // Napišimo dva vmesnika in ju implementirajmo v razredu. // Deklaracija prvega vmesnika/Interface-a interface angDimenzije { float Dolzina(); float Visina(); }

Page 93: PROŠNJA - uranic.tsckr.siuranic.tsckr.si/VISUAL C#/VISUAL C#.pdf · MICROSOFT VISUAL C# .NET Srečo Uranič . Visual C# . ... Za začetek novega projekta v okolju Visual C# Standard

Visual C# . NET

Algoritmi in programski jeziki – 4.letnik

93

// Deklaracija drugega vmesnika/Interface-a interface eudDimenzije { float Dolzina(); float Visina(); } // Deklaracija razreda Box ki implementira (podeduje) oba vmesnika/interface-a class Box : angDimenzije, eudDimenzije { float dolzinaIncev; float visinaIncev; public Box(float dolzina, float visina) { dolzinaIncev = dolzina; visinaIncev = visina; } // Eksplicitan implementacija člana vmesnika angDimenzije: float angDimenzije.Dolzina() { return dolzinaIncev; } float angDimenzije.Visina() { return visinaIncev; } // Eksplicitan implementacija člana vmesnika euDimenzije: float eudDimenzije.Dolzina() { return dolzinaIncev * 2.54f; //pretvorba inči->cm } float eudDimenzije.Visina() { return visinaIncev * 2.54f; //pretvorba inči->cm } } //UPORABA (npr. ob kliku na nek gumb, opcijo menija, … // Deklaracija instance box1 razreda box: Box box1 = new Box(30.0f, 20.0f); //Deklaracija instance vmesnika angDimenzije angDimenzije aDimenzija = (angDimenzije)box1; //Eksplicitna konverzija //Deklaracija instance vmesnika euDimenzije eudDimenzije eDimenzija = (eudDimenzije)box1;//Eksplicitna konverzija // Izpis dimenzij v inčih: Console.WriteLine("Dolžina(inči): {0}", aDimenzija.Dolzina()); Console.WriteLine("Višina (inči): {0}", aDimenzija.Visina()); // Izpis dimenzij v cm: Console.WriteLine("Dolžina(cm): {0}", eDimenzija.Dolzina()); Console.WriteLine("Višina (cm): {0}", eDimenzija.Visina());

Omejitve pri pisanju vmesnika:

V vmesnikih nikoli na pišemo polj, niti statičnih. Ker vmesniki ne vsebujejo polj, v vmesnikih nikoli ne pišemo konstruktorjev; konstruktorji namreč

vsebujejo stavke za inicializacijo polj, ki pa jih v vmesniku ni. Prav tako v vmesnikih nikoli ne pišemo destruktorjev, saj destruktorji vsebujejo stavke, ki poskrbijo za

uničenje objekta. V vmesnikih nikoli ne pišemo elementov zaščite (stavkov private, public ali protected). Gnezdenje tipov prav tako ni dovoljeno (naštevanje – enum, strukure – struct, razredi, vmesniki oz.

delegati znotraj vmesnika NISO dovoljeni). Vmesnika ne moremo podedovati iz nekega razreda ali strukture.

Razred lahko hkrati podeduje drugi razred in še poljuben vmesnik. V takem primeru izpeljani razred ne razlikuje med bazičnim razredom in vmesnikom (tako kot je npr. v Javi). Mora pa biti najprej najavljen bazični razred, nato šele vmesnik, npr.: interface Vmesnik {

Page 94: PROŠNJA - uranic.tsckr.siuranic.tsckr.si/VISUAL C#/VISUAL C#.pdf · MICROSOFT VISUAL C# .NET Srečo Uranič . Visual C# . ... Za začetek novega projekta v okolju Visual C# Standard

Visual C# . NET

Algoritmi in programski jeziki – 4.letnik

94

... } class Bazicni { ... } class Izpeljani_Razred : Bazicni, Vmesnik //Najprej je naveden bazični razred, nato pa vmesnik { ... } //tvorba objektov: Izpeljani_Razred Novi = new Izpeljani_Razred(); //Objektu, izpeljanemu iz našega vmesnika lahko priredimo referenco na objekt, ki je izpeljan //iz reazreda, po hierarhiji višje od vmesnika Vmesnik V1 = Novi;

Abstraktni razredi in abstraktne metode Abstraktni razredi so razredi, iz katerih ne moremo tvoriti objektov, ampak jih lahko le podedujemo, oziroma tvorimo izpeljane razrede. Namen takih razredov je, da poskrbijo za temeljno definicijo bazičnega razreda, njegova uporaba pa bo implementirana v številnih izpeljanih razredih. Abstraktni razred naredimo s pomočjo rezervirane besede abstract. abstract class abstraktniRazred { ... }

V abstraktnih razredih se pogosto pojavijo tudi abstraktne metode. Pred tipom take metode zapišemo besedico abstract. Abstraktne metode imajo samo glavo, ki ji sledi podpičje, nimajo pa implementacije (nimajo telesa metode). Razredi, ki jih izpeljemo iz abstraktnih razredov morajo zato implementirati vse abstraktne metode. Abstraktni razred je lahko izpeljan iz nekega bazičnega razreda. Kadar abstraktni razred podeduje virtualno metodo iz nekega bazičnega razreda, lahko abstraktni razred prekrije (povozi) virtualno metodo z abstraktno metodo, npr: // bazični razred public class D { public virtual void Nekaj(int i) { // Originalna implementacija. } } public abstract class E : D //abstraktni razred podeduje bazični razred { public abstract override void Nekaj(int i); //abstraktna metoda } public class F : E //razred F izpeljemo iz abstraktnega razreda { public override void Nekaj(int i)//prekrivanje bazične Nekaj bazičnega razreda D { // Nova implementacija } }

Če je neka virtualna metoda deklarirana kot abstraktna, je še vedno virtualna v vseh razredih, ki dedujejo abstraktni razred. Razred, ki podeduje abstraktni razred torej nima dostopa do originalne implementacije take metode – v zgornjem primeru torej metoda �ekaj razreda F &E more klicati metode �ekaj razreda D.

Primer:

Page 95: PROŠNJA - uranic.tsckr.siuranic.tsckr.si/VISUAL C#/VISUAL C#.pdf · MICROSOFT VISUAL C# .NET Srečo Uranič . Visual C# . ... Za začetek novega projekta v okolju Visual C# Standard

Visual C# . NET

Algoritmi in programski jeziki – 4.letnik

95

Kot primer abstraktnega razreda napišimo razred Racun, ki predstavlja abstraktno osnovo transakcijskega računa vseh komitentov neke banke. Razred nato implementirajmo v projekt, v katerem bo zaradi enostavnosti en sam obrazec za vnos komitentovega imena in potrebnih podatkov za izračun obresti za vezavo oz kredit! public abstract class Racun//Abstraktni razred: iz njega ne moremo neposredno tvoriti objektov { protected string ime; //polje dostopno samo v bazičnem razredu protected double stanje; //polje dostopno v bazičnem in izpeljanem razredu public Racun(string nm, double st) //bazični konstruktor { this.ime = nm; this.stanje = st; } //metoda za izračun novega stanja na računu, glede na polog oz. dvig public void Transakcija(double znesek) { this.stanje += znesek; } public void spremeniIme(string novoIme) //javna metoda za spreminjanje imena komitenta { this.ime = novoIme; } //abstraktna metoda: implementirana bo v izpeljanem razredu public abstract string Obresti(int steviloDni); }

Izgled in koda pripadajočega obrazca: Ob kliku na gumb Izračun se prikaže sporočilno okno //Razred UporabnikovRacun podeduje abstraktni razred Racun public class UporabnikovRacun : Racun { public UporabnikovRacun(string nm, double st) //podedujemo bazični konstruktor : base(nm, st) { } public override string Obresti(int steviloDni)//prekrivna metoda za izračun obresti { //POZOR! Zaradi enostavnosti je uporabljen navaden obrestni račun!!! double obresti; if (stanje >= 0) //Obresti za vezano vlogo { if (steviloDni <= 90) obresti = stanje * 0.03 * steviloDni / 365; //pozitivne obresti so 3% else if (steviloDni > 90 && steviloDni <= 365) obresti = stanje * 0.05 * steviloDni / 365; //pozitivne obresti so 5% else obresti = stanje * 0.07 * steviloDni / 365; //pozitivne obresti so 7% } Else //če znesek negativen gre za kredit!!!

Page 96: PROŠNJA - uranic.tsckr.siuranic.tsckr.si/VISUAL C#/VISUAL C#.pdf · MICROSOFT VISUAL C# .NET Srečo Uranič . Visual C# . ... Za začetek novega projekta v okolju Visual C# Standard

Visual C# . NET

Algoritmi in programski jeziki – 4.letnik

96

obresti = stanje * 0.1 * steviloDni / 365;//negativne obresti so 10% obresti = Math.Round(obresti, 2);//obresti zaokrožimo na dve decimalki string izpis="KOMITENT: " + ime+ "\nInformativni izračun obresti:\nvezava 1-3 mesece: 3%\nvezava 3-12 mesecev: 5%\nvezava več kot 1 leto: 7%\n\nObresti za kredit: 10%\n"+ "\nStanje: " + stanje+" EUR"+ "\nObresti: " + obresti +" EUR"+ " (Število dni: " + steviloDni + ")"+ "\nSkupno stanje: " + (stanje + obresti)+" EUR"; return izpis; } } //Odzivna metoda gumba button1 private void button1_Click(object sender, EventArgs e) { try { double znesek = Convert.ToDouble(textBox2.Text); int dni = Convert.ToInt32(maskedTextBox1.Text); //Kreiranje novega objekta – konstruktorju posredujemo ime in znesek vezave/kredita UporabnikovRacun rc1 = new UporabnikovRacun(textBox1.Text, znesek); MessageBox.Show(rc1.Obresti(dni)); } catch { MessageBox.Show("Napaka pri vnosu podatkov!"); } }

Zapečateni razredi (Sealed classes) Dedovanje za razredi ni vedno enostavno in zahteva tehten razmislek. Kadar kreiramo vmesnik ali pa abstraktni razred, zavestno delamo nekaj, kar bo nekdo nekoč lahko podedoval. Pisanje bazičnih razredov zahteva veliko napora in previdnosti, pa se klub temu lahko zgodi, da se v izpeljanih razredih bazične metode in polja ne obnašajo tako, kot smo si zamislili. Kadar ne želimo, da bi naš razred lahko kdorkoli podedoval, ga zapečatimo – pred rezervirano besedo class zapišemo besedico sealed. sealed class Zapecateni_Razred { ... }

Ker zapečateni razred zapišemo z namenom, da ga nihče ne more podedovati, v njem ne moremo pisati virtualnih metod. Edini namen besedice virtual je namreč v tem, da gre pač za prvo implementacijo metode, ki pa jo nameravamo v izpeljanih razredih prekriti (override) z novo metodo. Ker so strukture že po naravi zapečatene, jih ne moremo podedovati. Zapečatimo lahko tudi metode. Če pred imenom metode zapišemo besedico sealed, to pomeni, da take metode v izpeljanih razredih ne moremo prekriti. Lahko pa zapečatimo prekrivno (override) metodo (metodo deklariramo kor sealed override). Na rezervirane besede interface, virtual, override in selaed lahko torej gledamo takole:

• Vmesnik (interface) predstavi oz. uvede imena metod. • Virtualna (virtual) metoda je v resnici prva implementacija (uporaba) neke metode. • Prekrivna (override) metoda je druga (oz. naslednja) implementacija metode. • Zapečatena (selaed) metoda je zadnja implementacija metode.

Gnezdeni razredi Razrede lahko tudi gnezdimo. Vsebovani, notranji razred, se imenuje gnezdeni razred, razred ki le-tega vsebuje a je zunanji razred. Prednost gnezdenih razredov je v tem, da lahko dostopajo do vseh članov zunanjega razreda.

Page 97: PROŠNJA - uranic.tsckr.siuranic.tsckr.si/VISUAL C#/VISUAL C#.pdf · MICROSOFT VISUAL C# .NET Srečo Uranič . Visual C# . ... Za začetek novega projekta v okolju Visual C# Standard

Visual C# . NET

Algoritmi in programski jeziki – 4.letnik

97

Tako lahko npr. metoda gnezdenega razreda dostopa tudi do zasebnih članov zunanjega razreda. Gnezdeni razred je lahko skrit vsem ostalim razredom, kar pomeni, da je zaseben (private) za vse druge razrede. Do članov gnezdenega razreda dostopamo z dodatnim operatorjem pika. Primer: public class Ulomek //zunanji razred { public Ulomek(int stevec, int imenovalec) { this.stevec = stevec; this.imenovalec = imenovalec; } public override string ToString( ) //prekrivna metoda ToString() { StringBuilder s = new StringBuilder();//razred StringBuilder predstavlja niz znakov //z metodo AppendFormat dodamo stringu s formatiran string, ki lahko vsebuje navodila //za formatiranje(podobno kot pri izpisu v stavku WriteLine) s.AppendFormat("{0}/{1}", stevec, imenovalec); return s.ToString( ); //vrne s spremenjen v string } internal class UlomekIzpis //gnezdeni razred { public void Izpis(Ulomek f) { MessageBox.Show("Števec: "+f.stevec+"\nImenovalec: "+f.imenovalec); } } private int stevec; private int imenovalec; }

//primer kreiranja nove instance razreda Ulomek in uporaba metode ToString Ulomek f1 = new Ulomek(3, 4); //nov objekt izpeljan iz razreda Ulomek MessageBox.Show("f1: " +f1.ToString());//klic metode toString razreda Ulomek //primer kreiranja nove instance razreda UlomekIzpis in uporaba metode Izpis() Ulomek.UlomekIzpis fa = new Ulomek.UlomekIzpis();//nova instanca gnezdenega razreda //UlomekIzpis znotraj razreda Ulomek fa.Izpis(f1);

Operatorja is in as Pri programiranju se večkrat pojavi potreba po ugotavljanju, ali določen objekt pripada določenemu razredu, oz. ali je izpeljan iz nekega razreda (ali pa tudi vmesnika, …). Problem lahko rešimo s pomočjo operatorjev is in as. Sintaksa obeh stavkov je naslednja: izraz is tip //izraz pomeni poljuben izraz ali pa npr. nek objekt, tip pa npr. nek razred izraz as tip //izraz pomeni poljuben izraz ali pa npr. nek objekt, tip pa npr. nek razred

S pomočjo operatorja is ocenimo (rezultat je true), če lahko izraz varno pretvorimo (cast) v določen tip, ne da bi pri tem prišlo do izjeme (exception). Za primer uporabimo razred Ulomek iz prejšnjega primera. Za nek objekt U1 lahko torej preverimo, ali je izpeljan iz razreda Ulomek takole: if (!(U1 is Ulomek)) //preverimo, če je U1 izpeljan iz razreda Ulomek . . .

Operator as je kombinacija operatorja is in še pretvarjanja v drug tip (cast) in to tako, da najprej preveri če je pretvorba (cast) sploh veljavna (če bi torej operator is vrnil true), nato pa še izvede pretvorbo, če le ta obstaja oz. če je sploh možna. Če pretvorba ni možna, operator as vrne null. Rezervirana beseda null predstavlja ničelno

Page 98: PROŠNJA - uranic.tsckr.siuranic.tsckr.si/VISUAL C#/VISUAL C#.pdf · MICROSOFT VISUAL C# .NET Srečo Uranič . Visual C# . ... Za začetek novega projekta v okolju Visual C# Standard

Visual C# . NET

Algoritmi in programski jeziki – 4.letnik

98

referenco, to je referenco, ki ne kaže na noben objekt. Z uporabo operatorja as nam torej ni potrebno obdelovati izjem, ki lahko nastanejo pri pretvarjanju (cast) objektov oz. spremenljivk. Ulomek U = U1 as Ulomek;//U1 skušamo pretvoriti v izpeljanko razreda Ulomek if (U != null) //če pretvorba NI uspela, dobi objekt U referenco null ....

Prekrivanje operatorjev - uporaba rezervirane besede operator V C# so operatorji statične metode, ki vračajo rezultat neke operacije in katerih parametri so ustrezni operandi. Podobno kot lahko prekrivamo metode, pa lahko v C# prekrivamo tudi operatorje in jim s tem damo nove, bolj zanimive funkcije. Ko kreiramo nov operator za nek razred pravimo, da smo prekrili (overload) nek drug operator. Smisel prekrivanja operatorjev naj ne bi bil samo v tem, da jih lahko uporabimo nad poljubnimi objekti, ampak predvsem v tem, da predstavljajo sintaktično bližnjico za določeno operacijo. Zapisana koda bo tako bolj pregledna, prekriti operator pa se bo obnašal podobno kot že vgrajeni operatorji. Seveda pa pri prekrivanju operatorjev ne smemo pretiravati, saj lahko naši programi tako postanejo preveč kompleksni in težko berljivi. Smiselno je npr. prekriti operator primerjanja (= =), a katerim lahko za poljubna objekta ugotovimo, ali sta enaka ali ne. Seveda pa je v tem primeru potrebno prekriti tudi operator neenakosti (!=), saj sicer tak opretor ni smiselen. Podobno je s prekrivanjem operatorjev večji (>) oz. manjši (<), manjši ali enako (<=), ter večje ali enako (>=). Za primer pokažimo, kako lahko npr. prekrijemo operator za seštevanje (operator +) tako, da lahko z njim seštevamo ulomke, ter še prikaz prekrivanja operatorja za primerjanje (operator = =) in operatorja različno (!=).

public class Ulomek { public Ulomek(int stevec, int imenovalec)//konstruktor { this.stevec = stevec; this.imenovalec = imenovalec; } public override string ToString( ) { string s = stevec + "/" + imenovalec; //med stevec in imenovalec postavimo ulomkovo //črto in vse skupaj spravimo v string return s; } //prekrivanje operatorja + za seštevanje ulomkov public static Ulomek operator +(Ulomek prvi, Ulomek drugi) { int noviStevec=prvi.stevec * drugi.imenovalec + prvi.imenovalec * drugi.stevec; int noviImenovalec = prvi.imenovalec * drugi.imenovalec; return new Ulomek( noviStevec,noviImenovalec); //vrnemo vsoto obeh ulomkov } public static bool operator==(Ulomek prvi, Ulomek drugi)//prekrijemo operator == za //primerjanje ulomkov { if (prvi.imenovalec == drugi.imenovalec && prvi.stevec == drugi.stevec) { return true; } return false; } public static bool operator !=(Ulomek prvi, Ulomek drugi)//hkrati MORAMO prekriti še //operator != za ugotavljanje ali sta dva ulomka različna { if (prvi.imenovalec == drugi.imenovalec && prvi.stevec == drugi.stevec) { return false; } return true; }

//prekrijmo še metodo Equals razreda object tako, da bo primerna tudi za primerjavo //ulomkov. Če je parameter metode ulomek, metoda preveri enakost dveh ulomkov s pomočjo //prekrivnega operatorja ==(eksplicitna konverzija)!

Page 99: PROŠNJA - uranic.tsckr.siuranic.tsckr.si/VISUAL C#/VISUAL C#.pdf · MICROSOFT VISUAL C# .NET Srečo Uranič . Visual C# . ... Za začetek novega projekta v okolju Visual C# Standard

Visual C# . NET

Algoritmi in programski jeziki – 4.letnik

99

public override bool Equals(object o) { if (!(o is Ulomek)) { return true; } return this == (Ulomek) o;

} private int stevec; //zasebno polje razreda Ulomek private int imenovalec; //zasebno polje razreda Ulomek }

Operator + sedaj lahko uporabimo za seštevanje objektov tipa ulomek, operator == pa za primerjanje dveh ulomkov.: Ulomek A=new Ulomek(3,4); Ulomek B=new Ulomek(1,4); Ulomek vsota = A + B; //operator + smo v razredu Ulomek prekrili, tako da ga sedaj lahko //uporabimo za seštevanje ulomkov MessageBox.Show(vsota.ToString());//klic metode ToString() za izpis ulomka

//Prikaz prekrite uporabe metode Equals, s katero lahko primerjamo tudi //ulomke if (C.Equals(vsota))

MessageBox.Show(vsota.ToString());//kliče se prekrivna metoda ToString razreda //Ulomek int st = 10; if (C.Equals(st))

MessageBox.Show(st.ToString());//kliče se klasična metoda ToString()

Delegati Za večino kode, ki smo jo napisali v različnih primerih in vajah je značilno, da se stavki izvajajo sekvenčno, drug za drugim. Včasih pa je trenutni tok izvajanja programa potrebno prekiniti in izvesti neko drugo, bolj pomembno nalogo. Ko se le-ta konča, se lahko program nadaljuje na mestu, kjer je bil prekinjen. Klasični primer takšnega stila programa je obrazec v okolju Windows (Windows Form). Obrazec prikazuje različne gradnike (kontrole), kot so npr. gumbi (Button) in tekstovna polja (TextBox). Ko uporabnik klikne gumb, ali pa vnaša vsebino nekega TextBox-a, se mora obrazec na klik oz. vnos teksta odzvati v trenutku. Trenutna aplikacija se mora torej začasno prekiniti in obdelati uporabnikov klik oz. vnos. Tak način dela ni vezan le na grafične vmesnike, ampak na kakršno koli aplikacijo, pri kateri mora biti neka operacija izvedena urgentno oz. prednostno (npr. izklop reaktorja v atomski centrali, če postane temperatura v njem previsoka). Da pa bi bilo vse to izvedljivo v času izvajanja nekega programa oz. aplikacije, je potrebno zagotoviti dvoje: sredstva za zaznavanje (indikacijo), da se je v toku programa zgodilo nekaj zelo pomembnega in način za prikaz metode, ki se mora v takem trenutku izvesti. To pa je naloga in namen dogodkov in delegatov.

Uporaba delegatov

Delegat je kazalec, referenca na neko metodo, izgleda in obnaša pa se zelo podobno kot običajna metoda, kadar le-to pokličemo. Razlika pa je v tem, da kadar v času izvajanja programa pokličemo delegata, se dejansko izvede metoda, na katero se delegat nanaša. Bistvo pa je seveda v tem, da lahko metodo, na katero se delegat nanaša, dinamično spreminjamo – teoretično lahko torej koda, ki kliče nekega delegata, vsakič ko se izvede pokliče neko drugo metodo. Lahko tudi rečemo, da delegati omogočajo metodam, da jih lahko posredujemo kot parametre. Delegati so torej nekakšne šablone, preko katerih lahko kličemo metode enakega tipa in z enakimi parametri, kot jih ima delegat.

� POZOR: Pomen delegatov je podoben kot kazalec na funkcije v C++. Za razliko od kazalcev na funkcije pa so delegati veliko bolj varni, saj lahko naredimo delegata le za metodo, ki se

Page 100: PROŠNJA - uranic.tsckr.siuranic.tsckr.si/VISUAL C#/VISUAL C#.pdf · MICROSOFT VISUAL C# .NET Srečo Uranič . Visual C# . ... Za začetek novega projekta v okolju Visual C# Standard

Visual C# . NET

Algoritmi in programski jeziki – 4.letnik

100

tako po tipu kot po parametrih ujema z delegatom in torej ne moremo klicati delegata, ki se po signaturi z veljavno metodo ne ujema.

Deklaracija delegata

Delegata deklariramo s pomočjo rezervirane besede delegate. Vedeti moramo še, da delegat definira obliko metode na katere se lahko nanaša – zato mu določimo še tip rezultata, ime in parametre (tip in število parametrov), npr.: public delegate int Kalkulacija (int x, int y); //deklaracija oz. napoved delegata

Vsako metodo, ki ustreza temu delegatu (ima dva celoštevilska parametra in vrne celo število) lahko priredimo temu delegatu. Na ta način dosežemo, da lahko programsko spremenimo klic metode obenem pa lahko vstavljamo novo kodo v obstoječe razrede. Prvi, najpogostejši način za deklaracijo in implementacijo instanc delegatnih metod, je s pomočjo operatorja new. Najprej deklariramo/napovemo novo instanco (novo delegatno metodo), nato pa ji s pomočjo operatorja new posredujemo referenco na pravo metodo: Kalkulacija vsota; //deklaracija instance delegata Kalkulacija: ime instance je vsota Referenco na pravo metodo sedaj naredimo npr. znotraj neke odzivne metode: vsota = new Kalkulacija(sestej); //delegatni objekt vsota dobi referenco na metodo sestej //delegatna metoda vsota se sedaj obnaša povsem enako kot metoda sestej MessageBox.Show(Convert.ToString(vsota(3, 4))); //še klic delegatne metode vsota

//še primer metode sestej, katere referenco smo posredovali delegatni metode vsota public int sestej(int a, int b) { return a+b; }

V zgornjem primeru, se bo torej pri klicu delegatne metode vsota dejansko izvedla metoda sestej (delegatni metodi vsota smo namreč posredovali referenco na metodo sestej). Vaja: Kreirajmo projekt, v katerem bomo prikazali osnovno deklaracijo in uporabo delegatov. Na obrazcu naj bodo le štirje gumbi:

Deklarirajmo delegata z imenom MojDelegat in še dve metodi, ki po tipu in parametrih ustrezata temu delegatu. Nato napovejmo še štiri delegatne metode delegate void MojDelegat(string s); //deklaracija oz. napoved delegata public static void Pozdrav(string s) //prva metoda, katere referenco bomo prenesli delegatu { MessageBox.Show("Živjo " + s+"!"); }

Page 101: PROŠNJA - uranic.tsckr.siuranic.tsckr.si/VISUAL C#/VISUAL C#.pdf · MICROSOFT VISUAL C# .NET Srečo Uranič . Visual C# . ... Za začetek novega projekta v okolju Visual C# Standard

Visual C# . NET

Algoritmi in programski jeziki – 4.letnik

101

public static void Adijo(string s) //druga metoda, katere referenco bomo prenesli delegatu { MessageBox.Show("Nasvidenje "+ s+"!"); }

MojDelegat Prvi, Drugi, Tretji, Cetrti; //Napoved delegatnih objektov - metod

V dogodku Load tega obrazca kreirajmo štiri delegatne metode - objekte: //Kreiranje delegatnega objekta Prvi, ki mu posredujemo referenco na metodo Pozdrav: Prvi = new MojDelegat(Pozdrav); //Kreiranje delegatnega objekta Drugi, ki mu posredujemo referenco na metodo Adijo: Drugi = new MojDelegat(Adijo); //Delegata Prvi in Drugi lahko sestavimo v delegatni objekt, ki kliče obe metodi zapored: Tretji = Prvi + Drugi; //Odstranimo delegatni objekt Prvi iz sestavljenega delegatnega objekta Tretji, tako da //delegatni objekt Cetrti kliče le metodo Adijo: Cetrti = Tretji - Drugi;

Delegatne metode sedaj lahko kličemo ob klikih na gumbe od 1 do 4, npr. takole: private void button1_Click(object sender, EventArgs e) { Prvi("Anja"); //delegatna metoda Prvi dobi za parameter string "Anja" } private void button2_Click(object sender, EventArgs e) { Drugi("Petra"); } private void button3_Click(object sender, EventArgs e) { Tretji("Jerneja"); } private void button4_Click(object sender, EventArgs e) { Cetrti("Jana"); }

Ker je delegatna metoda Tretji sestavljena iz dveh metod (Tretji = Prvi + Drugi ) se ob kliku na gumb številka 3 prikažeta dve sporočilni okni (eno za drugo). Na ta način smo dosegli, da se pri enem samem dogodku hkrati pokličeta dve metodi (v splošnem pa seveda lahko tudi več metod!). Tak način klicanja dveh ali več metod preko enega samega delegata imenujemo tudi multicasting. Multicasting je še posebej pomemben pri obdelovanju dogodkov (events), o čemer bo več napisanega v nadaljevanju. Obstajata pa še dva načina deklaracije in implementacije delegatov:

• Deklaracija delegata s pomočjo operatorja +=. Na ta način metodo dejansko dodamo k delegatu. Najprej deklariramo samega delegata: to lahko storimo npr. znotraj razreda, ali tako kot pri prejšnji vaji izven metode, v kateri bomo naredili njegovo instanco: private MojDelegat Peti; //deklaracija oz. napoved delegata

Instanco tega delegata sedaj naredimo znotraj neke metode (v prejšnji vaji npr. ob dogodku Load obrazca, ali pa ob kliku na nek gumb): Peti+= Pozdrav; //kreiranje nove instance s pomočjo operatorja +=. Delegatu smo

//dodali metodo Pozdrav Peti("Programer!");//Klic delegatne metode Peti, ki dobi za parameter string //"Programer"

• Deklaracija delegata s pomočjo lastnosti/property-ja oz. s pomočjo rezerviranih besed get in set

Page 102: PROŠNJA - uranic.tsckr.siuranic.tsckr.si/VISUAL C#/VISUAL C#.pdf · MICROSOFT VISUAL C# .NET Srečo Uranič . Visual C# . ... Za začetek novega projekta v okolju Visual C# Standard

Visual C# . NET

Algoritmi in programski jeziki – 4.letnik

102

public delegate void Obdelava();//deklaracija oz. napoved delegata public Obdelava razlika { get { return this.razlika; } set { this.razlika = value; } }

Vaja: Projekt Delegat_Knjigarna prikazuje malo bolj kompleksno deklaracijo in uporabo delegatov. Strukturo Knjiga ter nekaj razredov za obdelavo knjig zapišimo v svojo(ločeno) datoteko Knjigarna.cs. public struct Knjiga { public string Naslov; public string Avtor; public decimal Cena; public bool Vezana; //vezana ali ne public string vez //lastnost/property za vračanje podatka o tem, ali gre za vezano knjigo { get { if (Vezana==true) return "DA"; else return "NE"; } } public Knjiga(string naslov, string avtor, decimal cena, bool vezana)//Konstruktor { Naslov = naslov; Avtor = avtor; Cena = cena; Vezana = vezana; } public string PodatkiOKnjigi() //javna metoda, ki vrača podatke o knjigi { return ("Naslov: "+Naslov+", Avtor: "+Avtor+", Cena: "+Cena+" Vezana: "+vez); } } //Deklaracija delegata za obdelavo knjige public delegate void ObdelavaKnjige(Knjiga knjiga); //Vzdrževanje zbirke knjig public class ZbirkaKnjig { //Deklaracija zbirka knjig - zbirka se imenuje Seznam ArrayList Seznam = new ArrayList(); //Metoda za dodajanje knjige v zbirko Seznam public void DodajKnjigo(string naslov, string avtor, decimal cena, bool vezava) { Seznam.Add(new Knjiga(naslov, avtor, cena, vezava)); } //Klic ustrezne delegatne metode za vsako vezano knjigo iz zbirke //Parameter obdelava je referenca na dejansko metodo (formalni parameter za ime metode) public void ObdelavaVezanihKnjig(ObdelavaKnjige obdelava) { foreach (Knjiga b in Seznam) { if (b.Vezana) //obdelujemo le vezane knjige obdelava(b); //klic ustreznega delegata } } public ArrayList Podatki() //metoda tipa ArrayList, ki vrne zbirko vseh knjig { ArrayList zac = new ArrayList(); foreach (Knjiga k in Seznam) { zac.Add(k.PodatkiOKnjigi()); } return zac; } }

Page 103: PROŠNJA - uranic.tsckr.siuranic.tsckr.si/VISUAL C#/VISUAL C#.pdf · MICROSOFT VISUAL C# .NET Srečo Uranič . Visual C# . ... Za začetek novega projekta v okolju Visual C# Standard

Visual C# . NET

Algoritmi in programski jeziki – 4.letnik

103

class VsotaIzpis //razred vsebuje metode za izračun skupne cene in za izpis naslovov knjig { int stevecKnjig = 0; public decimal cenaKnjig = 0.0m; public string Naslovi = ""; public void DodajVsoti(Knjiga knjiga) { stevecKnjig += 1; cenaKnjig += knjiga.Cena; } public decimal PovprecnaCena() { return cenaKnjig / stevecKnjig; } //Izpis naslova knjige public void IzpisiNaslov(Knjiga k) { Naslovi=Naslovi+k.Naslov+"\n"; } }

Oblikujmo še obrazec, ki naj vsebuje gradnik ListBox, v katerem bodo prikazani podatki o knjigah. Vsebina ListBox-a je na začetku seveda prazna; podatki o knjigah se prikažejo šele v zagnanem projektu.

ListBox

Še koda, ki pripada obrazcu: ZbirkaKnjig zbirka = new ZbirkaKnjig();//Deklaracija zbirke knjig //Ob dogodku Load obrazca dodamo v zbirko nekaj knjig, te pa zapišemo še v gradnik listBox1 private void Form1_Load(object sender, EventArgs e) { //V zbirko dodajamo knjige s pomočjo metode, ki smo napisali v datoteki Knjigarna.cs zbirka.DodajKnjigo("C++ za velike in male", "Matjaž Prtenjak", 9.95m, true); zbirka.DodajKnjigo("Microsoft Visual C#.NET", "John Sharp", 39.95m, true); zbirka.DodajKnjigo("Angleško-Slovenski Slovar", "Bernarda Potočnik", 19.95m, false); zbirka.DodajKnjigo("Visual C#.NET", "Srečo Uranič", 0.00m, true); //V listBox1 prepišem vse podatke iz ArrayLista-a zbirka listBox1.DataSource=zbirka.Podatki(); } //odzivna metoda gumba Skupna cena vezanih knjig private void button2_Click(object sender, EventArgs e) { VsotaIzpis sCena = new VsotaIzpis(); //Kreiranje novega delegatnega objekta povezanega z metodo DodajVsoti objekta sCena zbirka.ObdelavaVezanihKnjig(new ObdelavaKnjige(sCena.DodajVsoti)); MessageBox.Show("Skupna cena vezanih knjig: " + Math.Round(sCena.cenaKnjig, 2) + " EUR"); } //odzivna metoda gumba Povprečna cena vezanih knjig private void button1_Click(object sender, EventArgs e) { VsotaIzpis sCena = new VsotaIzpis();//nov objekt //Kreiranje novega delegatnega objekta povezanega z metodo DodajVsoti objekta sCena zbirka.ObdelavaVezanihKnjig(new ObdelavaKnjige(sCena.DodajVsoti)); MessageBox.Show("Povprečna cena vezane knige: "+Math.Round(sCena.PovprecnaCena(),2)+" EUR"); } //odzivna metoda gumba Naslovi vezanih knjig private void button3_Click(object sender, EventArgs e) { VsotaIzpis sIzpis = new VsotaIzpis(); //Kreiranje novega delegatnega objekta povezanega z metodo DodajVsoti objekta sCena zbirka.ObdelavaVezanihKnjig(new ObdelavaKnjige(sIzpis.IzpisiNaslov));

Page 104: PROŠNJA - uranic.tsckr.siuranic.tsckr.si/VISUAL C#/VISUAL C#.pdf · MICROSOFT VISUAL C# .NET Srečo Uranič . Visual C# . ... Za začetek novega projekta v okolju Visual C# Standard

Visual C# . NET

Algoritmi in programski jeziki – 4.letnik

104

MessageBox.Show("Naslovi vezanih knjig:\n" + sIzpis.Naslovi); }

Anonimne metode in delegati

Vsi dosedanji primeri dodajanja metod delegatu so bili taki, da so vsebovali imena metod. Tak način je seveda zelo primeren v primerih, ko obstaja primerna metoda, ki se ujema s sintakso delegata (se ujema z njegovim tipom, številom in tipom parametrov). Če pa taka metoda ne obstaja, mi pa bi vseeno radi preko delegata izvedli neko kodo (zaporedje stavkov, klic metode ali več metod), pa nam C# nudi možnost uporabe anonimne metode (Anonymous Method). Anonimna metoda je metoda, ki nima imena, njena sintaksa pa je takale: Ime_delegata += delegate { //delegatu smo dodali poljubno kodo, lahko tudi klic metode, …}; Primer: Deklarirajmo delegata z imenom Printer in napišimo še motodo Delo, ki po tipu in parametrih ustreza temu delegatu: delegate void Printer(string s); //deklaracija delegata class TestClass { //Metoda, ki ustreza delegatu Printer public static void Delo(string k) { MessageBox.Show(k); } }

Instanco delegata Printer deklariramo sedaj takole (v spodnjem primeru je koda zapisana v dogodek Load obrazca): //Instanca delegata, ki uorablja anonimno metodo Printer p = delegate(string j) { MessageBox.Show(j); }; //Rezultat klica anonimnega delegata(oz delegata, ki uporablja anonimno metodo) p("Klic delegata, ki uporablja anonimno metodo."); //Kreiranje instanca delegata, ki uporablja metodo z imenom "Delo": p = new Printer(TestClass.Delo); // //Rezultat klica delegata, ki dobi referenco na metodo "Delo" p("Klic delegata, ki uporablja metodo Delo.");

Dogodki (events) Čeprav nam delegati preko referenc omogočajo neposreden klic poljubnega števila metod, se moramo na delegate še vedno sklicevati eksplicitno. V veliko primerih pa bi bilo koristno, da bi se delegat izvedel avtomatično, ko se zgodi nekaj pomembnega. Pri programiranju večkrat naletimo na situacijo, ko moramo izvesti določeno dejanje, pa v naprej ne vemo, katero metodo, ali pa celo nad katerim objektom želimo izvesti neko metodo, da se bo le-ta v času izvajanja programa izvedla. Tako mora npr. nek gumb, ko ga uporabnik klikne, obvestiti nek objekt, a v času načrtovanja programa še ne vemo kateri objekt to bo. Da nam ni potrebno določen gumb prikleniti na nek objekt, ga raje povežemo z nekim delegatom, v fazi izvajanja programa pa temu delegatu posredujemo ustrezno referenco na pravo metodo (ali pa reference na več metod). V pionirskih časih programiranja, so bili programi napisani tako, da se je program začel izvajati na začetku, nato pa se izvajal po vrsti, stavek za stavkom, dokler se ni končal. Današnje GUI (Graphical User Interface) programiranje pa zahteva drugačen pristop, imenovan dogodkovno-gnano programiranje (event-driven programming). Moderni programi uporabniku ponudijo nek grafični uporabniški vmesnik, nato pa čakajo na uporabnikova dejanja (npr. izbiro določene opcije menija, klikanje na gumbe, itd.). Vsako tako dejanje uporabnika ima za posledico zagon nekega dogodka (event-a). Nekateri dogodki pa se lahko sprožijo tudi brez

Page 105: PROŠNJA - uranic.tsckr.siuranic.tsckr.si/VISUAL C#/VISUAL C#.pdf · MICROSOFT VISUAL C# .NET Srečo Uranič . Visual C# . ... Za začetek novega projekta v okolju Visual C# Standard

Visual C# . NET

Algoritmi in programski jeziki – 4.letnik

105

posredovanja uporabnika, npr. dogodki, ki se izvedejo ob prihajajoči pošti, zaključku nekega kopiranja ali pa ob taktu ure. Dogodek (event) je v bistvu enkapsulacija ideje, da se je zgodilo nekaj pomembnega in da se mora program zaradi tega ustrezno odzvati. Med dogodki in delegati pa obstaja tesna povezava. Prožna obdelava dogodkov namreč zahteva, da je odgovor na tak dogodek posredovan ustreznemu obdelovalcu dogodkov (event handler-ju). Event handler pa je v C# tipično implementiran kot delegat. Delegati se prav tako uporabljajo za komunikacijo in sporazumevanje razredov med seboj, pa seveda za določanje metod, ki bodo postale znane šele v času izvajanja programa.

Deklaracija dogodka

Dogodek deklariramo v razredu, z namenom, da se zapisani stavki obnašajo kot izvorna koda (source) tega dogodka. Običajno so namreč razredi tisti, ki opazujejo okolje in sprožijo ustrezen dogodek, ko se zgodi nekaj pomembnega. Dogodek običajno vsebuje seznam metod, ki jih potrebno izvesti, ko se la-ta aktivira. Takim metodam v svetu objektnega programiranja pravimo tudi naročniške metode (subscribers). Dogodek deklariramo podobno kor deklariramo polje. Ker pa dogodke deklariramo zato, da jih bomo uporabili kot delegate, mora biti tip metode obvezno delegat, pred deklaracijo pa stoji še besedica event. Primer deklaracije: class KontrolaTemperature { public delegate void DelegatZaZaustavitevStroja(); //deklaracija delegata public event DelegatZaZaustavitevStroja PregrevanjeStroja; //deklaracija dogodka .... }

Logika razreda KontrolaTemperature mora biti v tem primeru seveda taka, da v primeru pregrevanja stroja avtomatično sproži dogodek PregrevanjeStroja. Ta dogodek pa znotraj sebe vsebuje interno zbirko prirejenih delegatov, ki dobijo potrebne reference na metode, ki se morajo v tem primeru izvesti.

&aročanje (subscribing) na nek dogodek

Tako kot delegate, lahko tudi metode k instanci nekega dogodka dodajamo s pomočjo operatorja +=. Dogodek razreda KontrolaTempetarure prikazanega kot primer deklaracije dogodka, bi lahko implementirali npr. takole: //najprej nova instanca objekta razreda KontrolaTemperature KontrolaTemperature tempMonitor = new KontrolaTemperature(); //sledi dodajanje (naročanje) metod dogodku PregrevanjeStroja tempMonitor.PregrevanjeStroja+= delegate {podajalec.konecPodajanja(0)}; //klic anonimne metode //dogodku PregrevanjeStroja dodamo metodo KonecSpajkanja razreda spajkalnik (ta je seveda //deklarirana nekje drugje!!!) tempMonitor.PregrevanjeStroja+=spajkalnik.KonecSpajkanja; //dogodku PregrevanjeStroja dodamo metodo KonecBarvanja razreda barve (ta je seveda //deklarirana nekje drugje!!!) tempMonitor.PregrevanjeStroja+=barve.konecBarvanja;

Sintaksa je torej povsem enaka kot pri dodajanju metod nekemu delegatu. Ko se bo v zgornjem primeru zgodil dogodek tempMonitor.PregrevanjeStroja, bo le-a poklical vse metode, ki smo jih dodali v ta dogodek (oz. vse metode, na katere je naročen). Metode pa lahko instanci nekega dogodka tudi odvzemamo. To storimo s pomočjo operatorja -=, npr.: tempMonitor.PregrevanjeStroja-=spajkalnik.KonecSpajkanja; //instanci dogodka odvzamemo metodo

Klic oz. zagon nekega dogodka

Page 106: PROŠNJA - uranic.tsckr.siuranic.tsckr.si/VISUAL C#/VISUAL C#.pdf · MICROSOFT VISUAL C# .NET Srečo Uranič . Visual C# . ... Za začetek novega projekta v okolju Visual C# Standard

Visual C# . NET

Algoritmi in programski jeziki – 4.letnik

106

Dogodek kličemo oz zaženemo tako kot delegata – kličemo ga torej tako kot metodo. Ko zaženemo nek dogodek, se kličejo vsi temu dogodku pripeti delegati, drug za drugim. Primer: Na obrazec postavimo en sam gumb, v kodi obrazca pa deklarirajmo dva delegata, nato pa še razred Test, znotraj katerega sta deklarirana dva dogodka in še dve metodi, ki sprožita ustrezen dogodek: public delegate void ObdelovalecObrazca(); //deklaracija delegata ObdelovalecObrazca public delegate void ZapiranjeProjekta(); //deklaracija delegata ZapiranjeProjekta class Test { // ObOdpiranjuObrazca je dogodek, ki bo implementiran preko delegata ObdelovalecObrazca. public event ObdelovalecObrazca ObOdpiranjuObrazca;//deklaracija dogodka ObOdpiranjuObrazca public event ObdelovalecObrazca ObZapiranjuObrazca;//deklaracija dogodka ObZapiranjuObrazca public void Odpri() //Metoda, ki sproži dogodek ObOdpiranjuObrazca() { ObOdpiranjuObrazca(); } public void Zapri()//Metoda, ki sproži dogodek ObZapiranjuObrazca() { ObZapiranjuObrazca(); } }

Napišimo še dve metodi, ki se bosta klicali ob sprožitvi dogodka ObOdpiranjuObrazca oz. dogodka ObZapiranjuObrazca . public void OblikujObrazec() //metoda, katere referenco bomo dodali dogodku ObOdpiranjuObrazca { this.Height = 131; this.Width = 300; this.BackColor = Color.Aqua; this.Text= "Okno je oblikoval delegat!"; this.ControlBox = false; } public void Zakljucek() //metoda, katere referenco bomo dodali dogodku ObZapiranuObrazca { this.button1.Text = "Kliknil si me!"; this.ControlBox = true; ; }

Ob kreiranju obrazca (dogodek Load) kreirajmo najprej instanco razreda Test, nato pa dogodku ObOdpiranjuObrazca dodajmo najprej našo lastno metodo za oblikovanje obrazca (metoda OblikujObrazec), nato pa še no anonimno metodo, v katero zapišimo prikaz sporočilnega okna MessageBox. Dogodek ObOdpiranjuObrazca torej vsebuje dve metodi. Končno še zaženemo metodo mb.Odpri(), ki sproži naš dogodek ObOdpiranjuObrazca(). private void Form1_Load(object sender, EventArgs e) { Test mb = new Test(); //nova instanca razreda Test //Določimo metodo, ki se sproži ob dogodku ObOdpiranjuObrazca mb.ObOdpiranjuObrazca += new ObdelovalecObrazca(OblikujObrazec); //Dogodku ObOdpiranjuObrazca dodajmo še eno dodatno anonimno metodo - dogodek //ObOdpiranjuObrazca sedaj vsebuje dve metodi!!! mb.ObOdpiranjuObrazca += delegate { MessageBox.Show("Pozdravljen uporabnik!"); }; //Sprožimo dogodek, ki znotraj sebe kliče dve prej dodani metodi mb.Odpri(); }

Na podoben način napišimo kodo, ki naj se izvede ob uporabnikovem kliku na gumb, ki je na obrazcu: private void button1_Click(object sender, EventArgs e) { Test mz = new Test();//nova instanca razreda Test //Določimo metodo, ki se sproži ob dogodku ObZapiranjuObrazca mz.ObZapiranjuObrazca += new ObdelovalecObrazca(Zakljucek); //dodajmo še eno anonimno metodo - dogodek ObZapiranjuObrazca sedaj vsebuje dve metodi!!!

Page 107: PROŠNJA - uranic.tsckr.siuranic.tsckr.si/VISUAL C#/VISUAL C#.pdf · MICROSOFT VISUAL C# .NET Srečo Uranič . Visual C# . ... Za začetek novega projekta v okolju Visual C# Standard

Visual C# . NET

Algoritmi in programski jeziki – 4.letnik

107

mz.ObZapiranjuObrazca += delegate { MessageBox.Show("OK!\nSedaj lahko okno zapreš s sistemskim gumbom!\nNasvidenje!"); }; //Sprožimo dogodek, ki znotraj sebe kliče dve prej dodani metodi mz.Zapri(); }

Odjavljanje (unsubscribing) dogodka

Metode lahko instanci nekega dogodka tudi odvzemamo – to storimo s pomočjo operatorja -=. //odjavljanje (unsubscribing) metode dogodku PregrevanjeStroja tempMonitor.PregrevanjeStroja-= delegate {podajalec.konecPodajanja(0)};

Razumevanje dogodkov grafičnega uporabniškega vmesnika ( GUI - Graphical User Interface)

Za izgradnjo grafičnega uporabniškega vmesnika uporabljajo razredi (pa tudi drugi sestavni deli .NET ogrodja) v veliki meri tudi dogodke. Tako npr. razred Button , ki je izpeljan iz razreda Control, podeduje javni dogodek imenovan Click, ki je tipa EventHandler. Delegat EventHandler pričakuje dva parametra: referenco na objekt, ki je bil vzrok za to, da se je ta dogodek sploh zgodil, in objekt EventArgs, ki vsebuje dodatno informacijo o dogodku: namespace System { public delegate void EventHandler(object sender, EventArgs args); public class EventArgs { . . . } }

namespace System.Windows.Forms { public class Control { public event EventHandler Click; { . . . } public class Button: Control { . . . } } }

Ko kliknemo nek gumb grafičnega vmesnika, razred Button avtomatično sproži dogodek Click (kako se to zgodi v resnici, presega namen te literature). Posledica tega dejstva je, da lahko kreiramo delegata za neko poljubno izbrano (napisano) metodo in tega delegata pripnemo k zahtevanemu dogodku. Naslednji primer npr. prikazuje okenski obrazec (Windows Form), ki vsebuje gumb z imenom OK, metodo imenovano OK_Klik in potrebno kodo, ki poskrbi za povezavo dogodka Click gumba OK in metode OK_Klik. Zaradi enostavnosti je v spodnjem primeru prikazana celotna vsebina modula, ki pripada obrazcu z imenom Form1: using System.Drawing; using System.Text; using System.Windows.Forms; namespace Dogodek_OK_Klik { public partial class Form1 : Form { public Form1() { InitializeComponent();

//this.OK = new System.Windows.Forms.Button();���� ta vrstica je zajeta že v datoteki Form1.Designer.cs

Page 108: PROŠNJA - uranic.tsckr.siuranic.tsckr.si/VISUAL C#/VISUAL C#.pdf · MICROSOFT VISUAL C# .NET Srečo Uranič . Visual C# . ... Za začetek novega projekta v okolju Visual C# Standard

Visual C# . NET

Algoritmi in programski jeziki – 4.letnik

108

//dogodku Click gumba OK dodamo še delegatno metodo OK_Klik – pri tem moramo //seveda vedeti, da je delegat z imenom EventHandler ŽE deklariran v razredih //imenskega prostora System this.OK.Click += new System.EventHandler(this.OK_Klik); } private void OK_Klik(object sender, System.EventArgs args) { MessageBox.Show("Ob kliku na gumb OK se je izvedla delegatna metoda OK_Klik"); } } }

Ob kliku na gumb OK se tako avtomatično izvede metoda OK_Klik. Dogodki, ki jih generirajo razredi, ki so sestavni del GUI, vedno sledijo enakemu vzorcu kot je prikazan v prejšnjem primeru. Dogodki so vedno delegati tipa void in imajo dva parametra. Prvi parameter je vedno pošiljatelj dogodka (sender), drugi parameter pa je vedno argument EventArgs (ali pa razred izpeljan iz EventArgs). Argument sender omogoča, da lahko neko metodo uporabimo v več dogodkih. Delegirana metoda lahko preveri pošiljatelja (preveri argument sender) in se nato ustrezno odzove. Tako lahko npr. eno samo metodo uporabimo za naročanje na dogodek Click dvem različnim gumbom (isto metodo dodamo različnim dogodkom). Ko se dogodek sproži, koda v metodi preveri pošiljatelja (preveri parameter sender) in s tem ugotovi, kateri gumb je bil kliknjen. Vaja: Vajo sestavljata dva razreda, razred Ura in razred TikTak, ter obrazec ki ob kliku na gumb prikazuje oz zaustavi tekoči čas. V vaji je uporabljen razred Timer, ki je sestavni del .NET ogrodja. Ta razred že vsebuje dogodek Elapsed, ki ga lahko sprožimo v poljubnem intervalu, določenim z lastnostjo Interval. Če nastavimo ta interval na 1000, se bo dogodek Elapsed sprožil vsakih 1000 milisekund, oz. vsako sekundo – seveda pa moramo pred tem timerjevo lastnost Enabled nastaviti na true. V razredu Timer je prav tako že deklariran delegat z imenom ElapsedEventHandler, s pomočjo katerega lahko naročamo nove, lastne metode k dogodku Elapsed. Metoda trenutniCas razreda TikTak povzema informacijo o trenutnem času, ta informacija pa je posredovana preko parametra ElapsedEventArgs. S pomočjo te informacije je preko metode takt sprožen delegatni dogodek z imenom Takt. Celotna koda datoteke z razredom TikTak: namespace Delegati_Timer { using System.Collections; using System.Timers; //deklaracija delegata Takt tipa void, s tremi parametri public delegate void Takt(int hh, int mm, int ss); class TikTak { public event Takt takt; //s pomočjo delegata Takt deklariramo dogodek takt public TikTak() //konstruktor razreda TikTak { //dogodek gradnika Timer z imeno Elapsed se zgodi vsakič, ko preteče interval //določen z lastnostjo Interval //ElapsedEventHandler je delegat v C#, ki predstavla metodo, ki bo pretečeni //Timerjev dogodek obdelala tiktakanje.Elapsed += new ElapsedEventHandler(trenutniCas); tiktakanje.Interval = 1000; //določimo interval tiktakanje.Enabled = true; } private void trenutniCas(object sender, ElapsedEventArgs args) { int hh = args.SignalTime.Hour; int mm = args.SignalTime.Minute; int ss = args.SignalTime.Second; if (takt != null) { takt(hh, mm, ss); //delegatna metoda s tremi parametri

Page 109: PROŠNJA - uranic.tsckr.siuranic.tsckr.si/VISUAL C#/VISUAL C#.pdf · MICROSOFT VISUAL C# .NET Srečo Uranič . Visual C# . ... Za začetek novega projekta v okolju Visual C# Standard

Visual C# . NET

Algoritmi in programski jeziki – 4.letnik

109

} } private Timer tiktakanje = new Timer();//deklaracija novega objekta tipa Timer } }

Celotna koda datoteke z razredom Ura: namespace Delegati_Timer { using System.Windows.Forms; class Ura { //konstruktor prejme ime objekta, za prikaz časa in zažene metodo Start razreda Ura public Ura(TextBox cilj) { utrip = new TikTak(); //inicializacija nove instance razreda Tikak cas = cilj; Start(); } public void Start() { //utrip je instanca razreda TikTak, ki vsebuje deklaracijo dogodka takt - temu //dogodku sedaj dodamo metodo OsveziCas utrip.takt += new Takt(this.OsveziCas); // } public void Stop() { //utrip je instanca razreda TikTak, ki vsebuje deklaracijo dogodka takt - temu //dogodku sedaj odvzamemo metodo OsveziCas utrip.takt -= new Takt(this.OsveziCas); } //klasična metoda OsveziCas, ki dobi za parametre podatke o tekočem času in le-te //zapiše v TextBox private void OsveziCas(int hh, int mm, int ss) { cas.Text = string.Format("{0:D2}:{1:D2}:{2:D2}", hh, mm, ss); } private TikTak utrip; //zasebno polje tipa Tikak private TextBox cas; //zasebno polje tipa TextBox } }

Na obrazec postavimo dva gumba in vnosno polje:

Zapišimo še kodo, ki so odziva na klik gumbov Zaženi uro in Zaustavi uro. private Ura clock; //deklaracija instance razreda Ura private void start_Click(object sender, System.EventArgs e) { //inicializacija instance clock razreda Ura. Konstruktor zareda Ura poskrbi za //avtoamatični zagon metode Start(). Ta metoda pa delegatnemu dogodku delegata Takt doda //metodo OsveziCas clock = new Ura(digital); } private void stop_Click(object sender, System.EventArgs e) { clock.Stop(); //preko metode Stop delegatnemu dogodku takt odvzamemo metodo OsveziCas }

Page 110: PROŠNJA - uranic.tsckr.siuranic.tsckr.si/VISUAL C#/VISUAL C#.pdf · MICROSOFT VISUAL C# .NET Srečo Uranič . Visual C# . ... Za začetek novega projekta v okolju Visual C# Standard

Visual C# . NET

Algoritmi in programski jeziki – 4.letnik

110

Knjižnice razredov in datoteka z razredi

V dosedanjih zgledih do smo razrede pisali le za "lokalno uporabo", znotraj določenega programa. Razred (ali pa več razredov) pa lahko zapišemo tudi v svojo datoteko, ki jo potem dodajamo k različnim projektom, ali pa celo zgradimo svojo knjižnico razredov, ki jih bomo uporabljali v različnih programih. Na ta način bomo tudi bolj ločili tisti del programiranja, ko gradimo razrede in tisti del, ko uporabljamo objekte določenega razreda.

Ustvarjanje nove knjižnice razredov

Naučimo se najprej, kako zgradimo svojo knjižnjico razredov: v okolju Visual C# tokrat ne izberemo Windows Applications/Console Application, ampak Class Library Kot ime knjižnjice bomo napisali npr. MojiRazredi. V to knjižnjico bomo kasneje lahko dodajali vse tiste razrede, ki jih bomo ustvarjali.

S klikom na gumb OK potrdimo našo izbiro. Vidimo, da je Visual C# za nas pripravil okolje in naredil tako

imenovam imenski prostor MojiRazredi. Znotraj tega imenskega prostora bomo zlagali naše razrede. Ker nam ime MyClass ni všeč, bomo to spremenili v Zajec.

Dodajmo še komentar, ki naj se začne z ///. To so tako imenovani dokumentacijski komentarji. Ti služijo za to, da pripravimo dokumentacijo o razredu. Ta je zelo pomembna, saj nosi tisto informacijo, ki jo kasneje potrebujemo, če želimo razred uporabljati. Zaenkrat si zapomnimo le, da na ustrezno mesto napišemo kratek povzetek o tem, čemu je razred namenjen. Napisali smo torej zelo poenostavljen opis, kako je določen poljuben zajec. Zavedati pa se moramo, da gre v bistvu le za načrt, kakšni so zajci, ne pa za konkretnega zajca. Opazimo tudi, da v knjižnici, ki jo pišemo, ni metode Main. Knjižnica namreč ni namenjena izvajanju oz. poganjanju tako kot smo bili navajeni pri dosedanjih programih, ampak je namenjena uporabi v drugih programih, ki jih bomo pisali kasneje. Preden nadaljujemo knjižnico shranimo: v meniju File izberimo opcijo Save All. Odpre se pogovorno okno za shranjevanje: s klikom na gumb Browse izberimo še mapo, v katero bomo našo knjižnico shranili (v našem primeru je ime mape Moj Razred)

Page 111: PROŠNJA - uranic.tsckr.siuranic.tsckr.si/VISUAL C#/VISUAL C#.pdf · MICROSOFT VISUAL C# .NET Srečo Uranič . Visual C# . ... Za začetek novega projekta v okolju Visual C# Standard

Visual C# . NET

Algoritmi in programski jeziki – 4.letnik

111

Knjižnjico je sedaj potrebno še prevesti. V meniju Build izberimo opcijo Build Solution (ali pa stisnimo tipko F6) in naša knjižnjica je sedaj pripravljena za uporabo. Ustvarili smo namreč ustrezno prevedeno obliko te knjižnjice, ki je dobila ime MojiRazredi.dll (dll je kratica za dinamic link library). Knjižnjica zaenkrat vsebuje le definicijo razreda Zajec. Datoteka se nahaja v mapi ..\ MojRazred\MojiRazredi\MojiRazredi\bin\Debug.

Uporaba knjižnjice razredov

Denimo, da sedaj pišemo nek program, kjer bomo potrebovali zajce. V ta namen sedaj sestavimo program v C#, ki bo med drugim uporabljal že obstoječo knjižnjico z imenom MojiRazredi. Obnašamo se tako kot smo pisali programe do sedaj. Začnimo nov projekt : File/�ew Project/Console applications. Projekt poimenujmo npr. TestZajec. V projekt moramo sedaj dodati še t.i. referenco, da bo naš program prepoznal naš razred MojiRazredi. V Solution Explorerju izberimo TestZajec in z desnim klikom odprimo lebdeči meni, v katerem izberimo opcijo AddReference

Odpre se okno za izbiro ustrezne reference: izberimo najprej jeziček Browse in nato poiščimo datoteko MojiRazredi.dll. Izbiro potrdimo s klikom na gumb OK

Opazimo, da se v Solution Explorerju pojavi nova referenca MojiRazredi, kar pomeni, da imamo sedaj dostop do vseh razredov knjižnjice MojiRazredi. Da bo dostop do naših razredov še lažji, na začetku datoteke dopišimo še en using stavek:

Page 112: PROŠNJA - uranic.tsckr.siuranic.tsckr.si/VISUAL C#/VISUAL C#.pdf · MICROSOFT VISUAL C# .NET Srečo Uranič . Visual C# . ... Za začetek novega projekta v okolju Visual C# Standard

Visual C# . NET

Algoritmi in programski jeziki – 4.letnik

112

using System; using System.Collections.Generic; using System.Linq; using System.Text; using MojiRazredi; //NOVO dodana knjižnjica

Nov objekt razreda Zajec, ki je definiran znotraj knjižnice MojiRazredi lahko sedaj naredimo takole: Zajec rjavko = new Zajec();

V spremenljivki rjavko je v bistvu naslov, kje je novo ustvarjeni zajec (objekt), a kljub temu navadno rečemo, da smo ustvarili objekt rjavko, ki smo ga izpeljali iz razreda Zajec (ali še drugače: naredili smo novo instanco razreda Zajec). Ustvarili smo torej konkretnega zajca po navodilih za razred Zajec. Ta zajec (rjavko) ima torej tri podatke / lastnosti / komponente), to pa so spol, serijska številka in masa. Te lastnosti lahko sedaj določimo poljubno, npr. takole: rjavko.spol = true; rjavko.serijska = "BRGH_17_A"; rjavko.masa = 3.2;

S tako ustvarjenimi spremenljivkami lahko sedaj počnemo povsem enako kot z običajnimi spremenljivkami. Še primer celotne vsebine datoteke TestZajec: using MojiRazredi; //NOVO dodana knjižnjica namespace TestZajec ... static void Main(string[] args) { Zajec rjavko = new Zajec();//ustvarimo novo instanco razreda Zajec rjavko.serijska = "1238-12-0"; rjavko.spol = false; rjavko.masa = 0.12; rjavko.masa = rjavko.masa + 0.3; //maso zajca lahko povečamo //posamezna polja objekta rjavko lahko izpisujemo Console.WriteLine("Zajec ima ser. št.:" + rjavko.serijska); Console.ReadKey(); } } }

Datoteka z razredom (ali več razredi)

Namesto kjnižnjice razredov pa lahko razred (ali pa skupino razredov) zapišemo tudi v posebno datoteko kar znotraj projekta, v bodočih projektih pa to datoteko samo dodamo v projekt. Začnimo nov projekt (nova konzolna aplikacija) in ga poimenujmo OsebnaVozila. Kreirajmo sedaj razred avto s štirimi zasebnimi polji (znamka, model, najvecjahitrost in teza), ter javno metodo za izpis polj tega razreda. Tej

metodi bomo dali ime IzpisPodatkov. Razred bomo zapisali v svojo datoteko, ki jo odpremo takole: v SolutionExplorerju izberimo vrstico OsebnaVozila, nato pa z desnim klikom miške odpremo lebdeči meni. Izberimo opcijo Add in nato Class.

V oknu, ki se odpre, se prepričajmo, če je izbrana opcija Class in vnesimo ime našega razreda – Avto.cs.

Page 113: PROŠNJA - uranic.tsckr.siuranic.tsckr.si/VISUAL C#/VISUAL C#.pdf · MICROSOFT VISUAL C# .NET Srečo Uranič . Visual C# . ... Za začetek novega projekta v okolju Visual C# Standard

Visual C# . NET

Algoritmi in programski jeziki – 4.letnik

113

Vse skupaj potrdimo s klikom na gumb Add. V Solution Explorerju bomo opazili novo vrstico z imenom Avto, v urejevalniku pa bo prikazana začetna vsebina datoteke (razreda) Avto.cs. Vsebino dopolnimo, tako da je izgled celotne datoteke takle: using System; using System.Collections.Generic; using System.Text; namespace OsebnaVozila { class Avto { //polja razreda Avto public string znamka; public string model; public int najvecjahitrost; public double teza; //javna metoda za izpis podatkov o konkretnem avtomobilu. Več o //metodah v razredih bo napisano v nadaljevanju public string IzpisPodatkov() { return ("\nIzpis podatkov o vozilu:\nZnamka: " + znamka + "\nModel: " + model + "\nNajvečja hitrost: " + najvecjahitrost + "\nteža vozila: " + teza); } } }

Datoteko shranimo in sedaj ponovno odprimo datoteko z "glavnim" programom (v Solution Explorerju dvoklinimo vrstico Program.cs). Deklarirajmo nov objekt moj, mu določimo začetne vrednosti in pokličimo metodo IzpisPodatkov: static void Main(string[] args) { Avto moj = new Avto();//deklaracija novega objekta moj //poskrbimo za inicializacijo moj.znamka = "Citroen"; moj.model = "C4 Picasso"; moj.najvecjahitrost = 185; moj.teza = 1850; //klic metode IzpisPodatkov, ki jo objekt moj seveda pozna, saj smo ga //izpeljali iz razreda Avto, v katerem je ta metoda definirana Console.WriteLine(moj.IzpisPodatkov()); }

Razred Avto smo torej napisali v svoji datoteki in ga nato uporabili v projektu, znotraj katerega smo ta razed tudi kreirali. Sedaj pa moramo še pokazati, kako bi ta razred uporabili v nekem drugem, novem proketu. Postopek je naslednji: ustvarimo nov projekt in ga poimenujmo npr. VozniPark. Ker želimo v tem projektu uporabiti razred, ki so ga v prejšnjem projektu zapisali v datoteko Avto.cs, ga lahko v naš novi projekt vključimo takole: v

Page 114: PROŠNJA - uranic.tsckr.siuranic.tsckr.si/VISUAL C#/VISUAL C#.pdf · MICROSOFT VISUAL C# .NET Srečo Uranič . Visual C# . ... Za začetek novega projekta v okolju Visual C# Standard

Visual C# . NET

Algoritmi in programski jeziki – 4.letnik

114

Solution Explorerju izberemo vrstico VozniPark, kliknemo desni miškin gumb in izberimo opcijo Add, ter nato Existing Item…

Odpre se pogovorno okno, s pomočjo katerega sedaj poiščimo datoteko Avto.cs, ki se nahaja znotraj prejšnjega projekta, ki smo ga poimenovali OsebnaVozila. Ko jo najdemo, izbiro potrdimo s klikom na gumb Add.

V Solution Explorerju se ojavi nova vrstica Avto.cs (razred Avto smo dodali v naš novi projekt). Ker smo razred Avto napisali v projektu Osebna vozila, se nanj sklicujemo takole: static void Main(string[] args) { OsebnaVozila.Avto sosedov = new OsebnaVozila.Avto(); //... }

Z objektom sosedov sedaj delamo popolnoma enakovredno kakor v prejšnjem projektu OsebnaVozila, v katerem smo razred Avto tudi definirali. Do razreda Avto smo torej prišli preko imenskega prostora OsebnaVozila. Če pa ta imenski prostor z using stavkom dodamo na začeku programa tako, da napišemo using System; using System.Collections.Generic; using System.Text; using OsebnaVozila; //dodamo imenski prostor

potem pa lahko nov objekt razreda Avto napovemo takole: static void Main(string[] args) { Avto sosedov = new Avto(); //... }

Pokazali smo torej, kako kreiramo svojo lastno knjižnico razredov (dll), ki jo potem dodamo kot referenco k vsem nadaljnjim projektom, prav tako pa smo pokazali kako ustvarimo datoteko z razredom in jo dodajamo k novim projektom. Samo zaradi enostavnosti in lažjega razumevanja, pa bomo v nadaljnji razlagi, primerih in

zgledih, razrede pisali kar znotraj določenega programa.

Page 115: PROŠNJA - uranic.tsckr.siuranic.tsckr.si/VISUAL C#/VISUAL C#.pdf · MICROSOFT VISUAL C# .NET Srečo Uranič . Visual C# . ... Za začetek novega projekta v okolju Visual C# Standard

Visual C# . NET

Algoritmi in programski jeziki – 4.letnik

115

Povzetek

Najenostavnejši razred kreiramo tako, da določimo spremenljivke (polja), ki določajo objekte tega razreda. Te so lahko različnih podatkovnih tipov. Sintaksa najenostavnejše definicije razreda je naslednja: public class ImeRazreda { public podatkovni tip element1; public podatkovni tip element2; … public podatkovni tip elementn; }

Če želimo tak razred uporabljati v nekem programu, moramo v programu dodati referenco na ustrezno prevedeno obliko (dll) tega razreda, ali pa datoteko s tem razredom dodati v program. Objekt tega razreda ustvarimo z new ImeRazreda(). S tem dobimo naslov, kje ta objekt je in ga shranimo v spremenljivko tipa ImeRazreda: ImeRazreda imeObjekta = new ImeRazreda();

Do posameznih spremenljivk (rečemo jim tudi komponente ali pa lastnosti), dostopamo z imeObjekta.imeKomponente;

Page 116: PROŠNJA - uranic.tsckr.siuranic.tsckr.si/VISUAL C#/VISUAL C#.pdf · MICROSOFT VISUAL C# .NET Srečo Uranič . Visual C# . ... Za začetek novega projekta v okolju Visual C# Standard

Visual C# . NET

Algoritmi in programski jeziki – 4.letnik

116

Dogodki Dogodki, ki jih Visual C# med izvajanjem programa sporoča programu se v splošnem delijo v štiri skupine:

• Sistemski dogodki: zaznava in jih programu pošilja operacijski sistem (okna); • Dogodki tipkovnice: sprožajo jih dejavnosti uporabnika pri delu s programom; • Dogodki miške: sprožajo jih dejavnosti uporabnika pri delu s programom; • Dogodki ure: dogodki, ki jih proži ura (gradnik Timer)

Sistemski dogodki Sistemske dogodke zaznava in jih pošilja programu operacijski sistem. Zgodijo se torej sami od sebe, avtomatično. Ti dogodki so skupni mnogim gradnikom, na tem mestu pa bomo omenili le nekaj sistemskih dogodkov obrazca.

Dogodek Razlaga, kdaj se dogodek zgodi

Load Ob nastanku obrazca, ko je obrazec prikazan prvič.

Activated Ko postane obrazec aktiven.

Deactivate Ko aktiven obrazec postane neaktiven.

Shown Ko se obrazec prikaže prvič (za dogodkom Load).

FormClosing Ob zapiranju obrazca, preden se obrazec zapre (z ukazom Close ali gumbom za zapiranje).

FormClosed Ko se obrazec že zapre ( z ukazom Close ali gumbom za zapiranje).

Paint Ob poslikavi obrazca.

Resize Ob spremembi velikosti obrazca.

• Dogodek Load: Dogodek ki se zgodi ko je obrazec prikazan prvič. Uporabimo ga npr. za definiranje začetnih vrednosti spremenljivk, začetnih lastnosti gradnikov, ...;

• Dogodek FormClosing: Zgodi se preden se obrazec zapre. Preden se obrazec zapre, se sprostijo oz. uničijo vse spremenljivke in objekti, ki so bili deklarirani na obrazcu (oz. na splošno v gradniku, ki se zapira). Pomnilnik, ki ga je zasedal objekt se sprosti. Uničenje oz. sprostitev pa lahko preprečimo tako, da drugi parameter te metode (to je parameter e, ki je tipa FormClosingEventArgs postavimo na true. Če pa je bil obrazec odprt z metodo ShowDialog (modalni obrazec), zapiranje obrazca povzroči, da postane obrazec skrit in se ne uniči;

• Activated: Dogodek se zgodi, ko se obrazec, (ki ga je npr. uporabnik skril z metodo Hide) ponovno prikaže. Ta dogodek lahko izkoristimo npr. za ažuriranje vsebine obrazca, ki bazira na spremembah podatkov, ki so se zgodile medtem, ko je bil obrazec neaktiven;

• Deactivate: Dogodek se zgodi, ko aktiven obrazec postane neaktiven. Obrazec lahko postane neaktiven zaradi naslednjih razlogov

o Ko je lastnost ActiveForm programsko prirejena drugemu obrazcu o Ko se uporabnik premakne na drug obrazec s klikom miške ali pa npr. preko neke bližnjice

(Link control) na neko spletno stran. Če se nobeden od naštetih razlogov ne zgodi, aktivni obrazec nikoli ne prejme sistemskega dogodka Deactivate.

Page 117: PROŠNJA - uranic.tsckr.siuranic.tsckr.si/VISUAL C#/VISUAL C#.pdf · MICROSOFT VISUAL C# .NET Srečo Uranič . Visual C# . ... Za začetek novega projekta v okolju Visual C# Standard

Visual C# . NET

Algoritmi in programski jeziki – 4.letnik

117

• Shown: Dogodek se zgodi, ko je obrazec prikazan prvič. V primeru, da je bil obrazec šele kreiran, se najprej zgodi dogodek Load, nato pa dogodek Shown. Dogodek pa se ne zgodi npr. ob maksimiranju obrazca, ali pa ob ponovni poslikavi obrazca in podobno.

• FormClosed: Dogodek se zgodi, ko uPorabnik zapre obrazec, potem ko se obrazec že zapre. • Paint: Dogodek se zgodi ob ponovni poslikavi obrazca. V praksi to pomeni tedaj, ko je bil obrazec npr.

delno zakrit ali pa npr. pomanjšan. • Resize: Dogodek se zgodi ob spreminjanju velikosti obrazca (oz. gradnika – seveda le tistega gradnika,

kateremu velikost med delovanjem programa sploh lahko spreminjamo).

Dogodki ki jih sproži tipkovnica Obrazci v okolju Windows obravnavajo vnose preko tipkovnice z obdelavo dogodkov ki jih proži tipkovnica. Obstajata dva dogodka, ki se zgodita, ko uporabnik pritisne tipko na tipkovnici in en dogodek ko spusti tipko na tipkovnici. Dogodke tipkovnice predstavlja naslednja tabela:

Dogodek Tipkovnice

R a z l a g a

KeyDown Dogodek ki se izvede ob pritisku na katerokoli tipko. Zgodi se samo enkrat.

KeyPress Dogodek se lahko zgodi večkrat, če uporabnik drži pritisnjeno neko tipko.

KeyUp Dogodek se zgodi, ko uporabnik spusti tipko.

Ko uporabnik pritisne tipko, je potrebno najprej preveriti, kateri dogodek se bo izvedel, to pa je odvisno od tega, ali je pritisnjena tipka nek znak, ali pa gre za katerokoli tipko na tipkovnici. Znakovne tipke so vse tipke, ki predstavljajo množico znakov (npr. znaki 'a', 'J', ',', '+', …). Če kombinacija tipk, ki jih uporabnik pritisne, ustreza nekemu znaku, se zgodi dogodek KeyPress. Vrstni red dogodkov, ki jih proži tipkovnica Splošno zaporedje dogodkov, ki jih proži tipkovnica je takole:

• KeyDown – izvede se ob pritisku na katerokoli tipko • KeyPress – izvede se ob pritisku na tipko, ki predstavlja nek znak iz množice vseh znakov.

Dogodek se bo sprožil če npr. uporabnik pritisne tipko 'A', ali pa npr. če uporabnik pritisne kombinacijo tipk Shift + 'A', ali pa če npr. uporabnik pritisne kombinacijo tipk AltGr + 'Q', …

• KeyUp – izvede se, ko uporabnik spusti katerokoli tipko. Vaja: Za prikaz dogodkov, ki jih proži tipkovnica kreirajmo nov projekt in ga poimenujmo DogodkiTipkovnice. Obrazec naj zgleda takole:

Gradniki tipa TextBox Zgornje (rumeno) vnosno polje je namenjeno le komentarju (navodilu), ostalim trem vnosnim poljem pa za prikaz dogodkov tipkovnice priredimo naslednje dogodke: KeyDown za prvo vnosno poje, KeyPress za drugo vnosno polje in KeyUp za tretje vnosno polje. Za implementacijo vseh treh metod moramo seveda

Page 118: PROŠNJA - uranic.tsckr.siuranic.tsckr.si/VISUAL C#/VISUAL C#.pdf · MICROSOFT VISUAL C# .NET Srečo Uranič . Visual C# . ... Za začetek novega projekta v okolju Visual C# Standard

Visual C# . NET

Algoritmi in programski jeziki – 4.letnik

118

najprej poskrbeti, da bo posamezen gradnik aktiven (mu dati t.i. focus). Koda, ki pripada posameznim dogodkom, oz. jo zapišemo k posameznem dogodku pa je npr. takale:

• Metoda KeyDown

private void textBox2_KeyDown(object sender, KeyEventArgs e) { MessageBox.Show("Pritisnil si tipko "+e.KeyCode+" - Koda tipke = " +Convert.ToString((int)e.KeyCode)); }

Drugi parameter te funkcije je tipa KeyEventArgs – to je razred, ki vsebuje nekaj zelo koristnih metod, s katerimi lahko kontroliramo uporabnikov pritisk na tipkovnico. Metoda KeyCode npr. vrne ime pritisnjene tipke, če pa to ime z eksplicitno konverzijo spremenimo v celo število pa dobimo ustrezno številsko kodo te tipke na tipkovnici!

• Metoda KeyPress

private void TBVnos1_KeyPress(object sender, KeyPressEventArgs e) { MessageBox.Show("Pritisnil si tipko " + e.KeyChar); }

S pomočjo metode MessageBox, drugega parametra (KeyPressEventArgs) dogodka KeyPress in lastnosti KeyChar smo na ekranu dobili rezultat uporabnikovega pritiska tipke (ali pa kombinacije tipk).

• Metoda KeyUp

private void TBVnos2_KeyUp(object sender, KeyEventArgs e) { MessageBox.Show("Spustil si tipko " + e.KeyCode); }

S pomočjo metode MessageBox, drugega parametra (KeyEventArgs) dogodka KeyUp in lastnosti KeyCode smo na ekranu dobili informacijo o tem, katera tipka je bila spuščena.

Vaja: Opisane dogodke pogostokrat uporabljamo za kontrolo uporabnikovih vnosov podatkov. V naslednji vaji bo prikazano, kako uporabniku preprečimo npr. vnos znakov v vnosno polje, ki naj bi predstavljalo nek znesek, ali pa vnos numeričnih podatkov v vnosno polje, ki naj bi predstavljalo zaporedje črk abecede, ipd. Kreirajmo projekt Kontrola_Vnosa; izgled obrazca:

Gradnik numericUpDown je namenjen vnosu izključno celih števil. Z lastnostma Max in Min lahko določimo največje in najmanjše število, ki ga uporabnik lahko vnese. Število lahko vnesemo ročno (priporočljivo če gre za večja števila), ali pa s pomočjo klikov miške na desni strani gradnika (kadar gre za manjša števila). Gradnik je seveda namenjen le vnosu celih števil, za drugačne potrebe pa je neuporaben. Kadar torej želimo specifične vnose in uporabniku dovoliti le

določene tipke, oz. mu preprečiti nezaželene vnose, moramo to storiti s pomočjo kode in dogodka KeyPress! Nekaj primerov je realiziranih v tej vaji, pri tem pa se seveda sklicujemo na lastnost gradnikov Handled.

Page 119: PROŠNJA - uranic.tsckr.siuranic.tsckr.si/VISUAL C#/VISUAL C#.pdf · MICROSOFT VISUAL C# .NET Srečo Uranič . Visual C# . ... Za začetek novega projekta v okolju Visual C# Standard

Visual C# . NET

Algoritmi in programski jeziki – 4.letnik

119

Uporabimo jo takrat, kadar želimo, da se prehiteti nek določen dogodek (oz. ga nekako umetno zaključiti!). Nekaj primerov iz te vaje:

• Uporabniku dovolimo le vnos znakov angleške abecede:

private void textBox1_KeyPress(object sender, KeyPressEventArgs e) { if ((e.KeyChar < 'A') || (e.KeyChar > 'Z')) e.Handled = true; }

Stavek e.Handled = true; dejansko zaključi metodo preden se konča v obliki pritisnjenega znaka, ki pa ni znak angleške abecede. Celotni zgornji stavek moramo torej razumeti takole: če pritisnjena tipka ni velika črka med A in Z, potem velja, kot da nismo pritisnili nobene tipke.

• Uporabniku dovolimo le vnos znakov D ali N:

private void textBox1_KeyPress(object sender, KeyPressEventArgs e) { if ((e.KeyChar != 'D') && (e.KeyChar != 'N')) e.Handled = true; }

Celotni zgornji stavek moramo torej razumeti takole: če pritisnjena tipka različna od velike črka D in hkrati različna od velike črke N, potem velja, kot da nismo pritisnili nobene tipke.

• Uporabniku dovolimo le vnos cifer in decimalne vejic, dovolimo pa mu tudi brisanje že vnesenih

znakov

private void textBox3_KeyPress(object sender, KeyPressEventArgs e) { if (((e.KeyChar < '0')||(e.KeyChar > '9')) && (e.KeyChar != ',') && (e.KeyChar != (char)(8))) e.Handled = true; }

V primeru smo se sklicevali na kodo tipke BackSpace (tipka ima numerično kodo 8!!!).

Namesto dogodka KeyPress bi za kontrolo vnosa lahko uporabili tudi dogodek KeyDown, a v tem primeri ugotavljamo pritisnjeno tipko s pomočjo lastnosti KeyCode.

private void textBox3_KeyDown(object sender, KeyEventArgs e) { if (((e.KeyCode < Keys.D0) || (e.KeyCode > Keys.D9)) && (e.KeyCode != Keys.Oemcomma) && (e.KeyCode != Keys.Back)) e.Handled = true; }

Kode posameznih tipk lahko dobimo tudi s pomočjo zaslonske pomoči, predstavljene pa so tudi na koncu tega priročnika v dodatku številka 1.

Page 120: PROŠNJA - uranic.tsckr.siuranic.tsckr.si/VISUAL C#/VISUAL C#.pdf · MICROSOFT VISUAL C# .NET Srečo Uranič . Visual C# . ... Za začetek novega projekta v okolju Visual C# Standard

Visual C# . NET

Algoritmi in programski jeziki – 4.letnik

120

Dogodki ki jih sproži miška Pri obdelavi dogodkov, ki jih proži miška, nas običajno zanima pozicija miškinega kazalca in stanje njenih gumbov.

Pozicija miškinega kazalca

Ko uporabnik premakne miško, operacijski sistem poskrbi za premik miškinega kazalca na zaslonu. Kazalec predstavlja eno samo točko (pixel), ki ji operacijski sistem sledi in prepoznava kot lokacijo tega kazalca. Ob premiku miške ali pa pritisku gumba se zgodi eden od dogodkov miške. Trenutni položaj miške lahko tako npr. ugotovimo s pomočjo lastnosti Location parametra MouseEventArgs, ali pa npr. z uporabo lastnosti Position razreda Cursor. S pomočjo informacije, ki jo dobimo o položaju miške tako lahko izvedemo točno določeno operacijo, ki je odvisna od tega, kje in kdaj smo kliknili z miško (npr. pri gradnikih ListView, TreeView, MonthCalendar, DataGridView, ..).

Dogodki miške

Temeljni problem, kako obdelati neko potezo narejeno z miško, je pravilna obdelava miškinih dogodkov. V naslednji tabeli so prikazani vsi dogodke, ki jih miška proži, zraven pa je razlaga, kdaj do njih pride.

Dogodek Miške R a z l a g a

Click Dogodek se zgodi ko spustimo miškin gumb, to je tik preden se zgodi dogodek MouseUp. Ta dogodek obdelamo tipično tedaj, ko nas zanima samo uporabnikov klik na določen gradnik.

MouseClick

Dogodek se zgodi, ko uporabnik z miško klikne nek gradnik (kot klik na določen gradnik se sicer šteje tudi pritisk na tipko <Enter>) . Ta dogodek uporabimo tedaj, ko želimo ob kliku na nek gradnik dobiti podatke o sami miški. Dogodek MouseClick se zgodi za dogodkom Click.

DoubleClick Dogodek se zgodi, ko na nek gradnik dvokliknemo. Uporabimo ga le tedaj, ko res želimo preveriti, ali je uporabnik dvokliknil nek gradnik.

MouseDoubleClick Dogodek se zgodi, ko uporabnik z miško dvoklikne na določen gradnik. Ta dogodek uporabimo tedaj, ko želimo ob dvokliku na nek gradnik dobiti podatke o sami miški.

MouseDown Dogodek se zgodi, ko je kazalnik miške nad določenim gradnikom in uporabnik pritisne enega od miškinih gumbov.

MouseEnter Dogodek se zgodi, ko miškin kazalec doseže rob nekega gradnika, oziroma samo površino tega gradnika – odvisno od vrste gradnika.

MouseHoover Dogodek se zgodi, ko se z miško zaustavimo in za trenutek obmirujemo nad nekim gradnikom.

MouseLeave Dogodek se zgodi, ko miškin kazalec zapusti rob določenega gradnika, oz. ko miškin kazalec zapusti površino nekega gradnika – odvisno od vrste gradnika.

MouseMove Dogodek se zgodi, ko se kazalnik miške, medtem ko smo nad nekom gradnikom, premakne.

MouseUp Dogodek se zgodi, ko je kazalec miške nad določenim gradnikom in uporabnik spusti miškin gumb.

Page 121: PROŠNJA - uranic.tsckr.siuranic.tsckr.si/VISUAL C#/VISUAL C#.pdf · MICROSOFT VISUAL C# .NET Srečo Uranič . Visual C# . ... Za začetek novega projekta v okolju Visual C# Standard

Visual C# . NET

Algoritmi in programski jeziki – 4.letnik

121

MouseWheel Dogodek se zgodi, ko uporabnik zavrti miškin kolešček, medtem ko je izbran ustrezen gradnik (ima focus). Ta dogodek podpira že .<ET Framework in <I namenjen za

uporabo neposredno preko kode!

Parameter Sender Pri večini gradnikov je parameter Sender prvi parameter vseh odzivnih metod gradnika. S pomočjo tega parametra lahko npr. preko imena gradnika testiramo, nad katerim gradnikom se je zgodil določen dogodek. Parameter Sender je nepogrešljiv npr. tedaj, ko je na obrazcu veliko število gumbov in nam ni potrebno pisati odzivnih metod za vsak gumb posebej: enemu od njih napišemo odzivno metodo, ostalim pa v Solution Explorerju ta isti dogodek le priredimo. Znotraj metode pa potem npr. s pomočjo if stavka preverjamo ime gradnikov in zapišemo ustrezno kodo. Vaja: Kreirajmo nov projekt DogodkiMiške. Nanj postavimo gradnik TabControl in na posameznih straneh tega gradnika bomo prikazali vse dogodke, ki jih proži miška. Na prvem zavihku je prikazan dogodek MouseHoover.

TabControl TextBox

Če z miško za nekaj trenutkov obmirujemo nad vnosnim poljem, se zgodi dogodek MouseHoover, ki mu z naslednjo kodo spremenimo barvo ozadja: private void textBox1_MouseHover(object sender, EventArgs e) { textBox1.BackColor = Color.DarkOrange; }

Drugi zavihek prikazuje dogodke Click, MouseClick, DoubleClick in MouseDoubleClick. V Visual C# nimajo vsi gradniki privzetih obeh dogodkov (Click – enojni klik in DoubleClick – dvojni klik) miške. Tudi gumbi tipa Button poznajo samo dogodek enojni klik, ki se izvede tudi v primeru, če uporabnik na nek gumb

dvoklikne. To dokazuje tudi primer na drugem zavihku, kjer je uporabljen tudi parameter Sender (pove nam, kateri gradnik je klical metodo). TextBox Gumbi tipa Button

Page 122: PROŠNJA - uranic.tsckr.siuranic.tsckr.si/VISUAL C#/VISUAL C#.pdf · MICROSOFT VISUAL C# .NET Srečo Uranič . Visual C# . ... Za začetek novega projekta v okolju Visual C# Standard

Visual C# . NET

Algoritmi in programski jeziki – 4.letnik

122

• Ob kliku na katerega koli od treh barvnih gumbov se zgodi dogodek Click gumba. Da pa ne treba pisali treh različnih metod, napišemo le eno, v kateri s parametrom Sender ugotavljamo, kateri gumb je klical metodo. Vsebina metode je naslednja:

private void bRumeni_Click(object sender, EventArgs e) { //gumbi imajo imena bRumeni, bModri in bRdeci if (sender==bRumeni) //preverimo ime gradnika, ki je klical metodo MessageBox.Show("Kliknil si na rumeni gumb!!!"); else if (sender == bModri) MessageBox.Show("Kliknil si na modri gumb!!!"); else if (sender == bRdeci) MessageBox.Show("Kliknil si na rdeči gumb!!!"); }

• Ob kliku na rumeni gumb se zgodi najprej dogodek Click, nato pa smo gumbu priredili še dogodek

MouseClick, ki smo mu priredili le izpis sporočilnega okna!

private void bRumeni_MouseClick(object sender, MouseEventArgs e) { MessageBox.Show("Rumenemu gumbu smo priredili še dogodek MouseClick. Ta se zgodi za

dogodkom Click!"); }

• Prikazana sta tudi dogodka MouseClick in MouseDoubleClick. Prvi se zgodi, ko uporabnik klikne na

gradnik s tekstom – TextBox. Priredili smo mu kodo, ki poskrbi za spremembo barve ozadja. Takoj za njim se zgodi še dogodek MouseDoubleClick, ki smo mu priredili kodo, ki v oknu izpiše obvestilo o izvršenem dogodku, nato pa še komentar o natančnem položaju miškinega kazalca v trenutku dvoklika. V kodi je uporabljena metoda Environment.NewLine, ki poskrbi za prelom vrstice teksta v sporočilnem oknu!

private void bRumeni_MouseClick(object sender, MouseEventArgs e) { MessageBox.Show("Rumenemu gumbu smo priredili še dogodek MouseClick. Ta se zgodi za

dogodkom Click!"); } private void textBox3_MouseDoubleClick(object sender, MouseEventArgs e) { textBox3.Clear(); //brisanje celotne vsebine textBox3.Text = "Izvedel se je dogodek

MouseDoubleClick."+Environment.NewLine+"Pozicija miške ob dvokliku: Koordinata X: "+Convert.ToString(e.X)+" Koordinata Y: "+Convert.ToString(e.Y);

}

� NASVET: Prelom vrstice v gradnikih, ki vsebujejo tekst lahko dosežemo na dva načina: s pomočjo stavka Environment.&ewLine ali pa s pomočjo znakovne konstante "\r\n".

Tretji zavihek prikazuje dogodka MouseDown in MouseUp.

TextBox Labela

Vnosnemu polju je prirejen dogodek MouseDown. Če se z miško pomaknemo nad vnosno polje in pritisnemo desni gumb, se vsebina TextBox - a v celoti označi, obenem pa se prikaže še PopUp meni.

Page 123: PROŠNJA - uranic.tsckr.siuranic.tsckr.si/VISUAL C#/VISUAL C#.pdf · MICROSOFT VISUAL C# .NET Srečo Uranič . Visual C# . ... Za začetek novega projekta v okolju Visual C# Standard

Visual C# . NET

Algoritmi in programski jeziki – 4.letnik

123

Dogodek MouseUp je prikazan nad labelo, ki je pod vnosnim poljem. Priredili smo mu kodo, ki spremeni in poveča tekst labele. private void textBox2_MouseDown(object sender, MouseEventArgs e) { if (e.Button == MouseButtons.Right) //preverimo, če je bil pritisnjen desni gumb { textBox1.Select(0, textBox1.Text.Length); //označimo tekst } } private void label3_MouseUp(object sender, MouseEventArgs e) { label3.Font = new Font(FontFamily.GenericSansSerif, 80.0F, FontStyle.Bold); //novi font label3.Text = "Hvala!"; //nov napis na labeli }

Četrti zavihek je namenjen prikazu dogodkov MouseEnter in MouseLeave.

Gradnik PictureBox

Gradnik PictureBox je namenjen prikazu slike. Najprej postavimo gradnik na obrazec, nato pa kliknemo na

tripičje pri lastnosti Image. Odpre se sporočilno okno, namenjeno iskanju slike, ki jo želimo postaviti v ta gradnik. V zgornjem levem kotu tega okna sta na voljo dve opciji, kje naj se slika, ki jo bomo izbrali shrani. Opcija Local resource pomeni, da se bo slika shranila le lokalno (le za ta gradnik), druga opcija Project resource file pa, da se bo slika shranila tudi v t.i. datoteko resoursov, kjer bo na voljo tudi za kdaj kasneje. S klikom na gumb Import se odpre okno (nekakšen mini explorer), ki nam pomaga pri iskanju slike, ki jo želimo postaviti v gradnik. Izbiro slike na koncu potrdimo s klikom na gumb OK.

� NASVET: V zgornjem desnem kotu PictureBox-a je majhna puščica. S klikom nanjo se odpre menu PictureBox Tasks. Če opcijo Size Mode nastavimo na AutoSize, se okvirček prilagodi vstavljeni sliki, tako da se nam ni potrebno ukvarjati z nastavljanjem velikosti okvirčka.

V primeru, da smo sliko postavili v datoteko resoursov, pa smo to storili pomotoma oz. jo želimo od tam

odstraniti, to na tem mestu ni mogoče – odstranimo jo tako, da v Solution Explorerju poiščemo datoteko Resources.resx, ki je v mapi Properties. Dvokliknimo na to datoteko in prikaže se nam njena vsebina. Sedaj lahko odstranimo vse nezaželene vnose. Peti, zadnji zavihek pa je namenjen dogodku MouseMove. Na panelu je ikona kocke, ki se ob aktiviranju panela in premiku miške prilepi za kazalec miške in se premika

hkrati z njo.

Page 124: PROŠNJA - uranic.tsckr.siuranic.tsckr.si/VISUAL C#/VISUAL C#.pdf · MICROSOFT VISUAL C# .NET Srečo Uranič . Visual C# . ... Za začetek novega projekta v okolju Visual C# Standard

Visual C# . NET

Algoritmi in programski jeziki – 4.letnik

124

Potovanje kocke hkrati z miškinim kazalcem smo dosegli s pomočjo dogodka MouseMove in njenim parametrom tipa MouseEventArgs, ki vsebuje tudi lastnosti X in Y – to sta koordinati miške. Operator new poznamo iz osnov jezika C# oz. C++ – dinamično zaseganje pomnilnika za nov objekt. private void panel1_MouseMove(object sender, MouseEventArgs e) { pictureBox2.Location = new Point(e.X, e.Y); }

Point pa v C# predstavlja že narejen razred, ki simbolizira točko z njenima koordinatama v dvodimenzionalnem koordinatnem sistemu. Objektu smo posredovali dve koordinati, ki predstavljata trenutni položaj miškinega kazalca. Ta nova točka tako postane nova lokacija ikone kocke v gradniku PictureBox. Vaja: Naredimo še projekt, v katerem bomo preizkusili dogodek MouseWheel, obenem pa še ponovimo, kako v C# generiramo naključna števila. Obrazec naj zgleda takole: Gradnik ListBoX (lastnost Selection Mode je

nastavljena na MultiSimple TextBox (ReadOnly =true, ScrollBars = Vertical))

Obrazcu priredimo dogodek Load: to je metoda, ki se zgodi prva ko se obrazec odpre. V telo metode zapišemo stavka, ki ob zagonu projekta začasno skrijeta gumba z napisoma &ajvečje izbrano število in &ajvečje generirano število. private void Form1_Load(object sender, EventArgs e) { button2.Visible = false; button3.Visible = false; }

Gumbu Generiraj priredimo dogodek Click, ki zgenerira 1000 naključnih števil med 0 in 100000. Števila naj se zapišejo v gradnik ListBox. Nato se prikaže še gumb &ajvečje generirano število.

Page 125: PROŠNJA - uranic.tsckr.siuranic.tsckr.si/VISUAL C#/VISUAL C#.pdf · MICROSOFT VISUAL C# .NET Srečo Uranič . Visual C# . ... Za začetek novega projekta v okolju Visual C# Standard

Visual C# . NET

Algoritmi in programski jeziki – 4.letnik

125

private void button1_Click(object sender, EventArgs e) { //Generiranje naključnih števil //najprej ustvarimo nov objekt (Nakljucno_Celo) razreda Random. Random Nakljucno_Celo = new Random(); //Ustavimo obnavljanje prikaza (painting) v krmilniku ListBox lBNakljucna.BeginUpdate(); //Generiramo 100 naključnih celih števil, večjih od 0 in manjših od 100000 for (int i = 0; i < 100; i++) lBNakljucna.Items.Add(Nakljucno_Celo.Next(100000).ToString()); //Sprostimo obnavljanje prikaza (painting) v krmilniku ListBox lBNakljucna.EndUpdate(); button3.Visible = true; //Prikaz gumba z napisom največje generirano število }

Gumbu &ajvečje generirano število priredimo dogodek Click in nato v telo pripadajoče metode napišimo kodo, ki poišče in v sporočilnem oknu izpiše največje generirano število. private void button3_Click(object sender, EventArgs e) { int najvecje = 0; for (int i = 0; i < lBNakljucna.Items.Count; i++) { if (Convert.ToInt32(lBNakljucna.Items[i]) > najvecje) najvecje = Convert.ToInt32(lBNakljucna.Items[i]); } MessageBox.Show("Največje generirano število je " + Convert.ToString(najvecje)); }

Ker smo gradniki ListBox lastnost Selection Mode nastavili na MultiSimple, lahko s pomočjo klikov miške označimo poljubno množico števil v gradniku. Že ob prvem kliku se na obrazcu prikaže še gumb z napisom &ajvečje izbrano število. private void lBNakljucna_Click(object sender, EventArgs e) { button2.Visible = true; //Prikažemo gumb } Ob kliku na gumb z napisom &ajvečje izbrano število se izvede dogodek Click tega gumba. Kodo v metodi zapišemo tako, da med izbranimi števili poiščemo največje in le-to izpišemo v sporočilnem oknu! private void button2_Click(object sender, EventArgs e) { int najvecje = 0; for (int i = 0; i < lBNakljucna.SelectedItems.Count; i++) { if (Convert.ToInt32(lBNakljucna.SelectedItems[i]) > najvecje) najvecje = Convert.ToInt32(lBNakljucna.SelectedItems[i]); } MessageBox.Show("Največje izbrano število je " + Convert.ToString(najvecje)); }

Na obrazcu je še gumb za napisom Briši. Temu gumbu priredimo dogodek Click, ki pobriše vsebino gradnika ListBox in obenem skrije gumba za napisoma &ajvečje izbrano število in &ajvečje generirano število. private void button5_Click(object sender, EventArgs e) { lBNakljucna.Items.Clear(); //Pobrišemo vsebino gradnika ListBox button2.Visible = false; //Skrijemo gumb button3.Visible = false; //Skrijemo gumb }

Page 126: PROŠNJA - uranic.tsckr.siuranic.tsckr.si/VISUAL C#/VISUAL C#.pdf · MICROSOFT VISUAL C# .NET Srečo Uranič . Visual C# . ... Za začetek novega projekta v okolju Visual C# Standard

Visual C# . NET

Algoritmi in programski jeziki – 4.letnik

126

Po obeh gradnikih (ListBox in TextBox) se lahko po vertikali premikamo tudi s pomočjo miškinega koleščka – ob tem se izvaja privzeti dogodek MouseWheel.

Dogodki ki jih sproži ura (gradnik Timer) in metode za delo s časom Gradnik Timer (Ura) je namenjen produciranju dogodkov v časovnih intervalih ki mu jih določimo bodisi v

Solution Explorerju, bodisi programsko. V oknu Toolbox se nahaja v skupini Components. Ta gradnik je eden izmed številnih nevizuelnih gradnikov: ko ga namreč želimo postaviti na obrazec, se avtomatično namesti v posebno polje pod samim obrazcem, ker gre pač za gradnik, ki na samem obrazcu ne bo viden, bo pa opravljal neko drugo funkcijo. Gradnik ima za razliko od ostalih gradnikov le nekaj lastnosti, od katerih sta poleg imena pomembni le dve:

• Lastnost Enabled: ta je privzeto postavljena na False. Če hočemo torej gradnik postaviti v operativno funkcijo, mu moramo lastnost Enabled postaviti na True;

• Lastnost Interval: s to lastnostjo nastavimo časovni interval v milisekundah, ki bo pretekel med posameznima dogodkoma, ki jih bo sprožil ta gradnik. Vrednost 1000 torej pomeni na primer vsako sekundo, vrednost 100 eno desetinko sekunde, ..

Gradniku Timer lahko v Solution Explorerju priredimo en sam dogodek. To je seveda dogodek Tick, ki se bo zgodil vsakokrat, ko se izteče čas, podan z lastnostjo Interval.

Delo z datumi in časom

Za delo z datumi in časom je v C# namenjena struktura DateTime, ki vsebuje množico lastnosti in metod. Nov objekt tipa DateTime lahko ustvarimo na tri načina:

• Z operatorjem new.

Splošna oblika:

DateTime datum=new DateTime(leto, mesec, dan [,ure,minute,sekunde [,miliskunde]]);

Podatki o letu, mesecu in dnevu so obvezni, podatki o urah, minutah, sekundah in milisekundah pa so le opcijski in torej niso obvezni. Če podatkov o času ne navedemo, bo nastavljen privzeti čas, to pa je 12.00 AM (AM = dopoldan). Primer: DateTime zacetniDatum = new DateTime(07, 08, 20); //20/8/0007 12:00 AM DateTime zacetniDatumInCas = new DateTime(2007,08,20,14,15,0); //20/08/2007 14:15:00

• S statično metodo Parse

Splošna oblika DateTime datum=DateTime.Parse(string);

Pri uporabi metode Parse mora biti seveda string v oklepaju v veljavnem formatu. Veljavni formati za nastavitev datuma so naslednji:

Page 127: PROŠNJA - uranic.tsckr.siuranic.tsckr.si/VISUAL C#/VISUAL C#.pdf · MICROSOFT VISUAL C# .NET Srečo Uranič . Visual C# . ... Za začetek novega projekta v okolju Visual C# Standard

Visual C# . NET

Algoritmi in programski jeziki – 4.letnik

127

20/08/2007 20/08/07 20-08-2007 2-7-2007 2007-01-01 Jan 20 2007 Marec 15, 2007 Veljavni formati za nastavitev časa: 2:15 PM AM = dopoldan, PM = popoldan 14:15 02:15:30 Primer:

DateTime zacetniDatum = DateTime.Parse("20/08/07"); //20/8/0007 12:00 AM DateTime zacetniDatumInCas = DateTime.Parse("Aug 20,2007,2:15 PM"); // 20/08/2007 14:15:00

• Z deklaracijo in takojšnjo inicializacijo objekta tipa DateTime:

DateTime Datum = DateTime.Now; //Trenutni datum in čas DateTime Datum1 = DateTime.Today; //Trenutni datum DateTime Datum2 = DateTime.UtcNow;//Trenutni datum in čas, ki je nastavljen na tem //računalniku

Tabela lastnosti in metod za delo z datumi in časom

Lastnost R a z l a g a

Date Vrne vrednost DateTime (trenutni datum), čas pa ima privzeto vrednost (12:00:00 AM).

Month Vrne celo število, ki predstavlja mesec trenutne vrednosti DateTime.

Day Vrne celo število, ki predstavlja dan trenutne vrednosti DateTime.

Year Vrne celo število, ki predstavlja leto trenutne vrednosti DateTime.

Hour Vrne celo število, ki predstavlja uro trenutne vrednosti DateTime.

Minute Vrne celo število, ki predstavlja minute trenutne vrednosti DateTime.

Second Vrne celo število, ki predstavlja sekunda trenutne vrednosti DateTime.

TimeOfDay Vrne vrednost tipa TimeSpan, ki predstavlja nek časovni interval.

DayOfWeek Vrne člana naštevnega tipa DayOfWeek, ki predstavlja dan v tednu trenutne vrednosti DateTime.

DayOfYear Vrne celo število, za zaporedno število dneva v tekočem letu glede na trenutno vrednost v DateTime.

Metoda R a z l a g a

DaysInMonth(leto, mesec)

Vrne število dni v navedenem letu in mesecu.

IsLeapYear(leto) Vrne logično vrednost true, če je leto prestopno leto.

Page 128: PROŠNJA - uranic.tsckr.siuranic.tsckr.si/VISUAL C#/VISUAL C#.pdf · MICROSOFT VISUAL C# .NET Srečo Uranič . Visual C# . ... Za začetek novega projekta v okolju Visual C# Standard

Visual C# . NET

Algoritmi in programski jeziki – 4.letnik

128

&ekaj primerov uporabe: DateTime datum = DateTime.Now; // 20.08.2007, 10:08:50 AM int mesec = datum.Month; //8 int ura = datum.Hour; //10 int danVLetu = datum.DayOfYear;//233 int dniVMesecu = DateTime.DaysInMonth(2007, 8);//31 bool prestopnoLeto = DateTime.IsLeapYear(2007);//False //še primer uporabe lastnosti DayOfWeek DayOfWeek danVTednu = datum.DayOfWeek; string sporočilo=""; if (danVTednu==DayOfWeek.Saturday||danVTednu==DayOfWeek.Sunday) sporočilo="Vikend"; else sporočilo="Delovni dan";

Tabela operacij za delo z datumi in časom

Metoda R a z l a g a

AddDays(dni) Dodajanje navedenega števila dni k vrednosti objekta DateTime in vračanje nove vrednosti DateTime.

AddMonths(meseci) Dodajanje navedenega števila mesecev k vrednosti objekta DateTime in vračanje nove vrednosti DateTime.

AddYears(leta) Dodajanje navedenega števila let k vrednosti objekta DateTime in vračanje nove vrednosti DateTime.

AddHours(ure) Dodajanje navedenega števila ur k vrednosti objekta DateTime in vračanje nove vrednosti DateTime.

AddMinutes(minute) Dodajanje navedenega števila minut k vrednosti objekta DateTime in vračanje nove vrednosti DateTime.

AddSeconds(sekunde) Dodajanje navedenega števila sekund k vrednosti objekta DateTime in vračanje nove vrednosti DateTime.

Add(timespan) Dodajanje navedene vrednosti tipa TimeSpan k vrednosti objekta DateTime in vračanje nove vrednosti DateTime.

Subtract(datetime) Odštevanje navedene vrednosti DateTime od vrednosti DateTime in vračanje nove vrednosti tipa TimeSpan.

&ekaj primerov: DateTime datum = DateTime.Now; // 21.08.2007, 10:50:50 AM DateTime novidatum = datum.AddMonths(2); //21.10.2007 DateTime novidatum1 = datum.AddDays(60); //21.10.2007 DateTime novidatum2 = datum.AddMinutes(30); //21.08.2007, 11:20:50 AM DateTime novidatum3 = datum.AddHours(12); //21.08.2007, 22:20:50 AM DateTime danasnjidatum = DateTime.Today; //21.08.2007 DateTime datumzapadlosti = DateTime.Parse("06/09/2007"); //06.09.2007 //spremenljivka tipa TimeSpan hrani pretečeni (izmerjeni) čas TimeSpan caszapadlosti = datumzapadlosti.Subtract(danasnjidatum); //16:00:00:00 int dnizapadlosti = caszapadlosti.Days; //16 //Za odštevanje dveh datumov lahko uporabimo tudi operator - (minus) DateTime danes = DateTime.Today; DateTime obletnica = DateTime.Parse("25/10/2007"); TimeSpan pretecenicas = obletnica - danes; //65.00:00:00

Page 129: PROŠNJA - uranic.tsckr.siuranic.tsckr.si/VISUAL C#/VISUAL C#.pdf · MICROSOFT VISUAL C# .NET Srečo Uranič . Visual C# . ... Za začetek novega projekta v okolju Visual C# Standard

Visual C# . NET

Algoritmi in programski jeziki – 4.letnik

129

Pri delu z objekti tipa DateTime lahko uporabljamo tudi operatorje +, -, = =, !=, <, <=, > in >=. //datume lahko med seboj tudi primerjamo bool pretecen = false; if (danes > obletnica) pretecen = true; Vaja: Naslednji primer prikazuje uporabo nekaterih matematičnih operatorjev, uporabo if stavka, projekt pa vsebuje tudi dve lastni metodi:

Gradnika DateTimePicker ( imeni sta prviDTP in drugiDTP) TextBox, ime gradnika je info Gumbu z napisom Primerjaj je prirejena metoda bPrimerjaj_Click, ki se odzove na klik gumba.

private void bPrimerjaj_Click(object sender, EventArgs e) { int diff = primerjajDatuma(prviDTP.Value, drugiDTP.Value); info.Text = ""; prikazi("prvi == drugi", diff == 0); prikazi("prvi != drugi", diff != 0); prikazi("prvi < drugi", diff < 0); prikazi("prvi <= drugi", diff <= 0); prikazi("prvi > drugi", diff > 0); prikazi("prvi >= drugi", diff >= 0); }

Ob kliku na gumb Primerjaj kličemo dve lastni metodi, ki sta napisani v nadaljevanju – to sta metodi primerjajDatuma in prikazi. private void prikazi(string izraz, bool rezultat)//lastna funkcija { info.Text += izraz; info.Text += " : " + rezultat.ToString(); info.Text += "\r\n"; //prelom vrstice } private int primerjajDatuma(DateTime prvi, DateTime drugi) //lastna funkcija { //V C# uporabljamo za predstavitev datuma in časa strukturo DateTime //funkcija primerjajDatuma vrne 0 če sta datuma enaka, -1 //če je prvi datum manjši in +1 če je prvi datum večji int rezultat; if (prvi.Year < drugi.Year) rezultat = -1; else if (prvi.Year > drugi.Year) rezultat = +1; else if (prvi.Month < drugi.Month) rezultat = -1; else if (prvi.Month > drugi.Month) rezultat = +1; else if (prvi.Day < drugi.Day) rezultat = -1; else if (prvi.Day > drugi.Day) rezultat = +1; else rezultat = 0; return rezultat; }

Page 130: PROŠNJA - uranic.tsckr.siuranic.tsckr.si/VISUAL C#/VISUAL C#.pdf · MICROSOFT VISUAL C# .NET Srečo Uranič . Visual C# . ... Za začetek novega projekta v okolju Visual C# Standard

Visual C# . NET

Algoritmi in programski jeziki – 4.letnik

130

Vaja: Še nekaj primerov uporabe razreda DateTime, razreda TimeSpan in gradnika DateTimePicker: button1 button2 private void button1_Click(object sender, EventArgs e) { try { DateTime datum = Convert.ToDateTime(textBox1.Text); MessageBox.Show("Vnešeni datum: \nDolgi format: " + datum.ToLongDateString() +"\nKratki format: "+datum.ToShortDateString() +"\nDan v tednu = "+datum.DayOfWeek +"\nZaporedni dan v letu: "+datum.DayOfYear); } catch { MessageBox.Show("Napaka!"); } } //dogodek CloseUp se zgodi, ko uporabnik izbere datum v gradniku DateTimePicker private void dateTimePicker1_CloseUp(object sender, EventArgs e) { MessageBox.Show("Izbrani datum:\nDolgi format: " + dateTimePicker1.Value.ToLongDateString() + "\nKratki format: " + dateTimePicker1.Value.ToShortDateString()); } private void button2_Click(object sender, EventArgs e) { DateTime izbraniDatum = dateTimePicker1.Value; TimeSpan obdobje = DateTime.Now - izbraniDatum; MessageBox.Show("Pretečeni čas od izbranega datuma do danes: "+ obdobje.ToString()); /*ali MessageBox.Show("Pretečeni čas od izbranega datuma do danes: " + (DateTime.Now- dateTimePicker1.Value).ToString());*/ }

Vaja: Zgradimo projekt, v katerem bomo predstavili preprosto štoparico. V projekt moramo vključiti nevizuelni gradnik Timer. Izgled obrazca naj bo takle:

Klik na gumb Začetek predstavlja začetek merjenja časa, ob kliku na gumb Vmesni čas se pretečeni čas zapiše v gradnik ListBox, obenem pa se prikazovanje časa v gradniku TextBox zaustavi za toliko časa, dokler ponovno ne pritisnemo tega gumba. Ob kliku na gumb Konec se merjenja časa zaustavi.

Page 131: PROŠNJA - uranic.tsckr.siuranic.tsckr.si/VISUAL C#/VISUAL C#.pdf · MICROSOFT VISUAL C# .NET Srečo Uranič . Visual C# . ... Za začetek novega projekta v okolju Visual C# Standard

Visual C# . NET

Algoritmi in programski jeziki – 4.letnik

131

public Form1() { InitializeComponent(); } //globalne spremenljivke DateTime Start; DateTime End; //spremenljivka tipa TimeSpan omogoča hranjenje izmerjenega časa TimeSpan izmerjeniCas; //izmerjeni časovni interval bool casTece; //spremenljivka določa, ali v gradniku texBox1 čas teče ali pa ne int stevec = 1; //števec vmesnih časov //gradniku timer1 priredimo dogodek Tick – prikazovanje pretečenega časa v gradniku textBox1 private void timer1_Tick(object sender, EventArgs e) { if (casTece) textBox1.Text=Convert.ToString(DateTime.Now-Start); } //klik na gumb Začetek private void bZacetek_Click(object sender, EventArgs e) { Start = DateTime.Now; //začetek merjenja časa casTece = true; //v gradniku textBox1 se prikazuje tekoči izmerjeni čas timer1.Enabled = true; //poženemo timer bVmesni.Visible = true; //prikažemo gumb za vmesne rezultate bKonec.Visible = true; //prikažemo gumb za končni rezultat bZacetek.Visible = false;//skrijemo gumb za začetek listBox1.Items.Clear(); //na začetku vedno pobrišemo prejšnje rezultate } //klik na gumb Konec private void bKonec_Click(object sender, EventArgs e) { End = DateTime.Now; //shranimo trenutni čas timer1.Enabled = false; //zaustavimo timer (merjenje časa) TimeSpan izmerjeniCas = End - Start; //pretečeni čas od začetma merjenja do tega trenutka listBox1.Items.Add("Končni čas: " + textBox1.Text); //Končni čas zapišemo tudi v ListBox1 bVmesni.Visible = false; //skrijemo gumb za vmesne rezultate bKonec.Visible = false; //skrijemo gumb za končni rezultat bZacetek.Visible = true; //prikažemo gumb za začetek } //klik na gumb Vmesni čas private void bVmesni_Click(object sender, EventArgs e) { if(casTece==true) { casTece = false; //zaustavimo prikazovanje časa v gradniku textBox1 bVmesni.Text = "Nadaljuj"; listBox1.Items.Add(stevec + ".) " + textBox1.Text);//v listBox1 zapišemo vmesni rez. stevec++; //števec vmesnih časov } else { casTece = true;//nadaljujemo s prikazom časa v gradniku textBox1 bVmesni.Text = "Vmesni čas"; } } //začetne nastavitve private void Form1_Load(object sender, EventArgs e) { bVmesni.Visible = false; bKonec.Visible = false; }

Vaja:

Page 132: PROŠNJA - uranic.tsckr.siuranic.tsckr.si/VISUAL C#/VISUAL C#.pdf · MICROSOFT VISUAL C# .NET Srečo Uranič . Visual C# . ... Za začetek novega projekta v okolju Visual C# Standard

Visual C# . NET

Algoritmi in programski jeziki – 4.letnik

132

Zgradimo projekt, v katerem bomo predstavili dogodek, ki ga proži Timer (Ura), obenem pa spoznali še gradnik StatusStrip in nekaj najpomembnejših metod za delo s časom. Kreirajmo nov projekt Ura in nanj postavimo naslednje gradnike: GroupBox TextBox

GroupBox StatusStrip

Gradnik StatusStrip (namenjen je oblikovanju statusne vrstice na dnu okna) in gradnik Timer sta t.i. nevizuelna gradnika, zato se avtomatično namestita v posebno polje pod obrazcem. V statusni vrstici bomo s pomočjo programske kode prikazali tekoči čas, ki se bo s pomočjo gradnika Timer seveda spreminjal vsako sekundo. Gradniku Timer nastavimo interval na 1000, lastnost Enabled pa postavimo na True. V gradniku Status Strip naredimo dva predalčka: to storimo tako, da odpremo spustni seznam znotraj tega gradnik in izberemo opcijo StatusLabel. Tako dobimo nov predalček, ki ga nato označimo in mu lastnost Text

nastavimo npr. Ura. Priredimo mu še lastnost BorderSides - Right (da dobimo rob predalčka). Širino predalčka določimo poljubno.

Posameznim gradnikom in gumbom sedaj priredimo naslednje dogodke:

• Obrazcu priredimo dogodek Load. Dogodek Load je dogodek, ki se zgodi prvi, ko se obrazec odpira.

private void Form1_Load(object sender, EventArgs e) { //kazalec this seveda kaže na obrazec, Text pa je lastnost tega obrazca this.Text = "Dogodek ure in funkcije za delo s časom. <"

+DateTime.Now.ToLongDateString()+" >"; button2.Visible = false; //na začetku skrijemo gumb z napisom Konec }

Metoda &ow objektu tipa DateTime priredi trenutni datum in čas, z metodo ToLongDateString() pa smo datum, ki ga objekt hrani, spremenili v string in ga prikazali v sporočilnem oknu.

• Gradniku Timer priredimo dogodek, ki vsako sekundo osveževal prikaz tekočega časa v statusni vrstici

private void timer1_Tick_1(object sender, EventArgs e) { //prikaz ure v prvem predalčku (indeks =0) statusne vrstice statusStrip1.Items[0].Text = DateTime.Now.ToLongTimeString();

Page 133: PROŠNJA - uranic.tsckr.siuranic.tsckr.si/VISUAL C#/VISUAL C#.pdf · MICROSOFT VISUAL C# .NET Srečo Uranič . Visual C# . ... Za začetek novega projekta v okolju Visual C# Standard

Visual C# . NET

Algoritmi in programski jeziki – 4.letnik

133

}

Uporabili smo metodo ToLongTimeString(), ki trenutni čas objekta tipa DateTime spremeni v string in ga prikaže v tekstovni obliki v predalčku naše statusne vrstice.

• V gradniku GroupBox z napisom Štoparica prikažimo, kako lahko merimo nek časovni interval (npr.

med dvema klikoma gumbov) in kako pretečeni čas prikažemo v obliki ur, minut, sekund in milisekund. Za realizacijo merjenja pretečenega časa potrebujemo tudi dve spremenljivki, ki ju lahko deklariramo kjerkoli znotraj razreda Form1, a izven vseh metod, saj gre za globalne spremenljivke obrazca. Obenem deklariramo še štiri spremenljivke tipa long, ki jih bomo potrebovali kasneje in napišimo dogodek obrazca Shown, ki poskrbi, da je potem, ko se obrazec prikaže na zaslonu aktiven gradnik (ima focus) gradnik button1 (gradnik z napisom Začetek)

DateTime zacetek, konec; long ure, minute, sekunde,milisekunde; private void Form1_Shown(object sender, EventArgs e) { button1.Focus(); //ob prikazu obrazca bo aktiven gradnik button1 }

• Štoparico poženemo s klikom na gumb Začetek, ki mu priredimo naslednji dogodek Click:

private void button1_Click(object sender, EventArgs e) { //trenutni čas spremenimo v string in ga zapišemo v gradnik textBox2 textBox2.Text=Convert.ToString(DateTime.Now.ToLongTimeString()); // trenutni čas shranimo tudi v globalno spremenljivko zacetek zacetek = DateTime.Now; button2.Visible = true; //prikaz gumba z napisom Konec }

Spremenljivka zacetek torej hrani podatek o tem, kdaj smo sprožili merjenje časa. Gumbu Konec sedaj priredimo njegov dogodek Click, ki bo merjenje časa zaustavil in ugotovil ter na primeren način izpisal vse potrebne podatke o izmerjenem času. Čas ob pritisku na gumb Konec bomo shranili v spremenljivko konec. Sedaj moramo seveda izračunati razliko zapisanih časov v spremenljivkah zacetek in konec, ter to razliko na primeren način prikazati, npr. v ustreznem sporočilnem oknu. private void button2_Click(object sender, EventArgs e) { //trenutni čas spremenimo v string in ga zapišemo v gradnik textBox3 textBox3.Text = Convert.ToString(DateTime.Now.ToLongTimeString()); // trenutni čas shranimo tudi v globalno spremenljivko konec konec = DateTime.Now; // izračunamo pretečeni čas v milisekundah – zato da ga bomo lahko pretvorili v //ure,

minute, sekunde in milisekunde long cas = konec.Millisecond - zacetek.Millisecond + (konec.Second -

zacetek.Second) * 1000 +(konec.Minute - zacetek.Minute) * 60000 + (konec.Hour - zacetek.Hour) * 3600000;

//pretečeni čas zapišemo v gradnik textBox4 v obliki hh:mm:ss.mmmmmmm textBox4.Text = Convert.ToString(konec-zacetek); //pretečeni čas v milisekundah pretvorimo v ure, minute, sekunde in milisekunde ure = cas / 3600000; minute = (cas-ure*3600000)/ 60000; sekunde = (cas-minute*60000) / 1000; milisekunde = (cas - sekunde * 1000); //pretečeni čas pretvorjen v dele ure prikažemo uporabniku še v sporočilnem oknu MessageBox.Show("Ure: "+Convert.ToInt32(ure)+"\nMinute:

"+Convert.ToInt32(minute)+"\nSekunde: "+Convert.ToInt32(sekunde)+"\nMilisekunde: "+Convert.ToInt32(milisekunde));

}

• Gumbu DateTime.now: Trenutni datum in čas na tem računalniku priredimo dogodek Click

Page 134: PROŠNJA - uranic.tsckr.siuranic.tsckr.si/VISUAL C#/VISUAL C#.pdf · MICROSOFT VISUAL C# .NET Srečo Uranič . Visual C# . ... Za začetek novega projekta v okolju Visual C# Standard

Visual C# . NET

Algoritmi in programski jeziki – 4.letnik

134

private void button3_Click(object sender, EventArgs e) { //v sporočilnem oknu prikazan v string pretvorjen trenutni čas MessageBox.Show(DateTime.Now.ToString()); }

• Gumbu DateTime.&ow.Year: Trenutna letnica priredimo dogodek Click:

private void button4_Click(object sender, EventArgs e) { //v sporočilnem oknu prikazana v string pretvorjena trenutna letnica MessageBox.Show(DateTime.Now.Year.ToString()); }

• Gumbu DateTime.&ow.Month: Trenutni mesec priredimo dogodek Click:

private void button5_Click(object sender, EventArgs e) { //v sporočilnem oknu prikazana v string pretvorjen trenutni mesec MessageBox.Show(DateTime.Now.Month.ToString()); }

• Gumbu DateTime.&ow.DayOfWeek: Trenutni dan v tednu priredimo dogodek Click:

private void button6_Click(object sender, EventArgs e) { //v sporočilnem oknu prikazan v string pretvorjen trenutni dan v tednu MessageBox.Show(DateTime.Now.DayOfWeek.ToString()); }

• Gumbu DateTime.&ow.DayOfYear: Zaporedni dan v letu priredimo dogodek Click:

private void button7_Click(object sender, EventArgs e) { //v sporočilnem oknu prikazan v string pretvorjen zaporedni dan v letu MessageBox.Show(DateTime.Now.DayOfYear.ToString()); }

Vaja: Naslednji projekt prikazuje uporabo gradnika Timer skupaj z gradnikom ProgressBar in gradnikom PerformanceCounter (gradnik PerformanceCounter omogoča uporabniku pridobivanje vseh informacij o zmogljivostih računalnika). Na obrazec postavimo gradnike PerformanceCounter, GroupBox, Label, Timer in ProgressBar. Gradniku PerformanceCounter nastavimo lastnosti takole:

Gradniku Timer priredimo dogodek Tick i zapišimo kodo (dvoklikemo na gradnik Timer): progressBar1.Value = (int)(performanceCounter1.NextValue()); label1.Text = "Uporaba CPU-ja: " + progressBar1.Value.ToString() + " %";

Poženimo sedaj projekt in dobimo pribljižno takole sliko:

Page 135: PROŠNJA - uranic.tsckr.siuranic.tsckr.si/VISUAL C#/VISUAL C#.pdf · MICROSOFT VISUAL C# .NET Srečo Uranič . Visual C# . ... Za začetek novega projekta v okolju Visual C# Standard

Visual C# . NET

Algoritmi in programski jeziki – 4.letnik

135

Page 136: PROŠNJA - uranic.tsckr.siuranic.tsckr.si/VISUAL C#/VISUAL C#.pdf · MICROSOFT VISUAL C# .NET Srečo Uranič . Visual C# . ... Za začetek novega projekta v okolju Visual C# Standard

Visual C# . NET

Algoritmi in programski jeziki – 4.letnik

136

Projekt z več obrazci – metodi Show in ShowDialog Ob zagonu aplikacije Visual C# zažene le en obrazec – tudi v primeru, da je v projekt vključenih večje število obrazcev. Projekt, ki zna delati z več obrazci torej vsebuje glavni obrazec iz katerega potem odpiramo podobrazce. Podobrazci so lahko dveh vrst:

• Dialogi (Modalni obrazci): to so obrazci, ki jih prikažemo uporabniku in mu ponudimo določene vnose ali pa ga le vprašamo za neko odločitev. Ko uporabnik obrazec zapre, le-ta vrne vrednost na osnovi katere potekajo nadaljnje aktivnosti v aplikaciji. Takim obrazcem pravimo tudi Modalni obrazci, odpremo pa jih z metodo ShowDialog. Metoda ShowDialog je torej funkcija, ki vrne način zapiranja obrazca. Modalno odprtim obrazcem pravimo v splošnem tudi pogovorna okna. Bistvo modalnih obrazcev je tudi v tem, da vsem ostalim oknom znotraj aplikacije preprečujejo, da bi postala aktivna (prejela focus) dokler je modalni obrazec odprt. Modalni obrazci so tako idealni v situacijah, ko ne bi imelo nobenega smisla, da se program nadaljuje oz. da zgodi karkoli, preden uporabnik odgovori na določena vprašanja v takih oknih, ali pa preden ne vnese ustreznih podatkov v vnosna polja.

• &emodalni obrazci: odpremo jih z metodo Show. Taki obrazci dovoljujejo uporabniku nadaljevanje dela v drugem obrazcu, ne da bi prej zaprl obrazec, odprt z metodo Show. Klasičen primer tako odprtega podobrazca je npr. okno Find And Replace v Microsoft Word-u. Uporabnik lahko vnese v okno iskalni string (besedo) in besedo za njegovo zamenjavo, ter nato klikne ustrezen gumb za iskanje in zamenjavo na dnu okna. Seveda pa se lahko uporabnik premisli in nadaljuje z delom v urejevalniku ne da bi mu bilo potrebno okno za iskanje zapreti. Okno za iskanje podatka je izgubilo focus ne da bi se zaprlo – ostane torej odprto. Uporabnik se lahko vrne v to okno kadarkoli.

&emodalno odprti obrazci ne zahtevajo posebne obravnave. Obrazec moramo seveda vnaprej pripraviti (ga kreirati, nanj postaviti ustrezne gradnike,…), nato pa le pravilno zapisati ustrezno kodo za njegovo odpiranje. Modalno odprti obrazci pa zahtevajo poseben način obravnave o tem, kako jih zapremo. Za vsak obrazec lahko preverimo, ali je bil odprt modalno ali nemodalno. To nam omogoča metoda Modal. Metoda vrne True, če je bil obrazec odprt modalno, sicer pa False. if (this.Modal == true) //kazalec this kaže na tekoči obrazec MessageBox.Show("Modalni obrazec"); else MessageBox.Show("NEmodalni obrazec");

� NASVET: V zgornjem desnem kotu ima vsak obrazec tri sistemske gumbe (za minimiziranje obrazca, za maksimiziranje in za zapiranje obrazca. Ti trije gumbi so privzeto prikazani na vsakem obrazcu. Skrijemo oz. odstranimo pa jih tako, da lastnosti obrazca ControlBox postavimo na False.

Vključevanje nemodalnih obrazcev v projekt V že začetem projektu naredimo nov obrazec tako, da v meniju Project izberemo opcijo Add Windows Form…. Prikaže se okno, v katerem Visual C# predlaga ime novega obrazca. Na obrazec nato postavljamo gradnike in pišemo odzivne metode tako kot pri osnovnem obrazcu. Na koncu obrazec shranimo. Visual C# ob zagonu programa odpre samo prvi obrazec. Odpiranje drugih obrazcev je potrebno zagotoviti programsko, s kodo, ne glede nato, ali odpiramo modalen ali nemodalen obrazec.. Če želimo iz osnovnega obrazca nemodalno odpreti katerikoli že pripravljen podobrazec, to storimo s pomočjo odzivne metode Click nekega gumba ali pa npr. s pomočjo odzivne metode nekega menija. Pri tem uporabimo metodo Show takole:

Page 137: PROŠNJA - uranic.tsckr.siuranic.tsckr.si/VISUAL C#/VISUAL C#.pdf · MICROSOFT VISUAL C# .NET Srečo Uranič . Visual C# . ... Za začetek novega projekta v okolju Visual C# Standard

Visual C# . NET

Algoritmi in programski jeziki – 4.letnik

137

Form1 form1 = new Form1();//dinamično kreiranje objekta form izpeljanega iz obrazca Form1 form1.Show();//nemodalno odpiranje obrazca Form1

Nemodalni obrazec lahko zapremo na dva načina: klasičen način je s pomočjo sistemskega gumba v zgornjem desnem delu okna, drugi način pa je z metodo Close. Stavek zapišemo npr. v telo odzivne metode Click nekega gumba, ki ga postavimo na ta nemodalni obrazec. private void button1_Click(object sender, EventArgs e) { this.Close();//zapiranje nemodalno odprtega obrazca. Lahko napišemo tudi samo Close(); }

Ko obrazec zapremo se ne uniči avtomatično, ampak še kar zaseda prostor v pomnilniku (seveda le dokler se ne konča blok, v katerem je bil odprt oz. kreiran z operatorjem new ). Programsko pa lahko obrazec uničimo in sprostimo pomnilnik z metodo Dispose! form1.Dispose(); //uničimo obrazec in sprostimo pomnilnik

V kolikor obrazca ne želimo zapreti in uničiti, ampak le začasno skriti, lahko uporabimo metodo Hide. form1.Hide(); //obrazec začasno skrijemo

Če želimo kasneje obrazec zopet prikazati, ga ni potrebno na novo kreirati ( stavek Form1 form1 = new Form1(); ni potreben). Dovolj je le klic metode Show za ponoven prikaz obrazca.

Vključevanje modalnih obrazcev v projekt Če želimo iz kateregakoli obrazca modalno odpreti nek že pripravljen podobrazec, to storimo s pomočjo odzivne metode Click nekega gumba ali pa npr. s pomočjo odzivne metode nekega menija. Pri tem uporabimo metodo ShowDialog. Modalno odprti obrazci pri svojem zapiranju vračajo podatek o tem, kako smo jih zaprli. Zapremo jih lahko na dva načina: klasičen način je s pomočjo sistemskega gumba v zgornjem desnem delu okna ( v tem primeru obrazec vrne enako vrednost, kot če bi kliknili na modalni gumb Cancel), drugi način pa je s pomočjo modalnega gumba. Modalni gumb je gumb, ki ima nastavljeno lastnost DialogResult. Pri lastnosti Dialog Result imamo na voljo naslednje opcije:

• &one – privzeta vrednost gumba, okno se ob kliku na tak gumb ne zapre samodejno; • OK - okno se zapre in vrne vrednost Dialogresult.OK; • Cancel - okno se zapre in vrne vrednost Dialogresult.Cancel; • Abort - okno se zapre in vrne vrednost Dialogresult.Abort; • Retry - okno se zapre in vrne vrednost Dialogresult.Retry; • Ignore - okno se zapre in vrne vrednost Dialogresult.Ignore; • Yes - okno se zapre in vrne vrednost Dialogresult.Yes; • &o - okno se zapre in vrne vrednost Dialogresult.&o;

Še primer modalnega odpiranja obrazca na katerem sta npr. dva modalna gumba (OK in Prekliči): Form1 form1 = new Form1();//dinamično kreiranje novega objekta – obrazca //Obrazec odpremo z metodo ShowDialog, ko pa se bo zaprl, bo vrnil neko vrednost //ki jo lahko testiramo kot pogoj v if stavku if (form1.ShowDialog()== DialogResult.OK) //preverimo, kako je bil obrazec zaprt MessageBox.Show("Gumb OK");//obrazec zaprt s klikom na modalni gumb OK //else veja:obrazec zaprt s klikom na modalni gumb cancel oz. modalni gumb None, ali pa s //klikom na sistemski gumb za zapiranje okna else MessageBox.Show("Gumb Prekliči");

Stavek if (form1.ShowDialog()== DialogResult.OK)... moramo razumeti takole: objekt form1 izpeljan iz obrazca Form1 smo odprli modalno z metodo ShowDialog. S tem obrazcem sedaj nekaj

Page 138: PROŠNJA - uranic.tsckr.siuranic.tsckr.si/VISUAL C#/VISUAL C#.pdf · MICROSOFT VISUAL C# .NET Srečo Uranič . Visual C# . ... Za začetek novega projekta v okolju Visual C# Standard

Visual C# . NET

Algoritmi in programski jeziki – 4.letnik

138

počnemo in ko ga bomo zaprli, se bo izvedlo preverjanje pogoja tega if stavka. Če bo metoda ShowDialog vrnila vrednost DialogResult.OK se zgodi prva veja if stavka, sicer pa else veja. V primeru, da je na modalno odprtem obrazcu več modalnih gumbov, moramo preverjanje zapiranja obrazca izvesti takole (v spodnjem primeru naj bi bili na modalno odprtem obrazcu modalni gumbi OK, Retry in Cancel): Form2 form2 = new Form2();//dinamično kreiranje novega objekta – obrazca //obrazec odpremo z metodo ShowDialog; vrednost ki jo bo obrazec vrnil, ko ga bo uporabnik //zaprl se bo shranila v spremenljivko Gumb, ki je tipa DialogResult DialogResult Gumb=form2.ShowDialog(); //preverjamo, kateri gumb je uporabnik kliknil ko je zaprl obrazec if (Gumb == DialogResult.OK) MessageBox.Show("Gumb OK"); else if (Gumb == DialogResult.Retry) MessageBox.Show("Gumb Ponovi"); else MessageBox.Show("Gumb Prekliči"); form2.Dispose(); //uničimo obrazec

Vaja: Opisane metode za odpiranje obrazcev bomo sedaj prikazali v naslednjem projektu. Ime projekta naj bo VečokenskaAplikacija, osnovni obrazec pa naj zgleda takole:

TextBox (Multiline=True) Button Obrazcu smo lastnost ControlBox postavili na False: s tem skrijemo vse tri sistemske gumbe v desnem zgornjem kotu obrazca.

Lastnost obrazca FormBorderStyle smo nastavili na FixedToolWindow – uporabnik obrazca ne more pomanjšati ali povečati! Trije gumbi na obrazcu so namenjeni odpiranju podobrazcev. Zgornja dva bomo uporabili za odpiranje dveh modalnih obrazcev z metodo ShowDialog, tretjega pa za odpiranje obrazca z metodo Show. Najprej zgradimo ostale tri obrazce (Form2, Form3 in Form4). Vsakega posebej izberemo v meniju opcijo Project in nato Add Windows Form… Izgled prvega obrazca (Form2):

TextBox Vsi gumbi na obrazcu so modalni razen gumba Zapri/Prekliči. Gumbom nastavimo lastnost DialogResult (po vrsti od leve proti desni) Ignore, Abort, Retry, &o, Yes, OK in &one.

Page 139: PROŠNJA - uranic.tsckr.siuranic.tsckr.si/VISUAL C#/VISUAL C#.pdf · MICROSOFT VISUAL C# .NET Srečo Uranič . Visual C# . ... Za začetek novega projekta v okolju Visual C# Standard

Visual C# . NET

Algoritmi in programski jeziki – 4.letnik

139

Prvi obrazec naj se odpre ob kliku gumba z napisom Odpiranje novega obrazca z metodo ShowDialog: več gumbov za zapiranje, ki se nahaja na osnovnem obrazcu Form1. Ustrezna metoda Click je: private void button1_Click(object sender, EventArgs e) { //iz obrazca Form2 izpeljemo nov objekt form2 Form2 form2 = new Form2();//dinamično kreiranje novega objekta – obrazca //obrazec odpremo z metodo ShowDialog; vrednost ki jo bo obrazec vrnil, ko ga bo uporabnik

//zaprl se bo shranila v spremenljivko Gumb, ki je tipa DialogResult DialogResult Gumb=form2.ShowDialog(); //preverjamo, kateri gumb je uporabnik kliknil ko je zaprl obrazec if (Gumb == DialogResult.OK) MessageBox.Show("Gumb OK"); else if (Gumb == DialogResult.Cancel) MessageBox.Show("Gumb Prekliči"); else if (Gumb == DialogResult.Yes) MessageBox.Show("Gumb DA"); else if (Gumb == DialogResult.No) MessageBox.Show("Gumb Ne"); else if (Gumb == DialogResult.Retry) MessageBox.Show("Gumb Ponovi"); else if (Gumb == DialogResult.Ignore) MessageBox.Show("Gumb Ignoriraj"); else if (Gumb == DialogResult.Abort) MessageBox.Show("Gumb Prekini"); else MessageBox.Show("Gumb Zapri"); form2.Dispose(); //na koncu obrazec uničimo }

Izgled drugega obrazca (Form3):

TextBox Button (navaden gumb s sliko) Modalna gumba

Modalnima gumboma nastavimo lastnost DialogResult na Cancel in OK. Gumbu s sliko priredimo dogodek Click, v katerem preverimo ali je bil obrazec odprt modalno in rezultat izpišemo v sporočilnem oknu. private void button3_Click(object sender, EventArgs e) { if (this.Modal == true) //preverimo, če je bil obrazec odprt modalno MessageBox.Show("Modalni obrazec"); else MessageBox.Show("NEmodalni obrazec"); }

Drugi obrazec naj se odpre ob kliku na gumb z napisom Odpiranje novega obrazca z metodo ShowDialog: dva gumba za zapiranje, ki se nahaja na glavnem obrazcu. Ustrezna metoda Click je: private void button2_Click(object sender, EventArgs e) { Form3 form3 = new Form3(); //dinamično kreiranje novega objekta – obrazca //obrazec odpremo modalno in preverjamo, kako ga bo uporabnik zaprl if (form3.ShowDialog()== DialogResult.OK) MessageBox.Show("Gumb OK"); else MessageBox.Show("Gumb Prekliči"); form3.Dispose(); //sprostimo pomnilnik }

Page 140: PROŠNJA - uranic.tsckr.siuranic.tsckr.si/VISUAL C#/VISUAL C#.pdf · MICROSOFT VISUAL C# .NET Srečo Uranič . Visual C# . ... Za začetek novega projekta v okolju Visual C# Standard

Visual C# . NET

Algoritmi in programski jeziki – 4.letnik

140

Izgled tretjega obrazca (Form4):

TextBox Button (gumb s sliko)

Gumbu s sliko enako kot na drugem obrazcu priredimo dogodek Click, v katerem preverimo ali je bil obrazec odprt modalno in rezultat izpišemo v sporočilnem oknu. Tretji obrazec naj so odpre ob kliku na gumb z napisom Odpiranje novega obrazca: metoda Show, ki se nahaja na glavnem obrazcu. Ustrezna metoda Click je: private void button3_Click(object sender, EventArgs e) { Form4 form4 = new Form4();//dinamično kreiranje novega objekta – obrazca form4.Show();//obrazec odpremo nemodalno }

� NASVET: Metoda obrazca Close običajno ne zaključi aplikacije. Če je namreč odprtih več obrazcev, se aplikacija nadaljuje in konča tedaj, ko zapremo zadnji obrazec. Celotno aplikacijo pa lahko zapremo z metodo Application.Exit. Pri uporabi te metode pa moramo vedeti, da se v tem primeru vsi odprti obrazci zaprejo, a če imajo le-ti prirejen dogodek Close se le ta ne bo izvedel – neshranjeni podatki bodo izgubljeni.

Vključevanje že obstoječih obrazcev in knjižnic v rešitev

V rešitev (Solution), ki jo izdelujemo, lahko dodamo nov ali pa že obstoječi projekt, lahko pa tudi vključimo obrazec ali pa knjižnico, ki že obstaja in je sestavni del nekega drugega projekta. Podobno lahko projekt iz naše rešitve izključimo, iz projekta pa seveda lahko izključimo tudi obrazec ali knjižnico.

Vključevanje projekta, obrazca ali knjižnice v rešitev

V Solution Explorerju izberemo trenutno rešitev ( v primeru da dodajamo nov ali že obstoječi projekt), oziroma izberemo ustrezen projekt (v primeru da dodajamo obstoječi obrazec ali knjižnico). Nato kliknemo desni miškin gumb, izberemo opcijo Add in v odvisnosti od tega, kaj želimo narediti, v oknu ki se prikaže, poiščemo ustrezen projekt, obrazec ali datoteko, ki predstavlja želeno knjižnico. Izbiro potrdimo s klikom na gumb Add in vključitev je opravljena. V rešitev ki jo delamo, se ta datoteka (ali pa celoten projekt) tudi fizično skopira.

Odstranjevanje projekta, obrazca ali knjižnice iz rešitve

Page 141: PROŠNJA - uranic.tsckr.siuranic.tsckr.si/VISUAL C#/VISUAL C#.pdf · MICROSOFT VISUAL C# .NET Srečo Uranič . Visual C# . ... Za začetek novega projekta v okolju Visual C# Standard

Visual C# . NET

Algoritmi in programski jeziki – 4.letnik

141

Postopek je podoben kot pri dodajanju. Če želimo odstraniti celoten projekt, v Solution Exporerju izberemo glavno rešitev (Solution – ta je povsem pod vrhom!), kliknemo desni miškin gumb in nato izberemo opcijo Remove. Prikaže se pogovorno okno, v katerem lahko našo odločitev potrdimo ali pa prekličemo s klikom ustreznega gumba. Datoteka se v tem primeru tudi fizično pobriše/odstrani iz naše rešitve. V kolikor pa želimo odstraniti obrazec ali pa knjižnico, le-to v oknu Solution Explorer najprej izberemo, kliknemo desni miškin gumb in nato izberemo opcijo Delete. Prikaže se pogovorno okno, v katerem lahko našo odločitev potrdimo ali pa prekličemo s klikom ustreznega gumba. Obstaja pa tudi možnost, da nek obrazec ali knjižnico iz projekta le izključimo in jo tako fizično ne pobrišemo. To storimo tako, da namesto opcije Delete izberemo opcijo Exclude From Project.

� Opozorilo: Če hočemo že narejeno formo podedovati, jo najprej v oknu Solution Explorer izberemo, kliknemo desni miškin gumb in v oknu ki prikaže izberemo opcijo Inherit Form (Operacija je možna le polni verziji Visual C#, v Express Edition pa to ni mogoče!!!!!)

Sporočilno okno AboutBox

Sporočilno okno AboutBox je okno, ki ga v projekt vključimo z namenom, da nam prikaže najpomembnejše podatke o projektu ( ime, verzijo programa, opis, …). Okno dodamo v projekt tako, da v Solution Explorerju

izberemo ustrezen projekt znotraj rešitve, kliknemo desni miškin gumb in v POP Up meniju izberemo opcijo Add, nato pa &ew Item.

V oknu, ki se odpre, izberemo AboutBox, po želji oknu tudi spremenimo ime.

Dodajanje potrdimo s klikom na gumb Add. Sporočilno okno je tako dodano v naš projekt, Na obrazcu je nekaj label, okno s tekstom (TextBox) in slika.

Page 142: PROŠNJA - uranic.tsckr.siuranic.tsckr.si/VISUAL C#/VISUAL C#.pdf · MICROSOFT VISUAL C# .NET Srečo Uranič . Visual C# . ... Za začetek novega projekta v okolju Visual C# Standard

Visual C# . NET

Algoritmi in programski jeziki – 4.letnik

142

Lastnosti gradnikov Label in vsebino gradnika TextBox &E spreminjajmo, ker se bo njihova vsebina zapisala sama, oziroma jo bomo določili na drugem mestu, pri t.i. lastnostih projekta. Poglejmo kodo obrazca AboutBox

(pogled View Code).

Koda se je zgenerirala avtomatsko: vsebuje kar nekaj klicev metod, ki so zapisane v delu Assembly Attribute Accessors – ta del kode je začasno skrit, seveda pa si skrito kodo lahko ogledamo s klikom na znak + na levem robu vrstice. Vseh stavkov verjetno ne bomo razumeli, a saj ni potrebno. Pomembno je le da vemo, da koda v oknu AboutBox izpiše podatke o našem projektu – te podatke pa zapišemo v lastnostih projekta. Lastnostih projekta nastavljamo v posebnem oknu, ki ga odpremo tako, da v Solution Explorerju izberemo ustrezen projekt, nato pa z desnim miškinim gumbom odpremo Pop Up Meni in izberemo opcijo Properties. Odpre se okno za nastavljanje lastnosti projekta.

V sekciji Application lahko nastavimo (spremenimo) glavne lastnosti projekta. S klikom na gumb Assembly Information se odpre okno za vnos dodatnih podatkov o tekočem projektu (ime, opis, verzija, …). Okno nam ponuja tudi možnost spreminjanja ikone, ki se bo kasneje prikazala v Windows Explorerju ob našem projektu.

Page 143: PROŠNJA - uranic.tsckr.siuranic.tsckr.si/VISUAL C#/VISUAL C#.pdf · MICROSOFT VISUAL C# .NET Srečo Uranič . Visual C# . ... Za začetek novega projekta v okolju Visual C# Standard

Visual C# . NET

Algoritmi in programski jeziki – 4.letnik

143

V primeru, da naš projekt vsebuje več obrazcev, nam okno za nastavitev lastnosti projekta ponuja možnost, da izberemo, kateri obrazec se bo prikazal prvi. To storimo z izbiro v spustnem seznamu, ki se odpre v vrstici Startup object. Okno vsebuje še cel kup lastnosti in razdelkov, ki pa jih zaenkrat ne bomo omenjali. Za sporočilno okno AboutBox moramo sedaj le še napisati kodo za prikaz. To lahko storimo npr. z novo postavko v meniju, nanjo dvokliknemo in zapišemo ustrezno kodo. Okno odpremo modalno (metoda ShowDialog), zato da se bo zaprlo ob kliku na njegov gumb OK. new AboutBox1().ShowDialog();

Lastnosti, ki smo jih nastavili v oknu AboutBox in spremembe, ki smo jih v njem naredili, bodo prikazane v oknu šele ob zagonu programa. Poženimo torej program in odprimo okno za ogled nastavljenih lastnosti.

Dostop do polj, metod in gradnikov drugega obrazca

Dostop do polj in metod generiranih v razredu drugega obrazca

Pri izdelavi projekta z več obrazci se večkrat srečamo s problemom dostopa do polja (spremenljivke) oz do metode nekega drugega obrazca. Direkten dostop je možen le do statičnih polj in statičnih metod razreda v drugem obrazcu. Recimo da imamo obrazec Form1 (glavni obrazec projekta) in iz tega odpiramo nov obrazec Form2. V razredu obrazca Form2 naj obstaja statično polje stevec in statična metoda Info. Njuna deklaracija je npr. takale: public static int stevec = 0; //števec objektov - globalna statična spemenljivka static public string Info() { return ime+"\nRazred ima statično polje (stevec) in eno statično metodo (Info)"; }

Ločiti moramo dva primera: • Objekt, ki ga generiramo ima različno ime kot je ime razreda v drugem obrazcu:

Form2 novaForma = new Form2();//ime objekta je novaForma, ime razreda je Form2

Do statičnih polj in statičnih metod razreda Form2 dostopamo preko imena razreda Form2, npr. takole: MessageBox.Show(Form2.Info() + "\nŠtevec objektov: " + Form2.stevec);

Page 144: PROŠNJA - uranic.tsckr.siuranic.tsckr.si/VISUAL C#/VISUAL C#.pdf · MICROSOFT VISUAL C# .NET Srečo Uranič . Visual C# . ... Za začetek novega projekta v okolju Visual C# Standard

Visual C# . NET

Algoritmi in programski jeziki – 4.letnik

144

• Objekt, ki ga generiramo ima enako ime kot je ime razreda v drugem obrazcu: Form2 Form2 = new Form2();//ime objekta je Form2, ime razreda je Form2

Do statičnih polj in statičnih metod razreda Form2 dostopamo preko imenskega prostora in imena razreda Form2, npr. takole: //pred imenom razreda je še ime imenskega prostora (v našem primeru IMP) MessageBox.Show(IMP.Form2.Info() + "\nŠtevec objektov: " + IMP.Form2.stevec);

Dostop do gradnikov na drugem obrazcu

Dostop do gradnikov drugega razreda je možen na dva načina:

• preko lastnosti Controls objekta, ki ga tvorimo iz nekega obrazca. Recimo da imamo obrazec Form1 (glavni obrazec projekta) in iz tega odpiramo nov obrazec z imenom Form2. Na obrazcu Form2 imamo npr gradnik textBox1 in textBox2. V razredu Form1 najprej zgeneriramo nov objekt razreda Form2, nato pa s pomočjo lastnosti Controls tega novega objekta lahko dostopamo do njegovih gradnikov.

Form2 novaForma = new Form2();//ime objekta je novaForma, ime razreda je Form2 NovaForma.Controls["comboBox1"].Text = "Slovenija"; //dostop do gradnika comboBox1 NovaForma.Controls["textBox2"].Text = "Kranj"; //dostop do gradnika textBox1

• preko imena drugega obrazca – a v tem primeru morajo biti vsi gradniki, do katerih želimo imeti

dostop deklarirani kot javni (privzeta deklaracija je private). Javno deklaracijo lahko določimo vsakemu gradniku na dva načina:

v datoteki .Designer.cs ( v našem primeru je to datoteka Form2.Designer.cs) poiščemo ustrezno

vrstico z deklaracijo (ta je privzeto private) in jo popravimo na public. gradniku, do katerega želimo dostopati preko drugega obrazca, nastavimo lastnost Modifiers na

Public.

Form2 novaForma = new Form2();//ime objekta je novaForma, ime razreda je Form2 NovaForma.comboBox1.Text = "Slovenija";//dostop do gradnika comboBox,ki mora biti javen NovaForma.textBox2.Text = "Kranj"; //dostop do gradnika textBox1, ki mora biti javen

Vaja: V naslednjem projektu imamo dva obrazca. Na prvem je kreiram preprosti meni, pod njim pa je v gradniku ListBox seznam nepremičnin. Nepremičnine v ListBox vnašamo in ažuriramo preko menija in s pomočjo drugega obrazca Form2. Obrazec Form1: Gradnik ListBox

Page 145: PROŠNJA - uranic.tsckr.siuranic.tsckr.si/VISUAL C#/VISUAL C#.pdf · MICROSOFT VISUAL C# .NET Srečo Uranič . Visual C# . ... Za začetek novega projekta v okolju Visual C# Standard

Visual C# . NET

Algoritmi in programski jeziki – 4.letnik

145

Pripadajoča koda: public Form1()//konstruktor zareda Form1 { InitializeComponent(); } //metoda se izvede ob kliku na opcijo Dodaj v meniju private void dodajToolStripMenuItem_Click(object sender, EventArgs e) { //POZOR!! - nov objekt se imenuje NovaForma! Objekt je izpeljan iz razreda Form2 //generiranje novega objekta NovaForma - klic konstruktorja Form2 Form2 NovaForma = new Form2(); NovaForma.Controls["comboBox1"].Text = "Vila";//začetni text v gradniku textBox1 //ali pa NovaForma.comboBox1.Text = "Vila"; - a v tem primeru mora biti gradnik comboBox1 //na obrazcu Form2 deklariran kot public //obrazec odpremo modalno if (NovaForma.ShowDialog() == DialogResult.OK) //uporabnik je vnos zaključil z OK { //med posamezne podatke vstavimo ločilni znak "|" - zaradi lažje obdelave podatkov listBox1.Items.Add(NovaForma.Controls["comboBox1"].Text //vrsta nepremičnine +" | "+NovaForma.Controls["textBox2"].Text //kraj +" | "+NovaForma.Controls["dateTimePicker1"].Text //datum vnosa +" | "+NovaForma.Controls["textBox3"].Text); //znesek } } //metoda se izcede ob zapiranju obrazca Form1 private void konecToolStripMenuItem_Click(object sender, EventArgs e) { Close(); } //metoda se izvede ob odpiranju obrazca Form1 private void Form1_Load(object sender, EventArgs e) { listBox1.Items.Add("VRSTA NEPREMIČNINE KRAJ DATUM VNOSA CENA"); } /*metoda se izvede ob kliku na opcijo Ažuriraj v meniju. V tem primeru moramo podatke v izbrani vrstici gradnika ListBox najprej prenesti v gradnike na obrazcu Form2*/ private void ažurirajToolStripMenuItem_Click(object sender, EventArgs e) { try //varovalni blok za primer napake, ali pa če ni izbrana nobena vrstica { int vrstica = listBox1.SelectedIndex; //številka izbrane vrstive v gradniku ListBox //podatke iz izbrane vrstice shranim v string podatki string podatki = listBox1.Items[vrstica].ToString(); string[] tabela = podatki.Split('|'); //s pomočjo ločilnega znaka ločim posamezne podatke //generiranje novega objekta NovaForma - klic konstruktorja Form2 Form2 NovaForma = new Form2(); NovaForma.Controls["comboBox1"].Text = tabela[0]; NovaForma.Controls["textBox2"].Text = tabela[1]; NovaForma.Controls["dateTimePicker1"].Text = tabela[2]; NovaForma.Controls["textBox3"].Text = tabela[3]; if (NovaForma.ShowDialog() == DialogResult.OK) //uporabnik je kliknil gumb OK { //med posamezne podatke vstavimo ločilni znak "|" - zaradi lažje obdelave podatkov listBox1.Items[vrstica] = NovaForma.Controls["comboBox1"].Text //vrsta nepremičnine + " | " + NovaForma.Controls["textBox2"].Text //kraj + " | " + NovaForma.Controls["dateTimePicker1"].Text //datum vnosa + " | " + NovaForma.Controls["textBox3"].Text; //znesek } } catch { MessageBox.Show("Napaka, ali pa ni izbrana nobena vrstica v seznamu!"); } } //metoda se izvede ob kliku na opcijo Info v meniju private void infoToolStripMenuItem_Click(object sender, EventArgs e) {

Page 146: PROŠNJA - uranic.tsckr.siuranic.tsckr.si/VISUAL C#/VISUAL C#.pdf · MICROSOFT VISUAL C# .NET Srečo Uranič . Visual C# . ... Za začetek novega projekta v okolju Visual C# Standard

Visual C# . NET

Algoritmi in programski jeziki – 4.letnik

146

/*do statičnih spremenljivk (stevec) dostopamo preko imena razreda (Form2) in ne preko imena objekta*/ MessageBox.Show(Form2.Info() + "\nŠtevec živih objektov: " + Form2.stevecZivih + "\nSkupno število doslej generiranih objektov: " + Form2.stevec + "\nSkupno število vnosov v gradniku ListBox: " + (listBox1.Items.Count - 1)); } //metoda se izvede ob kliku na opcijo Izpiši v meniju private void toolStripMenuItem1_Click(object sender, EventArgs e) { string seznam="SEZNAM NEPREMIČNIN\n"; double najdrazja = 0; //obdelamo celotno vsebino gradnik listBox1 razen prve vrstice for (int vrstica=1;vrstica<listBox1.Items.Count;vrstica++) { //podatke iz izbrane vrstice shranim v string podatki string podatki = listBox1.Items[vrstica].ToString(); string[] tabela = podatki.Split('|'); //s pomočjo ločilnega znaka ločim podatke seznam=seznam+tabela[0]+", "+tabela[1]+", "+tabela[2]+", Cena: "+tabela[3]+"\n"; try //varovalni blok, ker lahko da cena povsod ni zapisana { if (Convert.ToDouble(tabela[3]) > najdrazja) najdrazja = Convert.ToDouble(tabela[3]); } catch { } } MessageBox.Show(seznam+"\n\nNajdražja nepremičnina: "+najdrazja.ToString()+" EUR"); }

Obrazec Form2: ComboBox DateTimePicker Pripadajoča koda: public static int stevec = 0;//števec doslej generiranih objektov - globalna statična //spemenljivka public static int stevecZivih = 0; //števec živih objektov public static string ime = "Razred Form2"; public Form2()//konstruktor { InitializeComponent(); stevec++; stevecZivih++; } ~ Form2() // destruktor - izvede se ob uničenju objekta { stevecZivih--; } static public string Info() //statična metoda, dostopna je direktno preko imena razreda { return ime+"\nRazred ima 2 statični polji(stevec in stevecZivih) in statično metodo(Info)"; }

Page 147: PROŠNJA - uranic.tsckr.siuranic.tsckr.si/VISUAL C#/VISUAL C#.pdf · MICROSOFT VISUAL C# .NET Srečo Uranič . Visual C# . ... Za začetek novega projekta v okolju Visual C# Standard

Visual C# . NET

Algoritmi in programski jeziki – 4.letnik

147

//metoda se izvede ob kreiranju novega obrazca private void Form2_Load(object sender, EventArgs e) { //v gradnik ComboBox dodamo nekaj vrednosti comboBox1.Items.Add("Samostojna hiša"); comboBox1.Items.Add("Stanovanje - Dvosobno"); comboBox1.Items.Add("Stanovanje - Enosobno"); comboBox1.Items.Add("Stanovanje - Trisobno"); comboBox1.Items.Add("Stanovanje - Garsonjera"); comboBox1.Items.Add("Del hiše"); comboBox1.Items.Add("Parcela"); comboBox1.Sorted=true; //vsebino uredimo po abecedi } //metoda se zgodi ob pritisku tipke, ko ima focus gradnik textBox3 //z metodo dovolimo uporabniku le vnos cifer, decimalne vejice in tipko BackSpace private void textBox3_KeyPress(object sender, KeyPressEventArgs e) { if ((e.KeyChar < '0' || e.KeyChar > '9') && (e.KeyChar != ',') && (e.KeyChar != (char)8)) e.Handled = true; }

Vaja: Kadar je projekt sestavljen iz več obrazcev in v teh obrazcih uporabljamo objekte, ki jih izpeljemo iz naših lastnih razredov, je priporočljivo, da razred napišemo v svoji datoteki, ločeno od obrazca. Kreirajmo projekt Ulomki. Slika osnovnega obrazca naj bo takale:

Preden bomo zapisali potrebne deklaracije in odzivne metode, je potrebna deklaracija razreda Ulomek. Deklaracijo razreda Ulomek, ki ga bomo potrebovali pri delu s tem obrazcem, bomo zapisali v svoji datoteki. Izberimo opcijo Project -> Add Class in nato v pogovornem oknu (opcija Class je v tem primeru že izbrana) datoteki dajmo ime Ulomek.cs (namesto predlaganega imena Class1.cs).

Za vstavljanje novega razreda obstaja seveda še ena pot: v SolutionExplorerju izberimo projekt Ulomek, nato desni klik miške in izberimo opcijo Add in nato &ewItem. Odpre se isto okno kot v prvem primeru: izberimo opcijo Class in določimo ime novega razreda Ulomek.cs (ali pa pustimo predlagano ime).

Vsebina razreda Ulomek.cs naj bo npr. takale:

Page 148: PROŠNJA - uranic.tsckr.siuranic.tsckr.si/VISUAL C#/VISUAL C#.pdf · MICROSOFT VISUAL C# .NET Srečo Uranič . Visual C# . ... Za začetek novega projekta v okolju Visual C# Standard

Visual C# . NET

Algoritmi in programski jeziki – 4.letnik

148

class Ulomek { private int stevec,imenovalec; //Razred ima dve zasebni polji public Ulomek() //privzeti/osnovni konstruktor { stevec=0;imenovalec=1; } public Ulomek(int st, int im) //Preobloženi onstruktor { stevec=st; imenovalec=im; } public void obrat() //Metoda za zamenjavo števca in imenovalca { int zacasna = stevec; stevec = imenovalec; imenovalec = zacasna; } public int Stevec() //javna metoda za dostop do polja stevec { return stevec; } public int Imenovalec() //javna metoda za dostop do polja imenovalec { return imenovalec; } }

Ker se ta razred sedaj nahaja znotraj našega projekta, imamo do njega dostop v vseh obrazcih, ki sestavljajo naš projekt. V kateremkoli obrazcu projekta lahko iz tega razreda ustvarjamo objekte in se preko njihovih imen sklicujemo na javna polja in metode tega razreda. Znotraj tega razreda bi lahko definirali tudi statična polja, ki bi jih lahko uporabili kot globalne spremenljivke znotraj celotnega projekta. Do njih bi imeli seveda dostop preko imena razreda (Ulomek) in ne preko objektov izpeljanih iz tega razreda. Zapišimo sedaj dogodke našega osnovnega obrazca. public Form1() { InitializeComponent(); } Random naklj; Ulomek u1, u2; //ulomek je razred, ki je deklariran v datoteki Ulomek.cs private void Form1_Load(object sender, EventArgs e) { naklj=new Random(); u1 = new Ulomek(); u2=new Ulomek(naklj.Next(0,11),naklj.Next(1,11)); izpis(); } public void izpis() { textBox1.Text = Convert.ToString(u1.Stevec()); textBox2.Text = Convert.ToString(u1.Imenovalec()); textBox3.Text = Convert.ToString(u2.Stevec()); textBox4.Text = Convert.ToString(u2.Imenovalec()); } //dogodek se izvede ob kliku na gumba Obratni ulomek private void button1_Click(object sender, EventArgs e) { if (sender==button1) u1.obrat(); else u2.obrat(); izpis(); } //dogodek se zgodi ob kliku na gumb Tabela ulomkov private void button3_Click(object sender, EventArgs e) { TabelaUlomkov Ftabela = new TabelaUlomkov();

Page 149: PROŠNJA - uranic.tsckr.siuranic.tsckr.si/VISUAL C#/VISUAL C#.pdf · MICROSOFT VISUAL C# .NET Srečo Uranič . Visual C# . ... Za začetek novega projekta v okolju Visual C# Standard

Visual C# . NET

Algoritmi in programski jeziki – 4.letnik

149

Ftabela.Show(); }

Še izgled obrazca TabelaUlomkov: ListBox Pripadajoča koda tega obrazca: Ulomek[] Tabela;//ulomek je razred, ki je deklariran v datoteki Ulomek.cs private void TabelaUlomkov_Load(object sender, EventArgs e) { listBox1.Items.Add("Števec Imenovalec"); listBox1.Items.Add(" "); Random naklj = new Random(); //zgeneriramo tabelo 10 ulomkov in jih prikažemo v gradniku ListBox Tabela = new Ulomek[10]; for (int i = 0; i < 10; i++) { Tabela[i] = new Ulomek(naklj.Next(1, 10), naklj.Next(1, 10)); //zapis ustrezno formatiramo string vrstica = string.Format("{0,8} {1,10}", Tabela[i].Stevec(), Tabela[i].Imenovalec()); listBox1.Items.Add(vrstica); } } private void button1_Click(object sender, EventArgs e) { Close(); }

Vaja: V naslednji vaji je prikazana še rešitev, kako dosežemo, da imajo obrazci, ki jih odpiramo iz glavnega obrazca, dostop do gradnikov na glavnem obrazcu. Če hočemo v podrejenih obrazcih priti do gradnikov na glavnem obrazcu, je potreben poseg v projektno datoteko (običajno je njeno ime Program.cs). Vsebino projektne datoteko spremenimo tako, da je objekt, ki ga izpeljemo iz glavnega obrazca statičen, saj bomo le tako lahko do resursov na glavnem obrazcu dostopali iz drugih obrazcev. //VSEBINA PROJEKTNE DATOTEKE //napoved statičnega obrazca - do njega bomo lahko dostopali iz ostalih obazcev static public FGlavni glavniObrazec; //napoved stat.objekta razreda Form [STAThread] static void Main() { Application.EnableVisualStyles(); Application.SetCompatibleTextRenderingDefault(false); glavniObrazec = new FGlavni(); //ustvarjanje novega objekta izpeljanega iz razreda FGlavni Application.Run(glavniObrazec);//zagon projekta – odpiranje glavnega obrazca }

Page 150: PROŠNJA - uranic.tsckr.siuranic.tsckr.si/VISUAL C#/VISUAL C#.pdf · MICROSOFT VISUAL C# .NET Srečo Uranič . Visual C# . ... Za začetek novega projekta v okolju Visual C# Standard

Visual C# . NET

Algoritmi in programski jeziki – 4.letnik

150

Na glavnem obrazcu je en sam gradnik – gumb, za odpiranje novih obrazcev. Uporabljena je metoda Show, da je lahko hkrati odprtih več obrazcev. Ob kliku na ta gumb se odpirajo podrejeni obrazci – v glavi vsakega od njih je

zapisana številka tega obrazca. Tudi podrejeni obrazci imajo en sam gumb – ob kliku na ta gumb se obrazec zapre, v gradnik TextBox na glavnem obrazcu pa se zapiše vrstica s podatkom o tem, kateri podrejeni obrazec je bil zaprt in koliko je število trenutno še odprtih obrazcev (ta podatek je shranjen v statičnem polju razreda FGlavni).

Koda glavnega obrazca FGlavni: public FGlavni() { InitializeComponent(); } public static int stevec=0; //statično polje – uporabili ga bomo za štetje odprtih obrazcev private void button1_Click(object sender, EventArgs e) { FNov novObrazec = new FNov();//kreiranje novega objekta izpeljanega iz razreda FNov novObrazec.Show(); //vsak naslednji obrazec bo za 20 enot bolj oddaljen od zgornjega roba novObrazec.Top = stevec*20; novObrazec.Text = "Obrazec številka: " + Convert.ToString(stevec); stevec++;//Povečamo število živečih obrazcev. }

Koda podrejega obrazca F&ov: public FNov() { InitializeComponent(); } private void button1_Click(object sender, EventArgs e) { //Do statičnega polja števec na GLAVNEM OBRAZCU dostopamo preko IMENA glavnega obrazca FGlavni.stevec--;//Zmanjšamo število živečih obrazcev. Program.glavniObrazec.Controls["textBox1"].Text +="Zaprt " + this.Text +". Še živečih obrazcev: "+Convert.ToString(FGlavni.stevec)+(char)13+(char)10; //Če pa lastnost Modifiers gradnika textBox1 nastavimo na Public, se lahko nanj sklicujemo //tudi takole: Program.glavniObrazec.textBox1.Text +="Zaprt " + this.Text+... Close(); }

Page 151: PROŠNJA - uranic.tsckr.siuranic.tsckr.si/VISUAL C#/VISUAL C#.pdf · MICROSOFT VISUAL C# .NET Srečo Uranič . Visual C# . ... Za začetek novega projekta v okolju Visual C# Standard

Visual C# . NET

Algoritmi in programski jeziki – 4.letnik

151

Delo z enostavnimi in sestavljenimi meniji (nadaljevanje), orodjarna

Meniji so standardni elementi okenskih programov. V menijih zapisane vrstice predstavljajo ukaze in gume, ki jih lahko vklopimo z miško ali pa s tipkovnico. Poznamo dve vrsti menijev: glavnega, ki ga navadno postavimo pod gornji rob obrazca, in lebdečega, ki ga odpre klik desnega gumba miške na določenem predmetu. Opisana gradnika predstavljata naslednja Visual C# gradnika:

• MenuStrip – glavni meni in • StatusStrip – lebdeči meni.

Glavni meni – MenuStrip (ponovitev + nadgradnja) Gradnik MenuStrip, ki ga potrebujemo za kreiranje glavnega manija, se nahaja v oknu Toolbox v skupini gradnikov Menus & Toolbars. To je nevizuelni gradnik: ko ga želimo postaviti na obrazec se namesti v polje

pod obrazcem (tako kot npr. gradnik Timer, ki ga že poznamo), na vrhu obrazca pa se pokaže začetno stanje menija. Z oblikovanjem menija pričnemo tako, da kliknemo v okno menija z napisom Type Here in zapišemo tekst, ki ga želimo imeti v meniju. Tekst menija nato po želji dodajamo v horizontalni ali pa v vertikalni smeri. Urejevalnik menija je videti kot pravi meni, le da ima nakazana prazna mesta, kamor je mogoče pisati nove postavke.

Vsaka postavka v meniju ima svojo kartico lastnosti, ki so prikazane v oknu Properties. Posamezno postavko menija predstavlja Visual C# predmet MenuItem. Najbolj značilna lastnost menijske postavke je njen napis (lastnost Text), iz katere Visual C# tudi izpelje ime postavke (lastnost Name). Če smo npr. neko postavko napisali ime Konec, je njeno programsko ime konecToolStripMenuItem.

� NASVET: Če pred imen postavke menija, ali pa pred katerokoli črko v imenu, zapišemo znak & bo ta črka v meniju podčrtana, kar pomeni, da lahko do te postavke dostopamo tudi s kombinacijo tipk Shift+Črka.

V urejevalniku menija postavke dodajamo, brišemo in prestavljamo s tipkami oziroma z miško:

• Brisanje že narejene postavke: tipka Delete • Desni klik nad menijem na obrazcu: prikaže se PopUp meni s

številnimi opcijami:

o Set Image: Izbira slike (ikone), ki se naj prikaže ob postavki menija;

o Enabled: Postavka menija omogočena ali onemogočena; o Cheched: Če jo označimo se na levi strani prikaže kljukica; o ShowShortcutKeys: vklop/izklop prikaza kombinacije tipk za

dostop opcije preko tipkovnice (v tem primeru moramo v oknu Properties gradniku določiti lastnost ShortcutKeys in izbrati ustrezno kombinacijo tipk);

o Convert To: Vsaka postavka v meniju je lahko prikazana na

Page 152: PROŠNJA - uranic.tsckr.siuranic.tsckr.si/VISUAL C#/VISUAL C#.pdf · MICROSOFT VISUAL C# .NET Srečo Uranič . Visual C# . ... Za začetek novega projekta v okolju Visual C# Standard

Visual C# . NET

Algoritmi in programski jeziki – 4.letnik

152

štiri načine: kot MenuItem, ComboBox, Separator ali kot TextBox. Preko opcije Convert To lahko spremenimo način prikaza;

o Insert: Dodajanje nove postavke v meni; o Select: izbira te opcije nam ponudi seznam gradnikov, v katerem izberemo, kateri od njih naj bo

izbran; o Cut: Izrežemo izbrano postavko; o Copy: Kopiranje izbrane postavke; o Paste: Prilepi postavko; o Delete: Brisanje izbrane postavke o Document Outline: v Solution Explorerju se nam prikaže novo okno, v katerem so vse prikazani

gradniki obrazca in vse opcije gradnika MenuStrip; o Properties: okno Properties izbrane postavke menija postane aktivno.

• Tipki Ctrl + tipka s puščico v desno: začenjanje podmenija • Vleka: prestavljanje menijske postavke skupaj z morebitnim podmenijem.

� NASVET: Ločilno črto (separator) v meniju lahko naredimo tudi tako, da namesto vnosa imena neke postavke zapišemo le znak pomišljaj ''-''..

Vsaki postavki v meniju priredimo odzivni dogodek tako, da jo najprej izberemo, nato v oknu Properties kliknemo hitri gumb za prikaz dogodkov (Events) in končno izberemo ustrezni dogodek. Običajno bo to dogodek Click ali pa DoubleClick. Dvokliknemo v prazno polje ustreznega dogodka, da nam C# pripravi ogrodje metode, nato pa končno zapišemo ustrezne stavke odzivne metode.

Lebdeči (Pop Up) meni - ContextMenuStrip Lebdeči meni se v zagnanem programu pokaže, ko na nek gradnik kliknemo z desnim gumbom miške. Vsakemu gradniku lahko določimo njegov lebdeči meni. To storimo tako, da ime lebdečega menija zapišemo v lastnost ContextMenuStrip. Seveda po moramo prej lebdeči meni pripraviti, a izdelava se sploh ne razlikuje od izdelave glavnega menija. Uporabimo gradnik ContextMenuStrip, ki se tako kot gradnik MenuStrip nahaja v oknu

Toolbox v skupini gradnikov Menus & Toolbars. Tudi ta gradnik je nevizuelni gradnik: ko ga postavimo na obrazec se namesti v polje pod obrazcem na vrhu obrazca pa se pokaže začetno stanje menija. Lebdeči meni urejamo tako kot glavni meni: v prazno okno vpišemo besedilo postavke, nato pa se premaknemo navzdol, če želimo imeti naslednjo opcijo v vertikali, ali pa desno, če želimo v tej vrstici začeti nov podmeni.

Vsaki postavki v lebdečem meniju priredimo odzivni dogodek tako, da jo najprej izberemo, nato v oknu Properties kliknemo hitri gumb za prikaz dogodkov (Events) in končno izberemo ustrezni dogodek. Običajno bo to dogodek Click ali pa DoubleClick. Dvokliknemo v prazno polje ustreznega dogodka, da nam C# pripravi ogrodje metode, nato pa končno zapišemo ustrezne stavke odzivne metode.

Page 153: PROŠNJA - uranic.tsckr.siuranic.tsckr.si/VISUAL C#/VISUAL C#.pdf · MICROSOFT VISUAL C# .NET Srečo Uranič . Visual C# . ... Za začetek novega projekta v okolju Visual C# Standard

Visual C# . NET

Algoritmi in programski jeziki – 4.letnik

153

Orodjarna - ToolStrip Orodjarna ali orodna letvica je vsebnik – gradnik ki vsebuje druge gradnike. Na letvico lahko postavljamo gradnike tipa Button, Label, SplitButton, DropDownButton, Separator, ComboBox, TextBox in ProgresBar. Tudi ta gradnik je nevizuelni gradnik: ko ga postavimo na obrazec se namesti v polje pod obrazcem, na vrhu obrazca pa se pokaže začetno stanje orodjarne. Uporabimo gradnik ToolStrip, ki se nahaja v oknu Toolbox v skupini gradnikov Menus & Toolbars. Nove gradnike v orodjarno dodajamo tako, da odpremo

spustni seznam. V oknu, ki se odpre, nato izberemo ustrezni gradnik. Če želimo v orodjarno dodatki nov gumb, obstaja še hitrejši način: samo kliknemo v polje s sličico in v orodjarni se takoj pojavi nov gumb. Tako nastalim gumbom lahko poljubno spreminjamo lastnosti (npr. sličico na gumbu, …) in prirejamo odzivne metode (najpogosteje dogodek Click). Posamezne gradnike znotraj orodjarne lahko kadarkoli odstranimo (pobrišemo) ali pa jih preuredimo (primemo z miško in prenesemo na drugo pozicijo v orodjarni).

Vaja: Za prikaz lebdečega menija in orodjarne naredimo nov projekt in ga poimenujmo Lebdeči meni. Obrazec naj zgleda takole:

Orodjarna (toolStrip1) s štirimi gumbi in separatorjem Lebdeči meni (contextMenuStrip1) s štirimi postavkami (Izreži, Kopiraj, Prilepi in Sprememba fonta). Vsakemu od gumbov smo s pomočjo njegove lastnosti Image priredili ustrezno sličico. Gradnik RichTextBox (za razliko od gradnika TextBox lahko tekst v tem gradniku tudi oblikujemo). Na obrazec pa smo postavili tudi nevizuelni gradnik FontDialog (fontDialog1) – gradnik se nahaja v oknu ToolBox v skupini Dialogs.

Gradniku RichTextBox priredimo lastnost ContextMenuStrip na contextMenuStrip1 (povežemo lebdeči meni z gradnikom RichTextBox). Postavkam v lebdečem meniju sedaj zaporedoma priredimo naslednje dogodke Click:

• Postavka Izreži: namen postavke je izrez (brisanje) označenega teksta v gradniku RichTextBox. Ustrezna koda:

private void izrežiToolStripMenuItem_Click(object sender, EventArgs e) { richTextBox1.Cut(); //izrežemo označeni tekst }

Page 154: PROŠNJA - uranic.tsckr.siuranic.tsckr.si/VISUAL C#/VISUAL C#.pdf · MICROSOFT VISUAL C# .NET Srečo Uranič . Visual C# . ... Za začetek novega projekta v okolju Visual C# Standard

Visual C# . NET

Algoritmi in programski jeziki – 4.letnik

154

• Postavka Kopiraj: namen postavke je kopiranje označenega teksta v gradniku RichtextBox. Ustrezna koda: private void kopirajToolStripMenuItem_Click(object sender, EventArgs e) { richTextBox1.Copy();//kopiranje označenega teksta }

• Postavka Prilepi: namen postavke je kopiranje kopiranega teksta v gradniku RichTextBox. Ustrezna

koda: private void prilepiToolStripMenuItem_Click(object sender, EventArgs e) { richTextBox1.Paste();//lepljenje kopiranega teksta }

• Postavka Sprememba fonta: ob kliku na to postavko se odpre pogovorno okno FontDialog (okno je že sestavni del Visual C#), ki nam omogoči, da prej izbranemu tekstu določimo nove nastavitve (font, velikost, stil, barvo,…). Ustrezna koda:

private void spremembaFonaToolStripMenuItem_Click(object sender, EventArgs e) { // okno fontDialog1 odpremo modalno if (fontDialog1.ShowDialog() != DialogResult.Cancel) { //če je uporabnik pri zapiranju okna fontDialog NI kliknil gumba Prekliči richTextBox1.SelectionFont = new Font(fontDialog1.Font.Name,

fontDialog1.Font.Size, fontDialog1.Font.Style); //ali kar richTextBox1.SelectionFont = fontDialog1.Font; } }

Zgoraj opisane dogodke, ki smo jih priredili postavkam v lebdečem meniju zdaj priredimo še posameznim gumbom v orodjarni. To storimo tako, da izberemo ustrezen gumb, se preselimo v okno Properties, izberemo zavihek Events/Dogodki in nato pri dogodku Click s pomočjo spustnega seznama IZBEREMO enega že prej pripravljenih dogodkov. Vaja: Za zaključek poglavja o menijih naredimo še projekt, v katerem bomo uporabili obe vrste menija: glavni meni in lebdeči meni. Projekt bo sestavljen iz dveh obrazcev. Na prvi (glavni) obrazec bomo postavili glavni meni, iz katerega bomo odpirali drugi obrazec. Izgled glavnega obrazca:

Glavni meni (menuStrip1) Gradnik StatusStrip( statusStrip1) z enim predalčkom – labelo za prikaz ure Na obrazcu je tudi gradnik Timer (timer1)

Glavni meni ima dve postavki v horizontalni smeri (Datoteka in Konec). Postavka datoteka ima pod seboj dodatne tri postavke (Dodaj, Ažuriraj in Izreži). Obrazcu smo določili še lastnost BackgroundImage. Za sliko v ozadju smo izbrali kar sliko, ki je sestavni del Visual Studia (C:\Program Files\Microsoft Visual Studio 8\Common7\Packages\Watermark.jpg). Klik na gumb Konec projekt zaključi (metoda Application.Exit). Ob kliku na gumb Dodaj, pa naj se odpre nov obrazec, ki ga bomo poimenovali FClanski_Obrazec. Koda za odpiranje tega obrazca je takale: private void dodajaToolStripMenuItem_Click(object sender, EventArgs e) { FClanskiObrazec FClanskiObrazec = new FClanskiObrazec();//kreiranje novega obrazca if (FClanskiObrazec.ShowDialog() == DialogResult.OK)

Page 155: PROŠNJA - uranic.tsckr.siuranic.tsckr.si/VISUAL C#/VISUAL C#.pdf · MICROSOFT VISUAL C# .NET Srečo Uranič . Visual C# . ... Za začetek novega projekta v okolju Visual C# Standard

Visual C# . NET

Algoritmi in programski jeziki – 4.letnik

155

MessageBox.Show("OK - Podatke bom shranil"); else MessageBox.Show("Preklic vnosa podatkov!"); FClanskiObrazec.Dispose(); //sprostimo pomnilnik }

Ker dela z datotekami (zapis v datoteko, branje, …) še ne poznamo, smo namesto shranjevanja zapisa v datoteko uporabili kar pogovorno okno MessageBox, ki izpiše ustrezno sporočilo. Za zapiranje obrazca (ob kliku na postavko Konec v glavnem meniju) uporabimo eno od oblik sporočilnega okna MessageBox, ki v oknu izpiše dva gumba (V redu in Prekliči), zraven pa še ikono z vprašajem. Uporabniku prepustimo odločitev o končanju programa oz. povratku na glavni obrazec). Ustrezna metoda: private void konecToolStripMenuItem_Click(object sender, EventArgs e) { if (MessageBox.Show("Zaključek programa" ,"KONEC" ,MessageBoxButtons.OKCancel

,MessageBoxIcon.Question)==DialogResult.OK) Application.Exit(); }

V metodi MessageBox smo uporabili štiri parametre:

• Prvi parameter je string "Zaključek programa" , ki bo izpisan znotraj sporočilnega okna; • Drugi parameter je string "KONEC", ki bo izpisan v naslovni vrstici okna MessageBox; • S tretjim parametrom MessageBoxButtons.OKCancel povemo kateri gumbi bodo v oknu; • Četrti parameter MessageBoxIcon.Question pa označuje ikono v oknu (vprašaj).

Takole zgleda to sporočilno okno v delujočem programu: Več o sporočilnih oknih bomo izvedeli v naslednjem poglavju. Obrazec z imenom FClanskiObrazec moramo seveda še oblikovati. V glavnem meniju Visual C# izberemo opcijo Project, nato pa Add Windows Form. V oknu, ki se odpre, zapišimo ime obrazca FClanskiObrazec.cs in kliknimo gumb Add za dodajanje le tega v naš projekt. Obrazec sedaj oblikujmo takole:

Obrazcu smo priredili sliko za ozadje (C:\Program Files\Microsoft

Visual Studio 8\Microsoft Visual C# 2008 Express Edition – E<U\Logo.bmp) Gradnik CheckBox (ime gradnika cBKadilec). Gradnika TextBox (imeni gradnikov sta tBIme in tBPriimek) Gradnik ComboBox (Ime gradnika cBLetnik) Gradnik GroupBox (ime gradnika groupBox1). Znotraj tega gradnika sta dva radijska gumba (imeni radioButton1 in radioButton2) Gradnik CheckedListBox (ime gradnika cLBInteresne). Gradnik DateTimePicker (ime gradnika dTPRojen) Na obrazcu je tudi gradnik ContexMenuStrip (ime gradnika contexMenuStrip1) in končno na dnu obrazca še trije gumbi (Briši, Prekliči in Shrani). Gumb Briši je običajen gumb (lastnost

DialogResult je &one), gumba Prekliči in Shrani pa sta modalna (lastnosti DialogResult sta Cancel in OK). Kontekstnemu meniju priredimo eno samo postavko njegovega menija z napisom Briši vsebino, namen te postavke pa je, da če bo uporabnik to postavko aktiviral (z desnim klikom miškinega gumba nad enim od TextBox-ov), se bo dosedanja vsebina gradnika pobrisala.. Metoda, ki se izvede ob izbiri te opcije kontekstnega menija je takale: private void brišiVsebinoToolStripMenuItem_Click(object sender, EventArgs e) {

Page 156: PROŠNJA - uranic.tsckr.siuranic.tsckr.si/VISUAL C#/VISUAL C#.pdf · MICROSOFT VISUAL C# .NET Srečo Uranič . Visual C# . ... Za začetek novega projekta v okolju Visual C# Standard

Visual C# . NET

Algoritmi in programski jeziki – 4.letnik

156

//Preverimo kateri gradnik je imel kontrolo, ko smo kliknili desni gumb miške if (contextMenuStrip1.SourceControl.Equals(tBIme)) tBIme.Text = ""; else tBPriimek.Text = ""; }

Končno ta kontekstni meni priredimo gradnikoma tBIme in tBPriimek (to storimo tako, da obema gradnikoma za lastnost ContextMenuStrip izberimo contextMenuStrip1. Tako pri odpiranju tega obrazca, kot pri kliku na gumb Briši želimo nastaviti začetne vrednosti gradnikom na obrazcu. Da nam teh stavkov ne bo potrebno pisati dvakrat, bomo raje napisali svojo metodo za inicializacijo in le-to potem priredili obema dogodkoma. Vsebina te metode je npr. takale: public void Inicializacija() { tBIme.Text = ""; //izpraznimo vsebino gradnika tBIme tBPriimek.Text = ""; //izpraznimo vsebino gradnika tBPriimek cBLetnik.Items.Clear(); //izpraznimo vsebino gradnika cBLetnik cBLetnik.Items.Add("Prvi"); //v gradnik tBIme dodamo tekst"Prvi" cBLetnik.Items.Add("Drugi"); cBLetnik.Items.Add("Tretji"); cBLetnik.Items.Add("Četrti"); dTPRojen.Value = DateTime.Today; //nastavitev današnjega datuma v DateTimePicker cBKadilec.Checked = false; //privzeta vrednost gradnika naj bo <neizbrano> radioButton1.Checked = true; //privzeti spol naj bo ženski cLBInteresne.Items.Clear(); //izpraznimo vsebino gradnika cBInteresne cLBInteresne.Items.Add("Tenis"); //v gradnik cBInteresne dodamo opcijo "Tenis" cLBInteresne.Items.Add("Košarka"); cLBInteresne.Items.Add("Nogomet"); cLBInteresne.Items.Add("Planinarjenje"); cLBInteresne.Items.Add("Tek"); cLBInteresne.Items.Add("Kolesarstvo"); cLBInteresne.Items.Add("namizni tenis"); cLBInteresne.Items.Add("Filmi, TV, Radio"); }

To metodo sedaj priredimo dogodku Load obrazca in dogodku Click gumba Briši: private void FClanskiObrazec_Load(object sender, EventArgs e) { Inicializacija();//klic metode Inicializacija() ob zagonu obrazca } private void button3_Click(object sender, EventArgs e) { Inicializacija();//klic metode Inicializacija() ob kliku na gumb Briši }

Page 157: PROŠNJA - uranic.tsckr.siuranic.tsckr.si/VISUAL C#/VISUAL C#.pdf · MICROSOFT VISUAL C# .NET Srečo Uranič . Visual C# . ... Za začetek novega projekta v okolju Visual C# Standard

Visual C# . NET

Algoritmi in programski jeziki – 4.letnik

157

Sporočilna okna – okno MessageBox Modalno okno MessageBox.Show prikaže sporočilno okno v številnih variantah. To pogovorno okno smo v dosedanjih primerih že uporabljali, a v njegovi najenostavnejši obliki, z enim samim parametrom. Metoda Show je funkcija, ki za rezultat sporoči, kako je bilo okno zaprto. Funkcija ima kar 21 različnih variant, na tem mestu pa bodo prikazane le najpomembnejše izmed njih;

• MessageBox.Show (String) : Prikaz poljubnega teksta v sporočilnem oknu.

Primer:

MessageBox.Show("Osnovno sporočilno okno!");

• MessageBox.Show (String,String) : Prikaz poljubnega teksta v sporočilnem oknu in še poljubnega teksta v naslovni vrstici okna.

Primer: MessageBox.Show("Tekst znotraj okna!","Napis na oknu");

• MessageBox.Show (String,String,MessageBoxButtons) : Sporočilno okno s tekstom v oknu, napisom na oknu in odločitvenimi gumbi.

Primer:

MessageBox.Show("Kakšna je vaša odločitev", "Sporočilno okno s tremi gumbi",

MessageBoxButtons.YesNoCancel);

Tretji parameter (odločitveni gumbi v oknu) je parameter tipa MessageBoxButtons. To je v bistvu naštevni tip s konstantami, ki določajo kateri gumbi se bodo prikazali v sporočilnem oknu. Vse možne konstante, skupaj za razlago so zbrane v naslednji tabeli:

Oznake gumbov Razlaga

AbortRetryIgnore Sporočilno okno vsebuje gumbe Prekini, Poskusi znova, in Prezri.

OK Sporočilno okno vsebuje gumb V redu.

OKCancel Sporočilno okno vsebuje gumba V redu in Prekliči.

RetryCancel Sporočilno okno vsebuje gumba Poskusi znova, in Prekliči.

Page 158: PROŠNJA - uranic.tsckr.siuranic.tsckr.si/VISUAL C#/VISUAL C#.pdf · MICROSOFT VISUAL C# .NET Srečo Uranič . Visual C# . ... Za začetek novega projekta v okolju Visual C# Standard

Visual C# . NET

Algoritmi in programski jeziki – 4.letnik

158

Yes&o Sporočilno okno vsebuje gumba Da in Ne.

Yes&oCancel Sporočilno okno vsebuje gumbe Da, Ne in Prekliči.

Metoda Show vrne vrednost, ki je odvisna od uporabnikovega klika na določen gumb, od tega pa je seveda odvisno nadaljevanje aplikacije. Vse vrednosti, ki jih metoda lahko vrne, so sestavni del naštevnega tipa DialogResult, zbrane pa so v naslednji tabeli:

Vrnjena vrednost Razlaga

Abort Sporočilno okno vrne vrednost Abort (običajno je to pri kliku na gumb Abort - Prekini).

Cancel Sporočilno okno vrne vrednost Cancel (običajno je to pri kliku na gumb Cancel - Prekliči).

Ignore Sporočilno okno vrne vrednost Ignore (običajno je to pri kliku na gumb Ignore - Prezri).

&o Sporočilno okno vrne vrednost &o (običajno je to pri kliku na gumb No - Ne).

&one Sporočilno okno ne vrne ničesar. To hkrati pomeni, da modalno okno še kar teče.

OK Sporočilno okno vrne vrednost OK (običajno je to pri kliku na gumb OK – V redu).

Retry Sporočilno okno vrne vrednost Retry (običajno je to pri kliku na gumb Retry – Poskusi znova).

Yes Sporočilno okno vrne vrednost Yes (običajno je to pri kliku na gumb Yes - Da).

• MessageBox.Show (String,String,MessageBoxButtons,MessageBoxIcon) : Sporočilno okno s tekstom v oknu, napisom na oknu, odločitvenimi gumbi in vrsto ikone.

Primer:

MessageBox.Show("Kakšna je vaša odločitev", "Sporočilno okno s tremi gumbi in ikono",

MessageBoxButtons.AbortRetryIgnore, MessageBoxIcon.Question);

Četrti parameter (vrsta ikone v oknu) je parameter tipa MessageBoxIcon. To je v bistvu naštevni tip s konstantami, ki določajo katera ikona bo prikazala v sporočilnem oknu. Vse možne konstante, skupaj za razlago so zbrane v naslednji tabeli:

Oznaka ikone Razlaga

Asterisk Sporočilno okno vsebuje grafični simbol z malo črko i v sredini simbola.

Error Sporočilno okno vsebuje grafični simbol z znakom X sredini simbola na rdeči podlagi.

Exclamation Sporočilno okno vsebuje grafični simbol z znakom ! (klicaj) sredini simbola na rumeni podlagi.

Page 159: PROŠNJA - uranic.tsckr.siuranic.tsckr.si/VISUAL C#/VISUAL C#.pdf · MICROSOFT VISUAL C# .NET Srečo Uranič . Visual C# . ... Za začetek novega projekta v okolju Visual C# Standard

Visual C# . NET

Algoritmi in programski jeziki – 4.letnik

159

Hand Sporočilno okno vsebuje grafični simbol z znakom X sredini simbola na rdeči podlagi.

Information Sporočilno okno vsebuje grafični simbol z malo črko i v sredini simbola.

&one Sporočilno okno Ne vsebuje grafičnega simbola.

Question Sporočilno okno vsebuje grafični simbol z znakom ? (vprašaj) sredini simbola na modri podlagi.

Stop Sporočilno okno vsebuje grafični simbol z znakom X sredini simbola na rdeči podlagi.

Warning Sporočilno okno vsebuje grafični simbol z znakom ! (klicaj) sredini simbola na rumeni podlagi.

• MessageBox.Show (String , String , MessageBoxButtons , MessageBoxIcon , MessageBoxDefaultButton) : Sporočilno okno s tekstom v oknu, napisom na oknu, odločitvenimi gumbi, vrsto ikone in privzetim gumbom.

Primer: MessageBox.Show("Kakšna je vaša odločitev", "Sporočilno okno s tremi gumbi in ikono",

MessageBoxButtons.AbortRetryIgnore, MessageBoxIcon.Warning, MessageBoxDefaultButton.Button2);

Peti parameter (aktivni gumb v oknu) je parameter tipa MessageBoxDefaultButton. To je oznaka enega izmed treh možnih modalnih gumbov v sporočilnem oknu. Gumb, naveden kot peti parameter, bo izbrani gumb (ima focus), ko se pogovorno okno prikaže. Seznam vseh možnih gumbov, ki jih lahko zapišemo kot peti parameter je v naslednji tabeli:

Oznaka gumba Razlaga

Button1 Izbran (ima focus) bo prvi gumb v sporočilnem oknu.

Button2 Izbran (ima focus) bo drugi gumb v sporočilnem oknu.

Button3 Izbran (ima focus) bo tretji gumb v sporočilnem oknu.

• MessageBox.Show (String, String, MessageBoxButtons, MessageBoxIcon,

MessageBoxDefaultButton, MessageBoxOptions) : Sporočilno okno s tekstom v oknu, napisom na oknu, odločitvenimi gumbi, vrsto ikone, privzetim gumbom in opcijami.

Primer: MessageBox.Show("Kakšna je vaša odločitev", "Sporočilno okno s tremi gumbi in ikono",

MessageBoxButtons.AbortRetryIgnore, MessageBoxIcon.Warning, MessageBoxDefaultButton.Button2, MessageBoxOptions.RightAlign);

Page 160: PROŠNJA - uranic.tsckr.siuranic.tsckr.si/VISUAL C#/VISUAL C#.pdf · MICROSOFT VISUAL C# .NET Srečo Uranič . Visual C# . ... Za začetek novega projekta v okolju Visual C# Standard

Visual C# . NET

Algoritmi in programski jeziki – 4.letnik

160

Šesti parameter (opcije v oknu) je parameter tipa MessageBoxOptions. To je oznaka ene izmed štirih posebnih opcij, kako naj se prikaže sporočilo v sporočilnem oknu. Seznam vseh možnih opcij, ki jih lahko zapišemo kot šesti parameter je v naslednji tabeli:

Oznaka Parametra Razlaga

DefaultDesktopOnly Sporočilno okno je prikazano na aktivnem namizju.

RightAlign Tekst sporočila je desno poravnan.

RtlReading Opcija določa da je vsebina sporočilnega okna prikazana od desne proti levi (besedilo v oknu je na levi, ikona pa na desni.

Service&otification Sporočilno okno je prikazano na aktivnem namizju.

• MessageBox.Show (String , String,MessageBoxButtons , MessageBoxIcon , MessageBoxDefaultButton, MessageBoxOptions,Boolean) : Sporočilno okno s tekstom v oknu, napisom na oknu, odločitvenimi gumbi, vrsto ikone, privzetim gumbom, opcijami in dodatnim gumbom za pomoč.

Primer: DialogResult r = MessageBox.Show("Kakšna je vaša odločitev", "Sporočilno okno s tremi

gumbi in ikono", MessageBoxButtons.AbortRetryIgnore, MessageBoxIcon.Warning, MessageBoxDefaultButton.Button2, MessageBoxOptions.RtlReading, true);

//odstranimo obravnavo privzetega dogodka, ki je namenjen pomoči in ga npr. //nadomestimo z dogodkom ki je namenjen pomoči tekočega obrazca this.HelpRequested -= new

System.Windows.Forms.HelpEventHandler(this.Form1_HelpRequested);

Če uporabnik klikne gumb Pomoč, moramo pač uporabniku vnaprej pripraviti ustrezno pomoč – ob kliku na gumb Pomoč moramo najprej odstraniti obravnavo privzetega dogodka (this.HelpRequested -= new…), namenjenega pomoči in programsko napovedati, kateri dogodek naj se izvede. To smo storili s stavkom this.HelpRequested -= new

System.Windows.Forms.HelpEventHandler(this.Form1_HelpRequested);

Ob kliku na gumb Pomoč v sporočilnem oknu se v našem primeru torej izvedel vnaprej pripravljen dogodek Form1_HelpRequested, ki pripada obrazcu Form1.

� Opomba: Če se v funkciji MessageBox ne želimo navesti petega in šestega parametra, namesto njiju podamo vrednost 0.

• MessageBox.Show (String , String,MessageBoxButtons , MessageBoxIcon ,

MessageBoxDefaultButton, MessageBoxOptions, String) : Sporočilno okno s tekstom v oknu, napisom na oknu, odločitvenimi gumbi, vrsto ikone, privzetim gumbom, opcijami in stringom s katerim podamo ime datoteke s pomočjo.

Page 161: PROŠNJA - uranic.tsckr.siuranic.tsckr.si/VISUAL C#/VISUAL C#.pdf · MICROSOFT VISUAL C# .NET Srečo Uranič . Visual C# . ... Za začetek novega projekta v okolju Visual C# Standard

Visual C# . NET

Algoritmi in programski jeziki – 4.letnik

161

Primer: MessageBox.Show("Kakšna je vaša odločitev", "Sporočilno okno s tremi gumbi in ikono",

MessageBoxButtons.AbortRetryIgnore, MessageBoxIcon.Warning,0, 0, "mspaint.chm");

V oknu se bo prikazal dodatni gumb za napisom Pomoč. Ob kliku na ta gumb se odpre datoteka s pomočjo mspaint.chm (datoteka mora seveda obstajati). V zgornjem primeru smo namesto petega in šestega parametra uporabili kar 0, kar pomeni, da ta dva parametra ne upoštevamo oz. upoštevamo zanju privzete vrednosti.

• MessageBox.Show (String , String,MessageBoxButtons , MessageBoxIcon , MessageBoxDefaultButton, MessageBoxOptions, String, Help&avigator) : Sporočilno okno s tekstom v oknu, napisom na oknu, odločitvenimi gumbi, vrsto ikone, privzetim gumbom, opcijami, stringom s katerim podamo ime datoteke s pomočjo in parametrom s katerim povemo kateri element datoteke s pomočjo bo prikazan.

Primer: MessageBox.Show("Kakšna je vaša odločitev", "Sporočilno okno s tremi gumbi in ikono",

MessageBoxButtons.AbortRetryIgnore, MessageBoxIcon.Warning,0, 0, "mspaint.chm",HelpNavigator.Index);

V oknu se bo prikazal dodatni gumb z napisom Pomoč. Ob kliku na ta gumb se odpre datoteka s pomočjo mspaint.chm (datoteka mora seveda obstajati), parameter HelpNavigator.Index pa označuje, da bo aktiven zavihek v tem oknu zavihek Index.

Seznam vseh možnih opcij, ki jih lahko zapišemo za parameter HelpNavigator je v naslednji tabeli:

Oznaka parametra Razlaga

AssociateIndex Datoteka s pomočjo se odpre tako, da je le-ta prikazana povsem na začetku.

Find Datoteka s pomočjo se odpre tako, da bo aktiven zavihek v tem oknu zavihek Find.

Index Datoteka s pomočjo se odpre tako, da bo aktiven zavihek v tem oknu zavihek Index.

KeywordIndex Datoteka s pomočjo se odpre tako, da bo aktiven zavihek v tem oknu zavihek KeywordIndex, če le-ta obstaja.

TableOfContents Datoteka s pomočjo se odpre tako, da bo aktiven zavihek v tem oknu zavihek TableOfContents.

Topic Datoteka s pomočjo se odpre na določeni temi, če le ta obstaja.

TopicId Datoteka s pomočjo se odpre na določeni temi podani z številko te teme.

Page 162: PROŠNJA - uranic.tsckr.siuranic.tsckr.si/VISUAL C#/VISUAL C#.pdf · MICROSOFT VISUAL C# .NET Srečo Uranič . Visual C# . ... Za začetek novega projekta v okolju Visual C# Standard

Visual C# . NET

Algoritmi in programski jeziki – 4.letnik

162

• MessageBox.Show (String , String,MessageBoxButtons , MessageBoxIcon ,

MessageBoxDefaultButton, MessageBoxOptions, String, String) : Sporočilno okno s tekstom v oknu, napisom na oknu, odločitvenimi gumbi, vrsto ikone, privzetim gumbom, opcijami, stringom s katerim podamo ime datoteke s pomočjo in z imenom točno določene ključne besede znotraj datoteke s pomočjo.

Primer: MessageBox.Show("Kakšna je vaša odločitev", "Sporočilno okno s tremi gumbi in ikono",

MessageBoxButtons.AbortRetryIgnore, MessageBoxIcon.Warning,0, 0, "mspaint.chm",HelpNavigator.Index,"mspaint.chm::/paint_brush.htm");

V oknu se bo prikazal dodatni gumb z napisom Pomoč. Ob kliku na ta gumb se odpre datoteka s pomočjo mspaint.chm (datoteka mora seveda obstajati), prikazana tema pa ustreza zadnjemu navedenemu parametru v funkciji MessageBox.

Page 163: PROŠNJA - uranic.tsckr.siuranic.tsckr.si/VISUAL C#/VISUAL C#.pdf · MICROSOFT VISUAL C# .NET Srečo Uranič . Visual C# . ... Za začetek novega projekta v okolju Visual C# Standard

Visual C# . NET

Algoritmi in programski jeziki – 4.letnik

163

Delo z datotekami in podatkovnimi tokovi

Imenski prostor (knjižnjica) System.IO Imenski prostor System.IO vsebuje kopico razredov za delo z datotekami, za upravljanje z imeniki (direktoriji), datotekami in potmi do datotek. Razredi tega imenskega prostora pa vsebujejo cel kup metod za raznorazne vhodno-izhodne operacije, med katerimi so za nas najpomembnejše metode za kreiranje, zapisovanje, branje in na splošno manipuliranje s tekstovnimi in binarnimi datotekami.

Razredi imenskega prostora System.IO za delo z imeniki, datotekami in potmi do datotek

Razred Razlaga

Directory Uporablja se za kreiranje, urejanje, brisanje ali pridobivanje informacij o imenikih.

File Uporablja se za kreiranje, urejanje, brisanje ali za pridobivanje informacij o datotekah.

Path Uporablja se za pridobivanje informacij o poteh do datotek.

Vse te metode so statične metode, zaradi česar jih lahko kličemo direktno preko imena razreda in ne preko imen objektov. Pred uporabo kateregakoli od razredov imenskega prostora System.IO, je potrebna ta imenski prostor navesti v stavku using. using System.IO; //dodajanje imenskega prostora

Če tega stavka na začetku ne bi bilo, bi se morali na vse razrede imenskega prostora System.IO sklicevati preko tega imenskega prostora. Kratica IO v imenskem prostoru System.IO predstavlja vhodne (input) in izhodne (output) tokove (streams). S pomočjo njih lahko opravljamo želene operacije nad datotekami (npr. branje, pisanje).

&ajpomembnejše metode razreda Directory

Metoda Razlaga

Exists(pot) Vrne logično vrednost, ki ponazarja ali nek imenik obstaja ali ne.

CreateDirectory(pot) Kreira imenike v navedeni poti.

Delete(pot) Brisanje imenika in njegove vsebine.

GetFiles(pot) Pridobivanje imen datotek navedene poti

GetDirectories(pot) Pridobivanje imen map navedene poti

GetCreationTime(pot) Pridobivanje datuma kreiranja mape

GetDirectoryRoot(pot) Pridobivanje imena logične enote, na kateri se nahaja določena mapa

GetCurrentDirectory() Pridobivanje poti do tekoče mape

Imena datotek

Poimenovanje datoteke je odvisno od operacijskega sistema. Omejili se bomo na operacijske sisteme družine Windows. Vsaka datoteka ima ime in je v neki mapi (imeniku). Polno ime datoteke dobimo tako, da zložimo skupaj njeno ime (t.i. kratko ime) in mapo. Primer:

Page 164: PROŠNJA - uranic.tsckr.siuranic.tsckr.si/VISUAL C#/VISUAL C#.pdf · MICROSOFT VISUAL C# .NET Srečo Uranič . Visual C# . ... Za začetek novega projekta v okolju Visual C# Standard

Visual C# . NET

Algoritmi in programski jeziki – 4.letnik

164

D:\olimpijada\Peking\atletika.txt Če povemo z besedami: Ime datoteke je atletika.txt na enoti D:, v imeniku Peking, ki je v podimeniku imenika olimpijada. Imena imenikov so v operacijskem sistemu Windows ločena z znakom \. Znak \ v nekem stringu je v splošnem napoved neke t.i. escape (ubežne) sekvence (\n – nova vrstica, \t – Tabulator, \r – Return, \\ - Backslash, \" - dvojni narekovaj). Kadar pa želimo, da je znak \ sestavni del niza, moramo napisati kombinacijo \\. Spomnimo se še, da v jeziku C# znak \ v nizu lahko izgubi posebni pomen, kadar pred nizom uporabimo znak @. Primer uporabe: //Kreiranje novega imenika C# 2008 in v njem še podimenika datoteke string dir ="C: \\C# 2008\\Datoteke\\"; //ali //znak @ pred definicijo pomeni, da znak \ v stringu ne predstavlja escape sekvence string dir1 = @"C: \C# 2008\Datoteke\"; if (!Directory.Exists(dir)) //če imenik še ne obstaja, ga skreiramo Directory.CreateDirectory(dir); DateTime datum = Directory.GetCreationTime(@"c:\Windows"); MessageBox.Show(@"Mapa C:\Windows je bila kreirana " + datum.ToShortDateString()); //Ugotovimo, katere podmape vsebuje mapa Program Files string[] mape = Directory.GetDirectories(@"C:\Program Files"); foreach (string ime in mape) //izpišimo imena vseh datotek mape C:\Vaje C# MessageBox.Show(ime); string mapa = @"Program Files"; MessageBox.Show("Mapa '" + mapa + "' se nahaja na disku " + Directory.GetDirectoryRoot(@"c:\Program Files")); //ugotovimo in izpišimo celotno pot do tekoče mape string tekocamapa = Directory.GetCurrentDirectory(); MessageBox.Show(tekocamapa);//izpis tekoče mape

&ajpomembnejše metode razreda Path

Metoda Razlaga

GetFullPath(pot) Pridobivanje celotne poti do datoteke

string ime ="Datoteka.txt"; //celotno pot do datoteke dobimo s pomočjo metode GetFullPath razreda Path string celotnaPot = Path.GetFullPath(ime); MessageBox.Show(celotnaPot);//izpis celotne poti do datoteke

&ajpomembnejše metode razreda File

Metoda Razlaga

Exists(pot) Vrne logično vrednost, ki ponazarja ali neka datoteka obstaja ali ne.

Delete(pot) Brisanje datoteke.

Copy(source, dest) Kopiranje datoteke iz izvorne poti (source) do končne poti (dest).

Move(source, dest) Premik datoteke iz izvorne poti (source) do končne poti (dest).

CreateText(pot) Kreiranje ali odpiranje datoteke za pisanje. Parameter pot vsebuje pot do datoteke in njeno ime

OpenText(pot) Odpiranje obstoječe tekstovne datoteke za branje. Parameter pot vsebuje pot do datoteke in njeno ime

OpenRead(pot) Odpiranje obstoječe datoteke za branje. Parameter pot vsebuje pot do datoteke

Page 165: PROŠNJA - uranic.tsckr.siuranic.tsckr.si/VISUAL C#/VISUAL C#.pdf · MICROSOFT VISUAL C# .NET Srečo Uranič . Visual C# . ... Za začetek novega projekta v okolju Visual C# Standard

Visual C# . NET

Algoritmi in programski jeziki – 4.letnik

165

in njeno ime

OpenWrite(pot) Odpiranje obstoječe datoteke za pisanje. Parameter pot vsebuje pot do datoteke in njeno ime

AppendText(pot) Odpiranje obstoječe datoteke za pisanje in dodajanje teksta v to datoteko. Če datoteka še ne obstaja, se le-ta najprej ustvari. Parameter pot vsebuje pot do datoteke in njeno ime

ReadAllText(pot) Odpiranje obstoječe datoteke, branje vseh vrstic iz te datoteke, nato pa še zapiranje datoteke. Parameter pot vsebuje pot do datoteke in njeno ime

WriteAllText(pot,stavek) Kreiranje nove datoteke, zapis stavka v datoteko in zapiranje datoteke. Če datoteka s tem imenom že obstaja, bo prepisana.

WriteAllLines(pot,stavki) Kreiranje nove datoteke, zapis stavkov (npr. iz tabele stavkov) v datoteko in zapiranje datoteke. Če datoteka s tem imenom že obstaja, bo prepisana.

AppendAllText(pot,stavek) Zapis stavka v datoteko. Če datoteka še ne obstaja, bo skreirana nova.

Primer uporabe: string pot = dir + "Izdelki.txt"; //preverimo obstoj datoteke Izdelki.txt v navedenem imeniku (c:\C# 2008\Datoteke\) if (File.Exists(pot)) File.Delete(pot); //če datoteka obstaja, jo pobrišemo string stavek = "Danes je torek!"; //Kreirajmo NOVO datoteko, vanjo zapišimo string stavek in datoteko zaprimo! //POZOR: Če datoteka s tem imenom že obstaja, bo njena vsebina prepisana File.WriteAllText("Primer.txt", stavek); //Izpiši vsebino datoteke Primer.txt! string vsebinaDat = File.ReadAllText("Primer.txt");//vsebino shranimo v začasen string MessageBox.Show(vsebinaDat);//izpis vsebine začasnega stringa //V datoteko Primer.txt dodaj še stavek Jutri bo pa sreda File.AppendAllText("Primer.txt", "Jutri bo pa sreda!"); //ustvarimo tabelo stringov, jo inicializirajmo in prepišimo v datoteko string[] stavki = new string[5]; stavki[0] = "Danes je torek."; stavki[1] = "Ura je 13.30."; stavki[2] = "Učimo se enostavnega dela z datotekami."; stavki[3] = "Uporabljamo razred File."; stavki[4] = "Podatkovne tokove bomo obravnavali kasneje."; File.WriteAllLines("APJ.txt", stavki);//v datoteko zapišemo celo TABELO stringov

Delo s podatkovnimi tokovi Ko uporabljamo razrede imenskega prostora System.IO za delo z vhodno izhodnimi operacijami, lahko uporabljamo dve vrsti datotek: tekstovne datoteke in binarne datoteke. V tekstovnih datotekah so vsi podatki shranjeni kot tekstovni znaki, ali pa kot zaporedje znakov – stringi. Pogosto so posamezni sklopi znakov (besede, polja, …) med seboj ločeni s posebnimi ločili (npr znakom |, ali pa z znakom vejica ipd ), datoteke pa vsebujejo tudi znake end of line (znak za konec vrstice). Tekstovne datoteke

imajo torej vrstice. Izgled vsebine tekstovne datoteke odprte z Beležnico (v datoteki je ena sama vrstica).

Page 166: PROŠNJA - uranic.tsckr.siuranic.tsckr.si/VISUAL C#/VISUAL C#.pdf · MICROSOFT VISUAL C# .NET Srečo Uranič . Visual C# . ... Za začetek novega projekta v okolju Visual C# Standard

Visual C# . NET

Algoritmi in programski jeziki – 4.letnik

166

Tudi podatki v binarnih datotekah lahko vsebujejo znake, tako kot tekstovne, a so podatki med seboj ločeni s posebnimi znaki, zaradi česar je vsebina take datoteke, če jo odpremo z nekim editorjem, skoraj neberljiva. Poleg tega binarne datoteke ne vsebujejo vrstic. Izgled vsebine binarne datoteke, če jo odpremo z Beležnico

Tip datoteke Razlaga

Text – tekstovna datoteka

Tekstovna datoteka. Datoteka, ki lahko vsebuje le znake (podatkovni tip char) ali pa zaporedje znakov (stringe). Pogosto so le ti med seboj ločeni z ločili (mpr znakom |, znakom vejica ipd.

Binary – binarna datoteka

Binarna datoteka. Datoteka, ki vsebuje množico različnih podatkovnih tipov.

Za delo z vhodno-izhodnimi (I/O – Input/Output) operacijami s tekstovnimi in binarnimi datotekami, .&ET Framework uporablja tokove (streams). Tok (stream) si lahko predstavljamo kot pretakanje podatkov iz ene lokacije na drugo. Izhodni tok (output stream) si torej predstavljamo kot kot tok podatkov z internega pomnilnika aplikacije v datoteko na disku, vhodni tok (input stream) pa kot tok podatkov z diska v interni pomnilnik. Pri delu s tekstovnimi datotekami uporabljamo tekstovni tok podatkov (text stream), pri delu z binarnimi datotekami pa binarni tok (binary stream).

Podatkovni tok Razlaga

Text Uporablja se za prenos tekstovnih podatkov.

Binary Uporablja se za prenos binarnih podatkov.

V tem razdelku bodo prikazani razredi imenskega prostora System IO, ki jih uporabljamo za delo s tokovi in datotekami. Za kreiranje toka, ki nas poveže z datoteko, tako npr. uporabimo razred FileStream. Za branje podatkov iz datoteke preko tekstovnega toka uporabimo npr. razred StreamReader, za branje podatkov iz binarne datoteke preko binarnega toka pa razred BinaryReader.

Razred Razlaga

Stream Splošen podatkovni tok

FileStream Zagotavljanje dostopa do vhodnih in izhodnih datotek (podatkovni tok namenjen datotekam).

StreamReader Uporablja se za branje tekstovnih podatkov v podatkovni tok (npr. iz tekstovne datoteke).

StreamWriter Uporablja se za zapisovanje toka tekstovnih podatkov (npr. v tekstovno datoteko).

BinaryReader Uporablja se za branje binarnih podatkov v podatkovni tok (npr. iz binarne datoteke).

BinaryWriter Uporablja se za zapisovanje toka binarnih podatkov (npr. v binarno datoteko).

Razred Stream je osnovni razred za vse podatkovne tokove. Predstavljamo si ga lahko kot sekvenco/zaporedje zlogov (bytov), kot npr. datoteka, podatki vneseni preko tipkovnice, podatki, ki smo jih poslali na zaslon. Razred Stream torej omogoča splošen oz. enoličen pogled in obdelavo podatkov ne glede na njihov različen izvor, tako da se programerjem ni potrebno ukvarjati s posebnostmi operacijskega sistema in pripadajočo opremo. V binarne datoteke lahko shranimo prav vse vgrajene numerične podatkovne tipe, zaradi česar so binarne datoteke bolj primerne za aplikacije, ki operirajo z numeričnimi podatki. V nasprotju, pa so vsi numerični podatki v tekstovnih datotekah shranjeni kot zaporedje znakov, zaradi česar jih moramo, če jih hočemo uporabiti v aritmetičnih operacijah, spremeniti v numerične podatke.

Page 167: PROŠNJA - uranic.tsckr.siuranic.tsckr.si/VISUAL C#/VISUAL C#.pdf · MICROSOFT VISUAL C# .NET Srečo Uranič . Visual C# . ... Za začetek novega projekta v okolju Visual C# Standard

Visual C# . NET

Algoritmi in programski jeziki – 4.letnik

167

Ko shranimo nek tekst v tekstovno datoteko, lahko za to datoteko uporabimo poljubno končnico (ekstenzijo). Bolj naravno pa je (tako bomo delali tudi v nadaljevanju), da za tekstovne datoteke uporabimo končnico txt, za binarne datoteke pa končnico dat. Tako nam že same končnice datotek povedo, ali gre za tekstovne ali pa za binarne datoteke.

Enostavno delo s podatkovnimi tokovi Pisalni ali izhodni tok v jeziku C# predstavimo z objektom tipa StreamWriter. Bralni ali vhodni tok pa je objekt tipa StreamReader. S pomočjo teh dveh objektov in metod razreda File lahko izvajamo najosnovnejše operacije pri delu z datotekami. Datoteko lahko kreiramo, vanjo pišemo, dodajamo nove podatke, ali pa jo preberemo!

Ustvarjanje nove datoteke

Novo datoteko ustvarimo s pomočjo metode File.CreateText, ki ima za parameter ime datoteke (ta vključuje tudi pot do datoteke). string imeDatoteke = "MojaDatoteka"; //določimo ime datoteke if (!File.Exists(imeDatoteke)) //če datoteka s tem imenom še ne obstaja { File.CreateText(imeDatoteke); //ustvarimo novo datoteko } else MessageBox.Show("Datoteka s tem imenom že obstaja!");

Pisanje v tekstovno datoteko

Datoteko torej znamo ustvariti. A kako bi nanjo kaj zapisali? Kot vemo, lahko izpisujemo z metodo WriteLine (in z Write). Pisanje na datoteko je povsem podobna kot pisanje na konzolo (glej literaturo C#, Vhodni in izhodni stavki). Metoda File.CreateText(), ko ustvari datoteko, namreč vrne oznako tako imenovanega podatkovnega toka. To oznako shranimo v spremenljivko tipa StreamWriter. public static void Main(string[] args) { string imeDatoteke = "MojaDatoteka"; StreamWriter pisi; pisi = File.CreateText(imeDatoteke); pisi.WriteLine("Tale stavek bo zapisan na datoteko!"); }

Poženimo program in pokukajmo v imenik, kjer je nova datoteka. A glej! Datoteka sicer je tam, a je prazna. Zakaj? Vedno, ko nekaj počnemo z datotekami, jih je potrebno na koncu zapreti. To storimo z metodo Close. Če torej program spremenimo v public static void Main(string[] args) { string imeDatoteke = "MojaDatoteka"; StreamWriter pisi; pisi = File.CreateText(imeDatoteke); pisi.WriteLine("Tale stavek bo zapisan na datoteko!"); pisi.Close(); }

in si sedaj ogledamo datoteko, vidimo, da ni več dolga 0 zlogov. Če jo odpremo v najkoristnejšem programu Beležnici, bomo na njej našli omenjeni stavek. Če podatkovnega toka po pisanju podatkov ne zapremo, je možno, da v nastali datoteki manjka del vsebine, oziroma vsebine sploh ni. Namreč, da napisan program pospeši operacije z diskom, se vsebina ne zapiše takoj v datoteko na disku, ampak v vmesni polnilnik. Šele ko je ta poln, se celotna vsebina medpomnilnika zapiše na datoteko. Metoda Close v C# poskrbi, da je medpomnilnik izpraznjen tudi, če še ni povsem poln.

Page 168: PROŠNJA - uranic.tsckr.siuranic.tsckr.si/VISUAL C#/VISUAL C#.pdf · MICROSOFT VISUAL C# .NET Srečo Uranič . Visual C# . ... Za začetek novega projekta v okolju Visual C# Standard

Visual C# . NET

Algoritmi in programski jeziki – 4.letnik

168

Tekstovno datoteko lahko ustvarimo in vanjo zapisujemo podatke tudi na druge načine, npr. takole: //Ustvarimo novo datoteko: če datoteka s tem imenom že obstaja, bo prejšnja vsebina pobrisana StreamWriter pisi=new StreamWriter("Mojadatoteka.txt"); pisi.WriteLine("Danes je lep dan!"); pisi.Close();

Dodajanje podatkov v tekstovno datoteko

Za dodajanje podatkov v tekstovno datoteko uporabljamo metode File.AppendText(). Metoda vrne oznako podatkovnega toka, ki omogoča dodajane stavkov na konec tekstovne datoteke, katere ime podamo kot parameter te metode. To oznako shranimo v spremenljivko tipa StreamWriter. Če datoteka, v katero želimo podatke dodati, še ne obstaja, metoda AppendText ustvari novo datoteko. Še primer uporabe: public static void Main(string[] args) { string imeDatoteke = "MojaDatoteka"; StreamWriter pisi; pisi = File.AppendText(imeDatoteke); pisi.WriteLine("Tale stavek bo dodan v datoteko!"); pisi.WriteLine("Če datoteka še ne obstaja, se je ustvarila nova!"); pisi.Close(); }

Branje tekstovnih datotek

S tekstovnih datotek lahko beremo na tri načine: • beremo posamezne znake • beremo vrstice • preberemo celo datoteko hkrati

Branje posameznih znakov Posamezne znake beremo s pomočjo metode Read, ki vrne kodo znaka, ki ga preberemo. To kodo moramo seveda s pomočjo explicitne konverzije (casting) pretvoriti v ustrezen znak. StreamReader beri = File.OpenText("MojaDatoteka"); int koda; koda = beri.Read(); // s pomočjo metode Read beremo posamezne znake string vsebina = ""; // string vsebina je na začetku prazen while (koda != -1) // metoda Read vrne -1 ko pridemo do konca datoteke { vsebina=vsebina+((char)koda); // prebrane znake dodajamo stringu vsebina koda = beri.Read();// preberemo naslednji znak } beri.Close();// zapremo datoteko MessageBox.Show(vsebina); // vsebino datoteke izpišemo v sporočilnem oknu

Branje vrstic Cele vrstice beremo s pomočjo metode ReadLine. Če vrstice nismo uspeli prebrati, metoda vrne null – to pa lahko izkoristimo za testiranje konca datoteke. StreamReader beri = File.OpenText("MojaDatoteka"); string vrstica = beri.ReadLine();// skušamo prebrati prvo vrstico in jo shraniti v vrstica string vsebina = ""; // začetna vsebina datoteke while (vrstica != null) // dokler ni konec datoteke { vsebina=vsebina+vrstica+"\n"; // prebrano vrstico dodamo k vsebini in dodamo še znak za // konec vrstice vrstica = beri.ReadLine(); // skušamo prebrati naslednjo vrstico }

Page 169: PROŠNJA - uranic.tsckr.siuranic.tsckr.si/VISUAL C#/VISUAL C#.pdf · MICROSOFT VISUAL C# .NET Srečo Uranič . Visual C# . ... Za začetek novega projekta v okolju Visual C# Standard

Visual C# . NET

Algoritmi in programski jeziki – 4.letnik

169

beri.Close(); // na koncu datoteko zapremo MessageBox.Show(vsebina);

Tekstovno datoteko pa lahko odpremo za branje in jo obdelamo tudi takole: StreamReader beri = new StreamReader("MojaDatoteka.txt"); string vrstica = beri.ReadLine(); while (vrstica != null) // dokler ni konec datoteke { //obdelava vrstice, izpis na zaslon, … vrstica = beri.ReadLine(); } beri.Close();

Branje celotne datoteke naenkrat Celotno datoteko naenkrat preberemo s pomočjo metode ReadAllText razreda File, ali pa s pomočjo metode ReadToEnd objekta izpeljanega iz razreda StreamReader: // branje celotne vsebine datoteke s pomočjo metode ReadAllText razreda File string vsebina = File.ReadAllText("MojaDatoteka"); // branje celotne vsebine datoteke s pomočjo metode ReadToEnd razreda StreamReader StreamReader beri = File.OpenText("MojaDatoteka ");//najprej ustvarimo objekt tipa StremReader string vsebina1 = beri.ReadToEnd();// preberemo celotno vsebino datoteke beri.Close(); MessageBox.Show(vsebina+"\n"+vsebina1);

&apredno delo s podatkovnimi tokovi

Uporaba razreda FileStream

Za kreiranje podatkovnega toka, ki nas poveže z neko datoteko na disku, lahko uporabimo tudi razred FileStream. Sintaksa za kreiranje novega objekta razreda FileStream: FileStream fs = new FileStream(pot, mode [, access [, share]])

V prvem parametru povemo, kako se datoteka imenuje, lahko pa zapišemo tudi pot do te datoteke (imenike in podimenike), z drugim parametrom pa povemo, kako bomo to datoteko odprli (za dodajanje podatkov k obstoječim v datoteki, ali bomo kreirali novo datoteko, ali bomo datoteko le odprli, ipd.). Prva dva parametra (pot in način kreiranja – FileMode) sta obvezna, druga dva pa le opcijska. Če tretji parameter (access) ni naveden, lahko podatke v datoteko tako zapisujemo, kot tudi beremo iz nje. Za kodiranje argumentov mode , access in share uporabljamo zaporedoma naštevne tipe FileMode, FileAccess in FileShare. Če želimo npr. kreirati datoteko, ki še obstaja, bomo uporabili način FileMode.Create in tako kreirali novo datoteko. Če pa datoteka z imenom, ki smo jo navedli v prvem parametru (pot) že obstaja, bo njena vsebina prepisana z novo vsebino. Če pa seveda nočemo prepisati vsebine že obstoječe datoteke, bomo raje uporabili način FileMode.Create&ew. Naslednja tabela prikazuje vse možne načine odpiranja datoteke: Tabela načinov kreiranja datoteke - FileMode

&ačin kreiranja

Razlaga

Append Odpiranje datoteke, če le-ta obstaja in obenem se postavimo na njen konec. Če datoteka ne obstaja, se skreira nova. Ta način kreiranja datoteke lahko uporabimo le kadar želimo v datoteko pisati, ne pa tudi kadar želimo iz nje samo brati podatke.

Page 170: PROŠNJA - uranic.tsckr.siuranic.tsckr.si/VISUAL C#/VISUAL C#.pdf · MICROSOFT VISUAL C# .NET Srečo Uranič . Visual C# . ... Za začetek novega projekta v okolju Visual C# Standard

Visual C# . NET

Algoritmi in programski jeziki – 4.letnik

170

Create Kreiranje nove datoteke. Če datoteka že obstaja, bo njena vsebina prepisana.

Create&ew Kreiranje nove datoteke. Če datoteka že obstaja, pride do izjeme (exception).

Open Odpiranje že obstoječe datoteke. Če datoteka še ne obstaja, pride do izjeme (exception).

OpenOrCreate Odpiranje datoteke, če le ta obstaja, oziroma kreiranje nove datoteke, če le-ta še ne obstaja.

Truncate Odpiranje obstoječe datoteke in jo skrajšati (izprazniti), tako da je njena dolžina nič bytov.

Z drugim parametrom access povemo, ali bomo podatke iz datoteke brali, jih vanjo zapisovali ali pa oboje. Ta parameter lahko izpustimo, a v tem primeru bo vzeta privzeta vrednost – v datoteko bomo lahko podatke zapisovali in jih hkrati brali iz nje. Naslednja tabela opisuje vse tri možne načine manipulacije z neko datoteko: Tabela načinov manipulacije s podatki - FileAccess

&ačin manipulacije

Razlaga

Read Podatke iz datoteke lahko le beremo, ne pa tudi zapisujemo.

ReadWrite Podatke iz datoteke lahko beremo in tudi zapisujemo vanjo. Ta način je privzet.

Write Podatke lahko v datoteko le zapisujemo, ne pa tudi beremo.

S tretjim share argumentom povemo, ali bodo imeli dostop do te datoteke tudi drugi uporabniki in kakšne pravice za dostop bodo imeli. V naslednji tabeli so prikazani vsi možni načini: Tabela načinov porazdelitev (dostopa) podatkov z drugimi aplikacijami - FileShare

&ačin porazdelitve

Razlaga

&one Datoteko ne more odpreti nobena druga aplikacija.

Read Omogoča, da datoteko lahko odprejo tudi druge aplikacije, a le za branje.

ReadWrite Omogoča, da datoteko lahko odprejo tudi druge aplikacije in to za branje in pisanje.

Write Omogoča, da datoteko lahko odprejo tudi druge aplikacije, a le za branje.

Primer: Kreirajmo nov objekt izpeljan iz razreda FileStream in ga poimenujmo fs. Objektu smo s tretjim parametrom priredili le možnost pisanja v datoteko. string pot = @"C: \C# 2008\Datoteke\Izdelki.txt"; FileStream fs = new FileStream(pot, FileMode.Create, FileAccess.Write);

V primeru smo uporabili metodo FileMode.Create za kreiranje nove datoteke (oz. za prepis vsebine že obstoječe datoteke). Če pa smo v prvem parametru pot uporabili pot, ki ne obstaja (npr. navedli smo neobstoječi imenik), bo prišlo do izjeme tipa Directory&otFoundException. (več o izjemah je razloženo v poglavju Varovalni bloki – obravnava izjem). Kreirajmo nov objekt izpeljan iz razreda FileStream in ga poimenujmo fs. Objektu smo s tretjim parametrom priredili le možnost branja v datoteko. Tudi v tem primeru, tako kot v prejšnjem, smo uporabili metodo FileMode.Create za kreiranje nove datoteke (oz. za prepis vsebine že obstoječe datoteke). Velja tako kot za prvi primer: če smo v prvem parametru pot uporabili pot, ki ne obstaja (npr. navedli neobstoječi imenik), bo prišlo do izjeme tipa Directory&otFoundException. string pot = @"C: \C# 2008\Datoteke\Izdelki.txt"; FileStream fs = new FileStream(pot, FileMode.Create, FileAccess.Read);

Page 171: PROŠNJA - uranic.tsckr.siuranic.tsckr.si/VISUAL C#/VISUAL C#.pdf · MICROSOFT VISUAL C# .NET Srečo Uranič . Visual C# . ... Za začetek novega projekta v okolju Visual C# Standard

Visual C# . NET

Algoritmi in programski jeziki – 4.letnik

171

&ajpogostejše metode razreda FileStream

Metoda Razlaga

Close( ) Zapiranje podatkovnega toka in sproščanje pomnilnika za vse vire vezane na ta tok.

Seek( ) Nastavitev trenutnega položaja potkovnega toka na določeno vrednost

Flush( ) Izpraznitev podatkovnega toka in dokončen zapis vseh podatkov iz toka npr. na disk

Delo s tekstovnimi datotekami Za branje in pisanje podatkov v tekstovne datoteke uporabljamo razreda StreamReader in StreamWriter.

Pisanje podatkov v tekstovno datoteko

Za pisanje podatkov v tekstovno datoteko uporabljamo metodi Write in WriteLine ki pripadata razredu StreamWriter. Pri uporabi metode WriteLine je v datoteko avtomatsko dodan še znak za konec tekoče vrstice. V kolikor v datoteko shranjujemo posamezne podatke ( in ne cele stavke), lahko le-te med seboj ločimo z ustreznim ločilom (npr znakom | ). Da lahko pričnemo s pisanjem podatkov v tekstovno datoteko, moramo najprej ustvariti nov objekt tipa StreamWriter. To storimo s stavkom: StreamWriter textOut=new StreamWriter(podatkovni_tok);

Pri tem je podatkovni_tok objekt (spremenljivka) tipa FileStream, ki smo ga ustvarili že prej ( npr. s stavkom FileStream podatkovni_tok = new FileStream(@"C:\APJ\Vaja.txt", FileMode.Create);

Obstaja pa seveda tudi krajši način vzpostavljanja podatkovnega toka za pisanje v neko tekstovno datoteko. string datoteka = @"D:\APJ\Vaja.txt"; //ime datoteke, ki jo želimo ustvariti in vanjo pisati StreamWriter textOut = new StreamWriter(datoteka); /*ker smo uporabili PRIVZETI način za odpiranje nove datoteke, bo stara vsebina datoteke prepisana z novo vsebino, ne glede na prejšnjo vsebino datoteke!!!!*/

Nastavitve so v tem primeru torej privzete – ustvarila se bo nova datoteka (ne glede na to ali datoteka s tem imenom že obstaja), v to datoteko bomo pisali, če pa datoteka s tem imenom že obstaja, bo njena vsebina prepisana z novo vsebino brez opozorila!)

Metode razreda StreamWriter

Razlaga

Write(podatki) Zapiše podatke v izhodni tok.

WriteLine(podatki) Zapiše podatke v izhodni tok in na koncu doda še znak za konec vrstice (običajno znaka \r\n – carriage return in line feed).

Close( ) Zapre objekt tipa StremWriter in pripadajoči objekt FileStream.

Kot primer uporabe napišimo kodo, ki v tekstovno datoteko zapiše eno samo vrstico s tremi podatki, ki so med seboj ločeni z ločilom |. //Najprej deklariramo string, ki označuje pot do datoteke in njeno ime. //Pot (imenik in podimenik) MORA že obstajati, sicer pride do napake string pot = @"C: \C# 2007\Datoteke\Izdelki.txt";

Page 172: PROŠNJA - uranic.tsckr.siuranic.tsckr.si/VISUAL C#/VISUAL C#.pdf · MICROSOFT VISUAL C# .NET Srečo Uranič . Visual C# . ... Za začetek novega projekta v okolju Visual C# Standard

Visual C# . NET

Algoritmi in programski jeziki – 4.letnik

172

//dinamično kreiramo podatkovni tok: napovemo pot in ime datoteke, način kako jo bomo kreirali //FileMode.create) in kakšna bo manipulacija s podatki (FileAccess.write) FileStream fs=new FileStream(pot,FileMode.Create,FileAccess.Write); //dinamično kreiramo nov objekt tipa StreamWrite za zapisovanje v podatkovni tok - datoteko StreamWriter textOut=new StreamWriter(fs); textOut.Write("Kolo"+"|"); //zapis prvega podatka v datoteko, za njim pa še ločilnega znaka | //lahko bi zapisali tudi textOut.Write("Scott|"); textOut.Write("Scott"+"|"); int komadov = 20; textOut.Write(komadov + "|"); //enakovredno kot textOut.Write(komadov.ToString() + "|"); //podatek 220000 je sicer numeričen, a zaradi avtomat. konverzije v string ne pride do napake textOut.WriteLine(220000); //enakovreden zapis zadnjega stavka bi bil tudi textOut.WriteLine("220000"); textOut.Close(); //zapiranje podatkovnega toka in s tem datoteke fs.Close(); //zapiranje podatkovnega toka da bo datoteka na voljo drugim uporabnikom

V zgornjem primeru smo v tekstovno datoteko zapisali tudi numerična podatka. Pri vpisovanju pride do avtomatske konverzije numeričnega podatka v string, zaradi česar dodatna uporaba metode ToString() ni potrebna. Za konverzijo poskrbi kar sama metoda Write oz. WriteLine.

Branje podatkov iz tekstovne datoteke

Za branje podatkov iz tekstovne datoteke je na voljo več metod razreda StreamReader, najpomembnejši pa sta metodi Read in ReadLine. Da lahko pričnemo z branjem podatkov iz tekstovne datoteke, moramo najprej ustvariti nov objekt tipa StreamReader. To storimo npr. takole: string datoteka = @"C: \Tekstovna\Padavine.txt"; //ime in pot do datoteke //najprej ustvarimo objekt za povezavo z datoteko na disku. Uporabimo opcijo Open. FileStream podatkovni_tok = new FileStream(datoteka, FileMode.Open); //kreiramo nov objekt tipa StreamReader za branje v podatkovni tok StreamReader textIn = new StreamReader(podatkovni_tok);

Obstaja pa tudi krajši način za odpiranje datoteke za branje, brez predhodnega kreiranja objekta tipa FileStream. V tem primeru so nastavitve ob odpiranju datoteke privzete (datoteko odpremo za branje, vsebina datoteke pa bo drugim uporabnikom nedostopna vse dokler je ne bomo zaprli). string datoteka = @"C: \Tekstovna\Padavine.txt"; //ime in pot do datoteke StreamReader textIn = new StreamReader(datoteka); //privzeto odpiranje datoteke za branje

Metode razreda StreamReader

Razlaga

Peek( ) Vrne naslednji razpoložljivi znak v vhodni tok, brez premika na naslednjo pozicijo (naslednji znak). Če ni na voljo nobenega znaka več, metoda vrne vrednost -1.

EndOfStream() Metoda vrne true, če smo že na koncu podatkovnega toka, sicer pa vrne vrednost false.

Read( ) Bere naslednji razpoložljivi znak z vhodnega toka.

ReadLine( ) Bere naslednjo vrstico podatkov z vhodnega toka in jo vrne kot string.

ReadToEnd( ) Bere podatke s trenutne pozicije v vhodnem toku, vse do konca toka in podatke vrne kot string. Navadno se ta metoda uporablja za branje celotne vsebine datoteke.

Close( ) Zapre objekt tipa StremReader in pripadajoči objekt FileStream.

Lastnost Razlaga

Page 173: PROŠNJA - uranic.tsckr.siuranic.tsckr.si/VISUAL C#/VISUAL C#.pdf · MICROSOFT VISUAL C# .NET Srečo Uranič . Visual C# . ... Za začetek novega projekta v okolju Visual C# Standard

Visual C# . NET

Algoritmi in programski jeziki – 4.letnik

173

EndOfStream Pridobivanje vrednosti ki nam pove, ali smo že na koncu podatkovnega toka. V tem primeru je lastnost enaka true, sicer pa false.

V naslednjem primeru bomo prebrali podatke iz prej kreiranje tekstovne datoteke Izdelki.txt. Podatke bomo shranili v dinamično tabelo izdelkov. Vsak izdelek je objekt razreda Izdelek, ki ga moramo seveda najprej deklarirati. Deklariramo ga seveda izven vseh metod, a znotraj imenskega prostora, ali pa znotraj razreda, ki pripada obrazcu, ki ga trenutno obdelujemo. public class Izdelek { public string naziv; public string proizvajalec; public int komadov; public decimal cena; public Izdelek() //konstruktor { } }

Še koda za branje podatkov iz datoteke in zapis v tabelo objektov tipa Izdelek: string pot = @"C: \C# 2007\Datoteke\Izdelki.txt"; FileStream fs=new FileStream(pot,FileMode.OpenOrCreate,FileAccess.Read); StreamReader textIn = new StreamReader(fs); //enodimenzionalna tabela objektov tipa izdelek Izdelek [] tabelaizdelkov=new Izdelek[10]; int indeks = 0;//zaporedna štev. izdelka in hkrati zaporedna vrstica v tabeli tabelaizdelkov while (textIn.Peek() != -1) //iz datoteke beremo podatke dokler jih ne zmanjka /*Lahko bi zapisali tudi:

while (!textIn.EndOfStream) //dokler NI konec toka {…} ali pa: string vrstica; //dokler metoda ReadLine vrača vrednost različno od null

while ((vrstica = branje.ReadLine()) != null) {…} */ { string vrstica = textIn.ReadLine();//preberemo celo vrstico //v tabelo stolpci zaporedoma zložimo zaporedja znakov med ločili | string[] stolpci = vrstica.Split('|'); Izdelek Izd = new Izdelek();//nov objekt tipa Izdelek //objektu izd, ki je izplejan iz razreda Izdelek priredimo vrednosti, ki smo jih izluščili //iz prebrane vrstice. To nam je uspelo zato, ker so bili podatki ločeni z ločilom | Izd.naziv = stolpci[0]; Izd.proizvajalec = stolpci[1]; Izd.komadov = Convert.ToInt32(stolpci[2]); Izd.cena = Convert.ToDecimal(stolpci[3]); tabelaizdelkov[indeks] = Izd;//objekt izd zapišemo v tabelaizdelkov z ustreznim indeksom indeks++; //povečamo indeks }

Vaja: V naslednji vaji je prikazana uporaba metode Seek razreda Filestream, ki omogoča, da se premikamo po podatkovnem toku. Metoda ima dva parametra. S prvim parametrom povemo, kolikšen naj bo relativni odmik (offset) od pozicije, ki jo določimo z drugim parametrom. Drugi parameter (origin) določa, ali želimo odmik (ki je podan s prvim parametrom) izvesti od začetka podatkovnega toka, od konca podatkovnega toka ali pa od

Page 174: PROŠNJA - uranic.tsckr.siuranic.tsckr.si/VISUAL C#/VISUAL C#.pdf · MICROSOFT VISUAL C# .NET Srečo Uranič . Visual C# . ... Za začetek novega projekta v okolju Visual C# Standard

Visual C# . NET

Algoritmi in programski jeziki – 4.letnik

174

trenutne pozicije. Izberemo lahko torej eno izmed treh vrednosti, ki pripadajo naštevnemu tipu SeekOrigin, ki ima tri vrednosti: SeekOrigin.Begin, SeekOrigin.Current in SeekOrigin.End. /*V Tekstovno datoteko zapišimo 10 naključnih celih števil. S pomočjo metode Seek se nato postavimo na zečetek toka in preberemo zapisane podatke*/ string datoteka = "Stevila.txt"; FileStream fs = new FileStream(datoteka, FileMode.OpenOrCreate,FileAccess.ReadWrite); StreamWriter textOut = new StreamWriter(fs); //Tekstovni podatkovni tok Random naklj = new Random(); //generator naključnih števil //v datoteko zapišemo 10 celih števil for (int i = 0; i < 10; i++) { int stevilo = naklj.Next(0, 101); textOut.WriteLine(stevilo); //zapis v tekstovno datoteko }

//Poskrbimo za fizičen zapis podatkov v toku na disk: podatki se namreč //dokončno zapišejo na disk šele ko zapremo podatkovni tok, ali pa ko //uporabimo metodo Flush() textOut.Flush(); //Fizičen zapis podatkov v toku na disk!!! StreamReader textIn = new StreamReader(fs);

//z metodo Seek se postavimo na začetek toka fs - SeekOrigin.Begin (TA JE //OSTAL ODPRT), offset(odmik) od začetka pa je enak 0. fs.Seek(0, SeekOrigin.Begin);

//while (!textIn.EndOfStream) - while zanke NE moremo uporabiti, ker //dejansko šele metoda close "zapiše" KONEC datoteke string vrstice= ""; for (int i = 0; i < 10; i++) { st = textIn.ReadLine();//preberemo vrstico vrstice+=st; //in jo dodamo k dosedanjim vrsticam } //string vrstice lahko npr. prikažemo v sporočilnem oknu! textOut.Close(); //zapremo podatkovne tokove textIn.Close(); fs.Close();

Delo z binarnimi datotekami Za branje in pisanje podatkov v tekstovne datoteke uporabljamo razreda BinaryReader in BinaryWriter.

Pisanje podatkov v binarno datoteko

Za pisanje podatkov v binarno datoteko uporabljamo metodo Write, ki pripada razredu BinaryWriter. Da lahko pričnemo s pisanjem podatkov v binarno datoteko, moramo najprej ustvariti nov objekt tipa BinaryWriter To storimo npr. takole: string datoteka = @"C: \Tekstovna\Padavine.dat"; //ime in pot do datoteke //povezava z datoteko na disku FileStream podatkovni_tok = new FileStream(datoteka, FileMode.Create, FileAccess.Write); //Kreiramo podatkovni tok za pisanje BinaryWriter binaryOut = new BinaryWriter(podatkovni_tok);

Krajši način, takšen kot smo ga spoznali pri tekstovnih datotekah, pri pisanju v binarno datoteko &E obstaja!!!

Metode razreda BinaryWriter

Razlaga

Page 175: PROŠNJA - uranic.tsckr.siuranic.tsckr.si/VISUAL C#/VISUAL C#.pdf · MICROSOFT VISUAL C# .NET Srečo Uranič . Visual C# . ... Za začetek novega projekta v okolju Visual C# Standard

Visual C# . NET

Algoritmi in programski jeziki – 4.letnik

175

Write(podatki) Zapiše podatke v izhodni tok.

Close() Zapre objekt tipa BinaryWriter in pripadajoči objekt tipa FileStream.

V naslednjem primeru smo uporabili objekt razreda Izdelek, ki smo ga deklarirali v prejšnjem primeru. Podatke zapisujemo v datoteko z metodo Write. Ta pred zapisovanjem najprej preveri tip podatka, ki ga želimo zapisati in nato ta TIP podatka zapiše v datoteko takšnega kot je. Če torej metodi Write posredujemo podatek, ki je tipa decimal, metoda tega podatka ne bo spremenila v string, ampak bo v datoteko zapisala decimalni podatek. Ker smo uporabili metodo FileMode.Create bodo tekoči podatki prepisali prejšnje, če le-ti seveda obstajajo. //določimo pot in ime datoteke string pot = @"C: \C# 2007\Datoteke\Izdelki.dat"; //dinamično kreiramo nov podatkovni tok FileStream fs=new FileStream(pot,FileMode.Create,FileAccess.Write); //dinamično kreiramo nov objekt tipa BinaryWriter BinaryWriter binaryOut = new BinaryWriter(fs); Izdelek Izd = new Izdelek();//nov objekt tipa Izdelek //določimo vrednosti članom razreda. Seveda bi lahko te podatke vnesel uporabnik, npr. v //gradnike TextBox nekega obrazca Izd.naziv = "Kolo"; ; Izd.proizvajalec = "Scott"; Izd.komadov = 110; Izd.cena =220000.00m; //podatke zapišemo v binarno datoteko /*POZOR: vpisujemo vsak podatek POSEBEJ!!!!! Le tako bomo lahko kasneje podatke iz datoteke uspešno prebrali*/ binaryOut.Write(Izd.naziv); binaryOut.Write(Izd.proizvajalec); binaryOut.Write(Izd.komadov); binaryOut.Write(Izd.cena); //zapremo tok podatkov in s tem tudi datoteko binaryOut.Close(); fs.Close();

Branje podatkov iz binarne datoteke

Za branje podatkov iz binarne datoteke uporabljamo metode, ki pripadajo razredu BinaryWriter. Da lahko pričnemo z branjem podatkov iz binarne datoteke, moramo najprej ustvariti nov objekt tipa BinaryReader. To storimo npr. takole: string datoteka = @"C: \Tekstovna\Padavine.dat"; //ime in pot do datoteke //povezava z datoteko na disku FileStream podatkovni_tok = new FileStream(datoteka, FileMode.Open); //Kreiramo podatkovni tok za branje BinaryReader binaryIn = new BinaryReader(podatkovni_tok);

Razred BinaryReader vsebuje kar nekaj metod, ki jih lahko uporabimo pri obdelavi oz. branju binarne datoteke

Metode razreda BinaryReader

Razlaga

PeekChar( ) Vrne naslednji razpoložljivi znak v vhodni tok, brez premika na naslednjo pozicijo (naslednji znak). Če ni na voljo nobenega znaka več, metoda vrne vrednost -1.

Read( ) Vrne naslednji razpoložljivi znak iz vhodnega toka in napreduje na novo pozicijo v datoteki.

ReadBoolean() Vrne logično vrednost z vhodnega toka in napreduje naprej od trenutne pozicije v podatkovnem toku za en byte.

ReadByte( ) Vrne byte iz vhodnega toka in ustrezno napreduje naprej od trenutne pozicije v

Page 176: PROŠNJA - uranic.tsckr.siuranic.tsckr.si/VISUAL C#/VISUAL C#.pdf · MICROSOFT VISUAL C# .NET Srečo Uranič . Visual C# . ... Za začetek novega projekta v okolju Visual C# Standard

Visual C# . NET

Algoritmi in programski jeziki – 4.letnik

176

podatkovnem toku.

ReadChar( ) Vrne znak iz vhodnega toka in ustrezno napreduje naprej od trenutne pozicije v podatkovnem toku.

ReadDecimal( ) Vrne decimalno vrednost iz vhodnega toka in napreduje naprej od trenutne pozicije v podatkovnem toku za 16 bytov.

ReadInt32( ) Vrne 4 byte dolgo predznačeno celo število iz vhodnega toka in napreduje naprej od trenutne pozicije v podatkovnem toku za 4 byte.

ReadString( ) Vrne string iz vhodnega toka in napreduje naprej od trenutne pozicije v podatkovnem toku za toliko, kot je skupno število znakov v tem stringu.

Close( ) Zapre objekt BinaryReader in pripadajoči objekt FileStrem.

Primer: Datoteko, ki smo jo kreirali v prejšnjem primeru bomo sedaj odprli za branje in jo prebrali. Prebrane podatke bomo prikazali v sporočilnem oknu! //določimo pot in ime datoteke string pot = @"C: \C# 2007\Datoteke\Izdelki.dat"; //kreiramo nov podatkovni tok za našo datoteko; če datoteke še ne obstaja se zgradi nova FileStream fs = new FileStream(pot, FileMode.OpenOrCreate, FileAccess.Read); //dinamično kreiramo nov objekt tipa BinaryReader BinaryReader binaryIn = new BinaryReader(fs); while (binaryIn.PeekChar() != -1) //iz datoteke beremo podatke dokler jih ne zmanjka { Izdelek Izd = new Izdelek();//nov objekt tipa Izdelek Izd.naziv = binaryIn.ReadString();//podatek o nazivu preberemo kot string Izd.proizvajalec = binaryIn.ReadString();//podatek o proizvajalcu preberemo kot string Izd.komadov = binaryIn.ReadInt32();//podatek o komadih preberemo kot CELO ŠTEVLO Izd.cena = binaryIn.ReadDecimal();//podatek o ceni preberemo kot DECIMALNO ŠTEVLO //prebrane podatke prikažemo v sporočilnem oknu MessageBox.Show("Izdelek: " + Izd.naziv + "\nProizvajalec: " + Izd.proizvajalec + "\nKomadov: " + Izd.komadov + "\nCena: " + Izd.cena); } //zapremo tok podatkov in s tem tudi datoteko binaryIn.Close();

Metoda PeekChar je uporabna le tedaj, kadar pri branju binarne datoteke pričakujemo, da je na vrsti nek znak. Kadar pa tega ne vemo, je metoda neuporabna (npr. bri branju binarne datoteke, v kateri so sama decimalna števila). V takem primeru raje uporabimo varovalni blok in while zanko. Primer: Random naklj=new Random(); //Kreiranje binarne datoteke FileStream fs=new FileStream("Stevila.bin",FileMode.Create); //podatkovni tok fs BinaryWriter pisi=new BinaryWriter(fs); //fs bo namenjen za pisanje //v datoteko zapišemo 1000 naključnih decimalnih števil med 0 in 1000 for (int i = 0; i < 1000; i++) pisi.Write(naklj.NextDouble() * 1000); pisi.Close(); //Obdelava datoteke fs = new FileStream("Stevila.bin", FileMode.Open); //podatkovni tok fs BinaryReader beri = new BinaryReader(fs); //fs bo namenjen za branje double vsota = 0; int n = 0; //while (beri.PeekChar() != -1) //metoda PeekChar ne pride v poštev, ker so v datoteki števila try {

Page 177: PROŠNJA - uranic.tsckr.siuranic.tsckr.si/VISUAL C#/VISUAL C#.pdf · MICROSOFT VISUAL C# .NET Srečo Uranič . Visual C# . ... Za začetek novega projekta v okolju Visual C# Standard

Visual C# . NET

Algoritmi in programski jeziki – 4.letnik

177

//dokler bo branje uspešno, se vrstimo v zanki, nato pa se program nadaljuje v catch //bloku - ker pa je ta prazen, se program nadaljuje za varovalnim blokom! while (true) { vsota = vsota + beri.ReadDouble();//skušamo prebrati naslednje število in ga prištejemo k vsoti n++; } } catch { } beri.Close(); fs.Close(); MessageBox.Show("Vsota "+n.ToString()+" števil v datoteki: " + vsota.ToString());

Včasih pa želimo binarno datoteko le prepisati, oz. narediti njeno kopijo. V takih primerih nas sestava binarne datoteke sploh ne zanima in iz datoteke le beremo posamezne zloge (byte). Primer: FileStream inputStream = File.OpenRead("Stevila.bin");//podatkovni tok za branje Stream outputStream = File.OpenWrite("Stevila1.bak");//podatkovni tok za pisanje //kreiramo začasno tabelo za shranjevanje prebranih zlogov byte[] buffer = new Byte[1024]; int bytesRead; string vsebina = ""; //prebrane zloge zapisujemo v izhodni tok vse dokler metoda read vrača prebrane zloge while ((bytesRead = inputStream.Read(buffer, 0, 1024)) > 0) { outputStream.Write(buffer, 0, bytesRead);//prebrane zloge zapišemo v datoteko Stevila1.bak for (int i = 0; i < buffer.Length; i++) //prebrane zloge shranjujemo še v začasen string vsebina = vsebina + (char)buffer[i]; } MessageBox.Show(vsebina);//izpišemo celotno vsebino //datoteke: ker gre za binarno datoteko, je vsebina //seveda neberljiva inputStream.Close(); outputStream.Close( );

Vaja: Projekt Izdelki operira s tekstovno datoteko, podatke iz nje prenese v gradnik ListBox, kjer jih potem lahko dodajamo, urejamo, brišemo, obdelujemo in zapišemo nazaj v datoteko. Obrazec zgleda takole: Ime obrazca je FIzdelki, lastnost FormBorderStyle je nastavljena na FixedToolWindow.

Gradnik ListBox se nahaja na gradniku Panel, imenovanem panel1. Gradnik panel1 ima lastnost Dock nastavljeno na Fill. Ime gradnika ListBox je privzeto – listBox1.

Vsi gumbi so na gradniku Panel imenovanem panel2. Gradnik panel2 ima lastnost Dock nastavljeno na Bottom.

Najprej deklarirajmo dve globalni spremenljivki/objekta, saj ju bomo potrebovali v več metodah. string pot = @"Izdelki.txt"; //ime tekstovne datoteke FileStream fs = null; //začetna vrednost podatkovnega toka je null

Page 178: PROŠNJA - uranic.tsckr.siuranic.tsckr.si/VISUAL C#/VISUAL C#.pdf · MICROSOFT VISUAL C# .NET Srečo Uranič . Visual C# . ... Za začetek novega projekta v okolju Visual C# Standard

Visual C# . NET

Algoritmi in programski jeziki – 4.letnik

178

Ob zagonu projekta naj se v gradnik ListBox prenese vsebina datoteke, če pa datoteka še ne obstaja, se kreira nova, prazna datoteka. private void FIzdelki_Load(object sender, EventArgs e) { try { //datoteka bo v mapi, v kateri je tudi izvršilna (exe) datoteka string dir = Path.GetFullPath(pot); //datoteko skušamo odpreti za branje, če pa še ne obstaja, jo skreiramo fs = new FileStream(dir, FileMode.OpenOrCreate); //deklariramo še nov objekt za branje podatkov iz datoteke StreamReader textIn = new StreamReader(fs); while (textIn.Peek() != -1) //iz datoteke beremo podatke dokler jih ne zmanjka { string vrstica = textIn.ReadLine();//preberemo celo vrstico listBox1.Items.Add(vrstica); //in jo zapišemo v gradnik ListBox } } catch { MessageBox.Show("Napaka pri kreiranju oz. odpiranju datoteke", "Napaka"); } finally { fs.Close(); } }

Koda, ki ustreza posameznim gumbom: Gumb Dodaj: Ob kliku na gumb Dodaj se najprej odpre modalni obrazec z imenom Fizdelek. Na obrazcu so poleg label še trije gradniki tip TextBox ( ntB&aziv, tBProizvajelc in tBCena), ter en gradnik tipa &umericUpDown – imenuje nUDKomadov. Gumba Prekliči in Shrani sta modalna (prvi vrne vrednost Cancel, drugi pa OK).

Gradniku tBCena smo priredili še dogodek KeyPress, kjer določimo, da lahko uporabnik vnaša le cifre in decimalno vejico.

private void tBCena_KeyPress(object sender, KeyPressEventArgs e) { //dovolimo le vnos cifer in decimalne vejic, dovolimo pa tudi brisanje že vnesenih znakov if (((e.KeyChar < '0')||(e.KeyChar > '9')) && (e.KeyChar != ',') && (e.KeyChar != (char)(8))) e.Handled = true; }

Ob kliku na gumb Shrani se izvede še metoda Click tega gumba, v kateri preverimo uporabnikove vnose. private void button1_Click(object sender, EventArgs e) { //še nekaj kontrol uporabnikovega vnosa if (nUDKomadov.Value == 0) { MessageBox.Show("Število komadov ne sme biti 0!"); DialogResult = DialogResult.None;//prekličemo zapiranje obrazca } else if (((tBCena.Text).Trim()) == "") { MessageBox.Show("Cena je obvezen podatek");

Page 179: PROŠNJA - uranic.tsckr.siuranic.tsckr.si/VISUAL C#/VISUAL C#.pdf · MICROSOFT VISUAL C# .NET Srečo Uranič . Visual C# . ... Za začetek novega projekta v okolju Visual C# Standard

Visual C# . NET

Algoritmi in programski jeziki – 4.letnik

179

DialogResult = DialogResult.None;//prekličemo zapiranje obrazca } }

Še koda, ki pripada gumbu Dodaj na glavnem obrazcu (obrazec Fizdelki) private void bDodaj_Click(object sender, EventArgs e) { try { FIzdelek FIzdelek = new FIzdelek();//dinamično kreiranje novega objekta – obrazca if (FIzdelek.ShowDialog() == DialogResult.OK) { //novapostavka je sestavljena iz podatkov, ki smo jih vnesli na obrazcu Fizdelek string novapostavka = FIzdelek.Controls["Panel2"].Controls["tBNaziv"].Text + "|" + FIzdelek.Controls["Panel2"].Controls["tBProizvajalec"].Text+"|" + FIzdelek.Controls["Panel2"].Controls["nUDKomadov"].Text+"|" + FIzdelek.Controls["Panel2"].Controls["tBCena"].Text; listBox1.Items.Add(novapostavka); //v listBox1 dodamo novo postavko } } catch (FileNotFoundException) { MessageBox.Show("Datoteka " + pot + " ne obstaja!", "Napaka!"); } catch (IOException ioe) { MessageBox.Show(ioe.Message, "IO Napaka!"); } finally { if (fs != null) fs.Close(); //na koncu zapremo podatkovni tok in s tem tudi datoteko } }

Gumb Briši postavko: private void bBrisi_Click(object sender, EventArgs e) { listBox1.Items.Remove(listBox1.SelectedItem);//pobrišemo označeno vrstico v listBox1 }

Gumb Briši vse: private void bBrisiVse_Click(object sender, EventArgs e) { listBox1.Items.Clear();//odstranimo vse postavke iz gradnika listBox1 }

Gumb Uredi: Tudi ob kliku na gumb Uredi se odpre obrazec Fizdelek. Izbrano postavko v gradniku listBox1 moramo prenesti v gradnike tega obrazca, jih urediti, nato pa zapisati nazaj. private void bUredi_Click(object sender, EventArgs e) { try { int pozicija = listBox1.SelectedIndex;//ugotovimo, ali je uporabnik izbral kako vrstico if (pozicija == -1) //če je ni ga obvestimo { MessageBox.Show("Izberi postavko!"); } else { FIzdelek FIzdelek = new FIzdelek();//dinamično kreiranje novega objekta – obrazca

Page 180: PROŠNJA - uranic.tsckr.siuranic.tsckr.si/VISUAL C#/VISUAL C#.pdf · MICROSOFT VISUAL C# .NET Srečo Uranič . Visual C# . ... Za začetek novega projekta v okolju Visual C# Standard

Visual C# . NET

Algoritmi in programski jeziki – 4.letnik

180

//izbrano vrstico zapišemo v začasni string string vrstica = (string)listBox1.Items[pozicija]; //posamezne postavke med ločili \ zapišemo v enodimenzionalno tabelo stringov string[] stolpci = vrstica.Split('|'); //v posamezne gradnike obrazca FIzdelek zapišemo ustrezne postavke FIzdelek.Controls["Panel2"].Controls["tBNaziv"].Text = stolpci[0]; FIzdelek.Controls["Panel2"].Controls["tBProizvajalec"].Text = stolpci[1]; FIzdelek.Controls["Panel2"].Controls["nUDKomadov"].Text = stolpci[2]; FIzdelek.Controls["Panel2"].Controls["tBCena"].Text = stolpci[3]; if (FIzdelek.ShowDialog() == DialogResult.OK) { //novapostavka je sestavljena iz podatkov, ki smo jih vnesli na obrazcu Fizdelek string novapostavka = FIzdelek.Controls["Panel2"].Controls["tBNaziv"].Text + "|" + FIzdelek.Controls["Panel2"].Controls["tBProizvajalec"].Text + "|" + FIzdelek.Controls["Panel2"].Controls["nUDKomadov"].Text + "|" + FIzdelek.Controls["Panel2"].Controls["tBCena"].Text; listBox1.Items[pozicija]=novapostavka; //v listBox1 dodamo popravljeno postavko } } } catch { MessageBox.Show("Napaka!"); } }

Gumb Obdelaj ListBox: private void bObdelaj_Click(object sender, EventArgs e) { int vsotaKomadov = 0; decimal skupajCena = 0; int stevilopostavk = listBox1.Items.Count;//ugotovimo, koliko postavk je v listBox1 for (int i = 0; i < stevilopostavk; i++) { //vsebino posamezne vrstice listBox1 začasno shranimo v string, da jo bomo lahko obdelali string vrstica = (string)listBox1.Items[i]; //v tabelo stolpci zaporedoma zložimo zaporedja znakov med ločili | string[] stolpci = vrstica.Split('|'); vsotaKomadov+= Convert.ToInt32(stolpci[2]); skupajCena+= Convert.ToDecimal(stolpci[3]); } MessageBox.Show("Skupaj komadov: " + vsotaKomadov + "\nSkupna cena: " + skupajCena.ToString("###,##0.00")); }

Gumb Obdelaj Datoteko: private void bObdelajDatoteko_Click(object sender, EventArgs e) { int vsotaKomadov = 0; decimal skupajCena = 0; string pot = @"Izdelki.txt"; string dir = Path.GetFullPath(pot); fs = new FileStream(dir, FileMode.Open); StreamReader textIn = new StreamReader(fs); while (textIn.Peek() != -1) //iz datoteke beremo podatke dokler jih ne zmanjka { //vsebino posamezne vrstice datoteke začasno shranimo v string, da jo bomo lahko obdelali string vrstica = textIn.ReadLine();//preberemo celo vrstico naenkrat //v tabelo stolpci zaporedoma zložimo zaporedja znakov med ločili | string[] stolpci = vrstica.Split('|'); vsotaKomadov += Convert.ToInt32(stolpci[2]); skupajCena += Convert.ToDecimal(stolpci[3]); } textIn.Close(); fs.Close(); MessageBox.Show("Skupaj komadov: " + vsotaKomadov + "\nSkupna cena: " + skupajCena.ToString("###,##0.00")); }

Page 181: PROŠNJA - uranic.tsckr.siuranic.tsckr.si/VISUAL C#/VISUAL C#.pdf · MICROSOFT VISUAL C# .NET Srečo Uranič . Visual C# . ... Za začetek novega projekta v okolju Visual C# Standard

Visual C# . NET

Algoritmi in programski jeziki – 4.letnik

181

Gumb Zapiši v datoteko: private void bShrani_Click(object sender, EventArgs e) { //zapis v datoteko string dir = Path.GetFullPath(pot); //datoteko prepišemo z novo vsebino, tako kot je v gradniku listBox1 fs = new FileStream(dir, FileMode.Create); //deklariramo še nov objekt za pisanje podatkov v datoteko StreamWriter textOut = new StreamWriter(fs); int stevilopostavk = listBox1.Items.Count;//ugotovimo, koliko postavk je v listBox1 for (int i=0;i<stevilopostavk;i++) textOut.WriteLine(listBox1.Items[i]);//celo vrstico zapišemo naenkrat textOut.Close();//na koncu zapremo podatkovni tok in s tem tudi datoteko fs.Close(); }

Page 182: PROŠNJA - uranic.tsckr.siuranic.tsckr.si/VISUAL C#/VISUAL C#.pdf · MICROSOFT VISUAL C# .NET Srečo Uranič . Visual C# . ... Za začetek novega projekta v okolju Visual C# Standard

Visual C# . NET

Algoritmi in programski jeziki – 4.letnik

182

Garbage Collector

Uporaba Garbage Collectorja in upravljanje s pomnilnikom

Življenjska doba objektov

Novo instanco (nov objekt) nekega razreda kreiramo s pomočjo rezervirane besede new (to smo spoznali že pri učenju osnov jezika C# oz. C++ - poglavje razredi in objekti). TextBox sporocilo = new TextBox(); //Kreiranje novega objekta - TextBox je referenčni tip

S stališča programerja je uporaba besedice new pogosto že kar avtomatična – omogoči pač kreiranje novega objekta. Vendar pa je kreiranje objekta v resnici dvofazni proces. Najprej mora operacija new alocirati (zasesti) nekaj prostega pomnilnika na kopici – na ta del kreiranja novega objekta nimamo prav nobenega vpliva. Drugi del operacije new pa je v tem, da mora zasedeni pomnilnik pretvoriti v objekt – mora inicializirati objekt. To drugo fazo operacije new pa seveda lahko kontroliramo z uporabo konstruktorja. Ko je nov objekt kreiran, lahko do njegovih članov (polj, metod, ..) dostopamo z uporabo operatorja pika, npr.: sporocilo.Text = "Tekstovno sporočilo!"; TextBox spor1 = sporocilo;//s sklicevanjem na že ustvarjeni objekt lahko naredimo tudi novo //referenčno spremenljivko

Iz nekega objekta lahko v nadaljevanju tvorimo poljubno število novih referenc, a vse te reference je potrebno slediti, oz. imeti nad njimi popoln nadzor. Če spremenljivka sporocilo npr. izgine (zaključek bloka, zaključek metode, …), lahko ostale spremenljivke (v našem primeru npr. spremnljivka spor1) še vedno obstajajo. Življenjska doba objekta torej ni vezana na določeno referenčno spremenljivko. Objekt je lahko uničen šele tedaj, ko izginejo vse njegove reference.

Destruktorji in Garbage Collector

Tako kot je dvofazni proces kreiranje novega objekta, je dvofazni proces tudi njegovo uničenje – je njegova zrcalna slika. Prvi del »čiščenja« opravimo s pisanjem destruktorja. Drugi del faze pa je v tem, da je potrebno ob uničenju objekta sprostiti (alocirati) del pomnilnika, ki ga je objekt zasedal in ga vrniti kopici v nadaljnjo prosto uporabo. Nad to drugo fazo, tako kot pri kreiranju objekta, nimamo neposrednega vpliva. Proces uničenja objekta in vračanje pomnilnika kopici je poznano pod imenom garbage collection. V okolju Visual C# objektov nikoli ne moremo uničiti sami, saj za to opravilo ne obstaja nikakršna sintaksa (tako kot je npr. v C++ temu namenjena metoda delete). Kar nekaj pomembnih razlogov botruje temu, da nam C# ne omogoča eksplicitno uničenje objektov, saj bi v primeru, da bi bila odgovornost nad uničenjem objektov prepuščena nam, slej ko prej prišlo do ene od naslednjih situacij:

• pozabili bi uničiti nek objekt. To bi pomenilo, da se objektov destruktor ni izvedel, čiščenje pomnilnika se ni izvedlo, s tem pa pomnilnik na kopici, ki ga objekt zaseda, ne bi bil sproščen. Na ta način bi kmalu ostali brez pomnilnika.

• poskusili bi uničiti aktivni objekt, kar pa bi bila katastrofa za vse objekte z referenco na ta uničeni objekt.

• isti objekt bi lahko poskušali uničiti večkrat, kar bi bila prav tako lahko katastrofalno, odvisno od destruktorja.

Page 183: PROŠNJA - uranic.tsckr.siuranic.tsckr.si/VISUAL C#/VISUAL C#.pdf · MICROSOFT VISUAL C# .NET Srečo Uranič . Visual C# . ... Za začetek novega projekta v okolju Visual C# Standard

Visual C# . NET

Algoritmi in programski jeziki – 4.letnik

183

Zgornji problemi so torej nesprejemljivi, zaradi tega je za uničevanje objektov odgovoren proces garbage collector. Ta proces nam zagotavlja, da bo vsak objekt zagotovo uničen, obenem pa se bo izvedel njegov destruktor. Če napišemo destruktor, se bo ta zagotovo izvedel, ne vemo pa kdaj, saj o uničenju objektov odloča garbage collector. Ko se zaključi nek program, bodo zagotovo uničeni tudi vsi v programu kreirani objekti. Obenem proces zagotavlja, da bo vsak objekt uničen natanko enkrat, uničen pa bo samo takrat, ko postane nedostopen – to pa pomeni tedaj, ko ne obstaja več nobena referenca na ta objekt. Ostane pa še vprašanje, kdaj pa se čiščenje (garbage collection) sploh izvede. Prav gotovo je to tedaj, ko objekt ni več potreben, to pa se ne zgodi vedno neposredno za tem, ko objekta ne potrebujemo več (npr. neposredno po zaključku nekega bloka). Zažene se tedaj, ko zazna, da sistemu začne primanjkovati pomnilnika, oz. da je prezaposlen. Tedaj sprosti pomnilnik vseh tistih objektov, ki niso več v funkciji. Proces garbage collector pa lahko zaženemo tudi sami s stavkom System.GC.Collect();

Tak ekspliciten zagon procesa garbage collection pa ni priporočljiv. Proces se sicer res zažene, a ker se izvaja asinhrono, po njenem zaključku še vedno ne vemo, ali so bili vsi objekti res uničeni. Uničevanje objektov je zato najpametneje prepustiti runtime-u.

Kako deluje Garbage Collector

Garbage Collector teče v svoji lastni niti in se lahko izvede samo v določenem času (tipično npr. ob zaključku neke metode). Ostale niti, ki tečejo istočasno v programu, se tedaj začasno zaustavijo, saj bo garbage collector morda premikal posamezne objekte in ažuriral njihove reference, to pa ni možno tedaj, ko so objekti v uporabi. Koraki, ki jih garbage collector izvede, so naslednji:

• Zgradi seznam vseh dostopnih objektov, pri čemer si pomaga z sledenjem referenc na polja znotraj objektov. Objekt, ki ga ni v tem seznamu, se razume kot da je nedosegljiv.

• Preverja, ali ima katerikoli od nedosegljivih objektov destruktor, ki bi se moral izvesti (proces se imenuje finalizacija). Vsak nedostopen objekt, za katerega se mora izvesti finalizacija, se postavi v posebno vrsto imenovano freachable queue (F-Reachable).

• Poskrbi za dealokacijo preostalih nedosegljivih objektov (tistih, ki ne potrebujejo finalizacije) in na ta način sprosti pomnilnik na vrhu kopice.

• Na tej točki se procesi, ki tečejo v ostalih nitih, lahko nadaljujejo. • Izvede se še finalizacija nedostopnih objektov, ki le-to potrebujejo (proces pa se izvede v ločeni niti).

S pisanjem destruktorjev postane koda bolj kompleksna, bolj zahteven postane tudi proces garbage collection, sam program pa počasnejši. Če pa v programu ni nobenega destruktorja, ni potrebna finalizacija, program pa je zato seveda hitrejši. Pisanja destruktorjev se zaradi tega izogibamo, razen kadar jih resnično potrebujemo. Pogosto namesto pisanja destruktorja raje uporabimo t.i. using stavek (razložen bo v nadaljevanju).

Upravljanje s pomnilnikom Sproščanje virov v konstruktorju pa včasih sploh ni priporočljivo. Nekateri viri so namreč tako pomembni, da njihovo sproščanje ni pametno pustiti garbage collectorju – potrebni jih je sprostiti čim prej je to mogoče. Metodi, ki poskrbi za sproščanje nekega vira, pravimo metoda za odstranjevanje (disposal method). V primeru, da nek razred vsebuje dispose metodo, lahko le-to pokličemo eksplicitno, s tem pa imamo nadzor nad sproščanjem ustreznega vira.

Dispose metode

Page 184: PROŠNJA - uranic.tsckr.siuranic.tsckr.si/VISUAL C#/VISUAL C#.pdf · MICROSOFT VISUAL C# .NET Srečo Uranič . Visual C# . ... Za začetek novega projekta v okolju Visual C# Standard

Visual C# . NET

Algoritmi in programski jeziki – 4.letnik

184

Razred, ki ima implementirano dispose metodo, je npr. razred TextReader iz imenskega prostora System.IO. Razred vsebuje mehanizme za branje znakov iz vhodnega sekvenčnega toka podatkov. TextReader vsebuje virtualno metodo Close, ki zapre podatkovni tok. Razred StreamReader (ki bere znake iz podatkovnega toka, npr. datoteke) in razred StringReader (ki bere znnake iz nekega stringa) dedujeta iz razreda TextReader in oba imata prav tako svojo (override) metodo Close. Tule je primer, kjer beremo vrstice teksta iz neke datoteke z uporabo razreda StreamReader in prebrane vrstice shranjujemo v nek string: StreamReader branje = new StreamReader(datoteka); string vrstica,vsebina=""; while ((vrstica= branje.ReadLine())!=null) vsebina+=vrstica; branje.Close();

Metoda ReadLine() skuša prebrati naslednjo vrstico iz datoteke (toka). V primeru, da se tok konča (konec datoteke) metoda vrne vrednost null. Stavek branje.Close()v zgornjem primeru je nujno potreben, saj z njim sprostimo datoteko, oz. postane le-ta dostopna drugim uporabnikom. A v tem primeru nastopijo težave takrat, kadar bo npr. pri branju podatkov prišlo do kakršnekoli napake, saj se v tem primeru stavek branje.Close() sploh ne bo izvedel. Če se to v programu zgodi večkrat se lahko zgodi, da odpiranje novih datotek (ali pa ponovno odpiranje datoteke) ne bo več mogoče. Rešitev je v tem, da dispose metodo zapišemo v brezpogojni varovalni blok finally: StreamReader branje = new StreamReader(datoteka); try { string vrstica, vsebina = ""; while ((vrstica = branje.ReadLine()) != null) vsebina += vrstica; } finally //brezpogojni varovalni blok { branje.Close(); }

Tak način je sicer legalen in v zgornjem primeru tudi ustrezen, a problem nastane, kadar je potrebno odstranjevanje (sproščanje) več kot enega vira ( v tem primeru je potrebno gnezdenje varovalnih blokov). Poleg tega je v zgornjem primeru referenca na objekt branje ostala tudi potem, ko se varovalni blok že zaključi. Za reševanje takih problemov je v C# na volju using stavek.

Using stavek

Using stavek predstavlja univerzalni mehanizem za kontrolo življenjske dobe objektov (virov). Katerikoli objekt, ki ga kreiramo v glavi bloka using bo avtomatično uničen, ko se ta blok konča. (POZOR: using stavka ne smemo zamenjati z ukazom using, s katerim v projekt dodamo nek imenski prostor.) Splošna sintaksa using stavka: using (deklaracija objekta = inicializacija) stavki Primer: using (StreamReader branje=new StreamReader(datoteka) //v glavi stavka smo napovedali nov objekt { string vrstica, vsebina = ""; while ((vrstica = branje.ReadLine()) != null) vsebina += vrstica;

} //konec bloka using stavka - avtomatski klic dispose metode Close()

Page 185: PROŠNJA - uranic.tsckr.siuranic.tsckr.si/VISUAL C#/VISUAL C#.pdf · MICROSOFT VISUAL C# .NET Srečo Uranič . Visual C# . ... Za začetek novega projekta v okolju Visual C# Standard

Visual C# . NET

Algoritmi in programski jeziki – 4.letnik

185

Če bi hoteli zgornji using stavek nadomestiti z enakovredno kodo, brez uporabe using stavka, bi jo morali zapisati takole: StreamReader branje = new StreamReader(datoteka); try { string vrstica, vsebina = ""; while ((vrstica = branje.ReadLine()) != null) vsebina += vrstica; } finally //brezpogojni varovalni blok { if (branje != null) //uporaba vmesnika IDisposable, ki vsebuje le eno metodo: metodo Dispose() ((IDisposable) branje).Dispose(); }

Objekt, ki ga deklariramo v glavi using stavka mora biti izpeljan iz vmesnika IDisposable. Ta je sestavni del imenskega prostora System in vsebuje eno samo metodo, ki se imenuje Dispose; namespace System { interface IDisposable { void Dispose(); } }

Vmesnik IDisposable je implementiran tudi v razredu StremReader, njegova metoda Dispose pa kliče metodo Close, ki zapre podatkovni tok. Using stavek torej uporabimo kot čisti, rubustni in pred napakami varen način za zagotavljanje, da bo nek objekt oz. vir vedno avtomatično sproščen. Tako se rešimo vseh problemov, ki lahko nastanejo pri uporabi varovalnega bloka try/finally.

Klic metode dispose iz destruktorja

Kdaj v nekem razredu napisati destruktor, ali pa v razred implementirati vmesnik IDisposable. Destruktor se bo zagotovo izvedel, a ne moremo z gotovostjo trditi kdaj. Na drugi strani pa zagotovo vemo, kdaj se bo izvedla metoda Dispose, ne moremo pa biti gotovi da bo do klica metode prišlo, saj je odvisna od tega, ali bo programer uporabil using stavek ali pa ne. Prav zagotovo pa se bo metoda Dispose zagotovo izvedla, če jo kličemo iz destruktorja.

Page 186: PROŠNJA - uranic.tsckr.siuranic.tsckr.si/VISUAL C#/VISUAL C#.pdf · MICROSOFT VISUAL C# .NET Srečo Uranič . Visual C# . ... Za začetek novega projekta v okolju Visual C# Standard

Visual C# . NET

Algoritmi in programski jeziki – 4.letnik

186

Dialogi – okenska pogovorna okna

Gradnike, ki v Visual C# predstavljajo standardna okenska pogovorna okna, najdemo v oknu Toolbox v skupini Dialogs. Vsi dialogi so nevizuelni gradniki: ko jih postavimo na obrazec se namestijo v polje pod obrazcem, kjer jih potem lahko izbiramo ter jim prirejamo lastnosti in dogodke. Visual C# pozna naslednje dialoge:

• ColorDialog, • FolderBrowserDialog, • FontDialog, • OpenDialog in • SaveDialog

ColorDialog – pogovorno okno za izbiro barve. Pogovorno okno ColorDialog je namenjeno izbiri barve. Okno odpremo z metodo ShowDialog. Če uporabnik barvo izbere in okno zapre z gumbom V redu, se izbrana barva shrani v lastnost okna Color. Ker je okno ColorDialog modalno, klik na gumb V redu vrne vrednost DialogResult.OK, kar lahko izkoristimo npr. za programsko spreminjanje barve gradnikom.

Najpomembnejše lastnosti okna:

Lastnost Razlaga

AlowFullOpen Omogočen/onemogočen gumb za mešanje barv v oknu.

AnyColor Če lastnost postavljena na true bodo v množici bazičnih barv prikazane vse možne barve.

Color Nastavitev barve, ki bo v oknu izbrana, ko se bo le to odprlo.

SolidColorOnly Barve v pogovornem oknu bodo/ne bodo omejene le na prave/pristne barve.

Naslednji primer prikazuje dogodek Click nekega gumba (button1). Ob kliku na gumb se Odpre se okno pogovorno okno ColorDialog in če uporabnik v tem oknu izbere poljubno barvo in klikne gumb V redu, se gumb pobarva z izbrano barvo. private void button1_Click(object sender, EventArgs e) { if (colorDialog1.ShowDialog()==DialogResult.OK) button1.BackColor = colorDialog1.Color; }

Page 187: PROŠNJA - uranic.tsckr.siuranic.tsckr.si/VISUAL C#/VISUAL C#.pdf · MICROSOFT VISUAL C# .NET Srečo Uranič . Visual C# . ... Za začetek novega projekta v okolju Visual C# Standard

Visual C# . NET

Algoritmi in programski jeziki – 4.letnik

187

Posebnost okna za izbiranje barv je njegova tabelarična lastnost CustomColors (barve po meri), kamor lahko uporabnik med izvajanjem programa shrani 16 svojih, iz osnovnih barv narejenih barv. Tule je en primer shranjevanja namešanih barv, pri čemer so seveda barve predstavljene z ustreznim desetiškim celim številom : colorDialog1.CustomColors = new int[]{6916092, 15195440, 16107657, 1836924, 3758726, 12566463, 7526079, 7405793, 6945974, 241502, 2296476, 5130294, 3102017, 7324121, 14993507, 11730944,};

Vsak dialog lahko odpremo tudi povsem programsko, ne da bi prej na obrazec postavili ustrezen gradnik. To dosežemo z dinamičnim zaseganjem pomnilnika za konkreten dialog. Za ColorDialog bi to naredili npr. takole: private void button2_Click(object sender, EventArgs e) { //najprej dinamično zaseganje pomnilnika za nov objekt tipa ColorDialog z imenom CD1 ColorDialog CD1=new ColorDialog; //modalno odpremo dialog in preverimo če je uporabnik kliknil gumb V redu if (CD1.ShowDialog()==DialogResult.OK) button2.BackColor = CD1.Color; //Če kliknjen gumb V redu, pobarvamo gumb button2 }

Naslednji primer pa prikazuje, kako dinamično kreiramo nov barvni dialog in nato z njegovo pomočjo določimo barvo gradnika TextBox (ime gradnika textBox1) – kodo zapišemo npr. ob dogodku Click nekega gumba. ColorDialog MyDialog = new ColorDialog();//Kreiramo nov dialog MyDialog.AllowFullOpen = false; // Onemogočimo gumb za mešanje barv MyDialog.ShowHelp = true; // Uporabniku omogočimo dostop za gumb Pomoč. //Dialogu ColorDialog nastavimo začetno barvo na trenutno barvo gradnika textBox1. MyDialog.Color = textBox1.ForeColor; //Če uporabnik v ColorDialog klikne gumb V redu, ažuriramo barvo v gradniku textBox if (MyDialog.ShowDialog() == DialogResult.OK) textBox1.ForeColor = MyDialog.Color;

FolderBrowserDialog – pogovorno okno za raziskovanje map. Pogovorno okno FolderBrowserDialog omogoči uporabniku raziskovanje po mapah računalnika in kreiranje novih map. Dialog je nekakšen komplement dialogu OpenFileDialog, ki pa se uporablja za raziskovanje in kreiranje datotek. Okno odpremo z metodo ShowDialog, omogoča pa izbiro poljubne mape. Izbiro potrdimo s klikom na gumb V redu, kar pomeni, da okno vrne vrednost DialogResult.OK. Najpomembnejše lastnosti okna:

Lastnost Razlaga

Description Nastavitev poljubnega teksta (naslova), ki se izpiše nad drevesno strukturo map v oknu.

SelectedPath Pred odpiranjem dialoga lahko določimo mapo, ki bo označena ko se bo dialog odprl.

Show&ewFolderButton V oknu je privzeto tudi gumb, ki omogoča kreiranje novih map. Če lastnost Show&ewFolderButton postavimo na false, tega gumba ni.

Page 188: PROŠNJA - uranic.tsckr.siuranic.tsckr.si/VISUAL C#/VISUAL C#.pdf · MICROSOFT VISUAL C# .NET Srečo Uranič . Visual C# . ... Za začetek novega projekta v okolju Visual C# Standard

Visual C# . NET

Algoritmi in programski jeziki – 4.letnik

188

Lastnost Description

Selected Path

Show&ewFolderButton ( True) Še primer dinamičnega kreiranja FolderBrowserDialog-a in sprememba nekaterih nastavitev: FolderBrowserDialog MyDialog = new FolderBrowserDialog(); MyDialog.Description ="Moj dialog za raziskovanje map";//Napis nad prikazom map MyDialog.SelectedPath="c:\\Program Files\\Common Files";//Izbrana mapa MyDialog.ShowNewFolderButton = false; //Onemogočen gumb za kreiranje novih map if (MyDialog.ShowDialog() == DialogResult.OK) MessageBox.Show("OK – mapa izbrana!");

FontDialog – pogovorno okno za izbiro pisave. Pogovorno okno FontDialog služi za izbiro pisave. Okno odpremo z metodo ShowDialog. Če uporabnik pisavo izbere in okno zapre s klikom na gumb V redu, okno vrne vrednost DialogResult.OK, izbrana pisava pa se shrani v lastnost okna Font. Najpomembnejše lastnosti okna:

Lastnost Razlaga

Color Nastavitev izbrane barve pisave.

Font Nastavitev izbranega fonta.

MaxSize Nastavitev največje velikosti pisave, ki jo uporabnik še lahko izbere.

MinSize Nastavitev najmanjše velikosti pisave, ki jo uporabnik še lahko izbere.

ShowApply V oknu bo oz. ne bo gumba Uporabi.

ShowColor V oknu bo oz ne bo možno izbiranje barve pisave.

ShowEfects V oknu bo oz. ne bo prikazana opcije Učinki (prečrtanje, podčrtavanje in izbira barve).

ShowHelp V oknu bo oz. ne bo gumba Pomoč.

Page 189: PROŠNJA - uranic.tsckr.siuranic.tsckr.si/VISUAL C#/VISUAL C#.pdf · MICROSOFT VISUAL C# .NET Srečo Uranič . Visual C# . ... Za začetek novega projekta v okolju Visual C# Standard

Visual C# . NET

Algoritmi in programski jeziki – 4.letnik

189

Naslednji primer pa prikazuje, kako s pomočjo dialoga za izbiro pisave spremenimo pisavo gradnika TextBox (ime gradnika je textBox1). if (fontDialog1.ShowDialog() == DialogResult.OK) { textBox1.Font= fontDialog1.Font; //lahko tudi textBox1.Font = new Font(fontDialog1.Font.Name, fontDialog1.Font.Size

,fontDialog1.Font.Style); }

OpenFileDialog - pogovorno okno za odpiranje datotek Pogovorno okni OpenFileDialog je namenjeno izbiri datoteke, ki jo nameravamo odpreti. Okno odpremo z metodo ShowDialog. Če uporabnik datoteko izbere in okno zapre s klikom na gumb V redu, okno vrne vrednost DialogResult.OK, ime izbrane datoteke pa se shrani v lastnost okna File&ame. Najpomembnejše lastnosti okna:

Lastnost Razlaga

AddExtension Lastnost določa, ali naj pogovorno okno avtomatično doda datoteki končnico, v primeru da jo je uporabnik pozabil napisati.

CheckFileExists Lastnost določa, ali naj pogovorno okno izpiše obvestilo v primeru, da je uporabnik navedel ime datoteke, ki ne obstaja.

CheckPathExists Lastnost določa, ali naj pogovorno okno izpiše obvestilo v primeru, da je uporabnik navedel pot do datoteke, ki ne obstaja.

DefaultExt Nastavitev privzete končnice datoteke.

File&ame Ime datoteke, ki naj bo izbrana v pogovornem oknu.

Filter Omejitev le na določene vrste datotek (npr. tekstovne, ..).

InitialDirectory Nastavitev privzetega imenika.

Multiselect Lastnost določa, ali lahko v pogovornem oknu hkrati izberemo več datotek

ReadOnlyChecked Lastnost določa, ali naj bo odkljukana opcija ReadOnly.

ShowReadOnly Lastnost določa, ali naj se v pogovornem okno prikaže stikalo Samo za branje.

Title Naslov pogovornega okna.

Title

ShowReadOnly

Page 190: PROŠNJA - uranic.tsckr.siuranic.tsckr.si/VISUAL C#/VISUAL C#.pdf · MICROSOFT VISUAL C# .NET Srečo Uranič . Visual C# . ... Za začetek novega projekta v okolju Visual C# Standard

Visual C# . NET

Algoritmi in programski jeziki – 4.letnik

190

Naslednji primer prikazuje, kako s pomočjo OpenFileDialoga ime neke datoteke zapišemo v polje TextBox (ime gradnika je textBox1). openFileDialog1.Filter = "Textovne datoteke (*.txt)|*.txt|Moje datoteke (*.MOJ)|*.MOJ"; openFileDialog1.ShowReadOnly = true; //V pogovornem oknu bo stikalo Read Only //POZOR: za dostop do nekega imenika pišemo dva znaka \\ openFileDialog1.InitialDirectory = "c:\\Program Files\\Common Files";//Privzeti imenik openFileDialog1.Title = "Poišči in izberi datoteko!"; //Naslov pogovornega okna if (openFileDialog1.ShowDialog() == DialogResult.OK) textBox2.Text=openFileDialog1.FileName;

� Opomba: Pri navajanju privzete poti v lastnosti InitialDirectory moramo zapisat dva znaka \\ za dostop do imenika!!!, ali pa pred začetkom stringa zapisati znak @.

SaveFileDialog - pogovorno okno za shranjevanje datotek Pogovorno okni SaveFileDialog je namenjeno shranjevanju datotek . Okno odpremo z metodo ShowDialog. Če uporabnik datoteko izbere in okno zapre s klikom na gumb V redu, okno vrne vrednost DialogResult.OK, ime izbrane datoteke pa se shrani v lastnost okna File&ame. Najpomembnejše lastnosti okna:

Lastnost Razlaga

AddExtension Lastnost določa, ali naj pogovorno okno avtomatično doda datoteki končnico, v primeru da jo je uporabnik pozabil napisati.

CheckFileExists Lastnost določa, ali naj pogovorno okno izpiše obvestilo v primeru, da je uporabnik navedel ime datoteke, ki ne obstaja.

CheckPathExists Lastnost določa, ali naj pogovorno okno izpiše obvestilo v primeru, da je uporabnik navedel pot do datoteke, ki ne obstaja.

CreatePrompt Nastavitev dovoljenja za kreiranje datoteke, če uporabnik navede ime datoteke, ki še ne obstaja.

DefaultExt Nastavitev privzete končnice datoteke.

File&ame Ime datoteke, ki naj bo izbrana v pogovornem oknu.

Filter Omejitev le na določene vrste datotek (npr. tekstovne, ..).

InitialDirectoty Nastavitev privzetega imenika.

OverwritePrompt Lastnost določa, ali naj nas program opozori, če za ciljno datoteko izberemo datoteko, ki že obstaja.

Title Naslov pogovornega okna.

Title

Filter

Page 191: PROŠNJA - uranic.tsckr.siuranic.tsckr.si/VISUAL C#/VISUAL C#.pdf · MICROSOFT VISUAL C# .NET Srečo Uranič . Visual C# . ... Za začetek novega projekta v okolju Visual C# Standard

Visual C# . NET

Algoritmi in programski jeziki – 4.letnik

191

Naslednji primer prikazuje, kako s pomočjo SaveFileDialoga vsebino gradnika RichTextBox (ime gradnika je richTextBox1) zapišemo v datoteko, ki jo izberemo s pomočjo tega dialoga. saveFileDialog1.OverwritePrompt = true; //program nas opozori, če datoteka že obstaja saveFileDialog1.InitialDirectory = "c:\\Programi";//Določimo privzeti imenik saveFileDialog1.Title = "Shrani datoteko!"; //Naslov pogovornega okna saveFileDialog1.Filter = " Moje *rtf datoteke (*.rtf)|*.rtf"; //Nastavitev filtra if (saveFileDialog1.ShowDialog() == DialogResult.OK) richTextBox1.SaveFile(saveFileDialog1.FileName, RichTextBoxStreamType.RichText);

� Pozor: S pogovornima oknoma OpenFileDialog in SaveFileDialog datoteke ni mogoče ne odpreti ne shraniti. Pogovorni okni samo omogočata izbiro datoteke, ki jo nameravamo odpreti oziroma shraniti. Za odpiranje in zapiranje datoteke moramo poskrbeti z ustrezno programsko kodo.

Vaja: Kreirajmo preprosti urejevalnik. Pri izdelavi projekta bomo uporabili vse možne dialoge. Izgled obrazca:

RichTextBox (ime gradnika richTextBox1)

V orodjarno (toolStrip1) s pomočjo spustnega seznama namestimo 12 gumbov in dva separatorja. Gumbom priredimo ustrezne slike, tako kot prikazuje gornja slika. Postavka Datoteka v meniju naj vsebuje podopcije &ova, Odpri, Shrani in Shrani kot… Imena gumbov (od leve proti desni); tSB&ova, tSBOdpri, tSBShrani, tSBShraniKot, tSBIzrezi, tSBKopiraj, tSBPrilepi, tSBPoudari, tSBItalic, tSBFont, tSBColor in tSBFolder. Ustrezna koda, ki pripada posameznim gumbom in ki jo zapišemo v dogodek Click ustreznega gumba pa je takale: Gumb tSB&ova: // Naslednja je potrebna le, če na obrazec ne bi postavili gradnika OpenFileDialog: //Dialog saveFile1 = new SaveFileDialog(); // Inicializiramo saveFileDialog -> delali bomo le z tekstovnimi datotekami saveFileDialog1.Filter = "Textovne datoteke (*.txt)|*.txt|Moje datoteke (*.MOJ)|*.MOJ"; if (richTextBox1.Text != "") if (MessageBox.Show("Shranim trenutno stanje?","Shranjevanje", MessageBoxButtons.OKCancel

,MessageBoxIcon.Question)==DialogResult.OK) if (saveFileDialog1.ShowDialog() == DialogResult.OK)

richTextBox1.SaveFile(saveFileDialog1.FileName,RichTextBoxStreamType.RichText); richTextBox1.Clear();//Pobrišemo gradnik TextBox

Page 192: PROŠNJA - uranic.tsckr.siuranic.tsckr.si/VISUAL C#/VISUAL C#.pdf · MICROSOFT VISUAL C# .NET Srečo Uranič . Visual C# . ... Za začetek novega projekta v okolju Visual C# Standard

Visual C# . NET

Algoritmi in programski jeziki – 4.letnik

192

Gumb tSBOdpri: saveFileDialog1.Filter = "Textovne datoteke (*.txt)|*.txt|Moje datoteke (*.MOJ)|*.MOJ"; if (richTextBox1.Text != "") if (MessageBox.Show("Shranim trenutno stanje?" "Shranjevanje" MessageBoxButtons.OKCancel

,MessageBoxIcon.Question)==DialogResult.OK) if (saveFileDialog1.ShowDialog() == DialogResult.OK)

richTextBox1.SaveFile(saveFileDialog1.FileName,RichTextBoxStreamType.RichText); richTextBox1.Clear();

Gumb tSBShrani: saveFileDialog1.Filter = "Textovne datoteke (*.txt)|*.txt|Moje datoteke (*.MOJ)|*.MOJ"; if (saveFileDialog1.ShowDialog() == DialogResult.OK)

richTextBox1.SaveFile(saveFileDialog1.FileName,RichTextBoxStreamType.RichText);

Gumb tSBShraniKot: saveFileDialog1.Filter = "Textovne datoteke (*.txt)|*.txt|Moje datoteke (*.MOJ)|*.MOJ"; if (saveFileDialog1.ShowDialog() == DialogResult.OK) //Shrani vsebino gradnik RichEdit v datoteko: richTextBox1.SaveFile(saveFileDialog1.FileName, RichTextBoxStreamType.RichText);

Gumb tSBIzrezi: richTextBox1.Cut();

Gumb tSBKopiraj: richTextBox1.Copy();

Gumb tSBPrilepi: richTextBox1.Paste();

Gumb tSBPoudari: if (richTextBox1.SelectionFont.Style!=FontStyle.Bold) richTextBox1.SelectionFont = new Font(richTextBox1.Font, FontStyle.Bold); else richTextBox1.SelectionFont = new Font(richTextBox1.Font, FontStyle.Regular);

Gumb tSBItalic: if (richTextBox1.SelectionFont.Style != FontStyle.Italic) richTextBox1.SelectionFont = new Font(richTextBox1.Font, FontStyle.Italic); else richTextBox1.SelectionFont = new Font(richTextBox1.Font, FontStyle.Regular);

Gumb tSBFont: if (fontDialog1.ShowDialog() == DialogResult.OK) richTextBox1.SelectionFont = new Font(fontDialog1.Font.Name, fontDialog1.Font.Size,

fontDialog1.Font.Style);

Gumb tSBColor: if (colorDialog1.ShowDialog() == DialogResult.OK) richTextBox1.SelectionColor = colorDialog1.Color;

Gumb tSBFolder: folderBrowserDialog1.ShowDialog();

Vaja:

Page 193: PROŠNJA - uranic.tsckr.siuranic.tsckr.si/VISUAL C#/VISUAL C#.pdf · MICROSOFT VISUAL C# .NET Srečo Uranič . Visual C# . ... Za začetek novega projekta v okolju Visual C# Standard

Visual C# . NET

Algoritmi in programski jeziki – 4.letnik

193

Naslednji primer prikazuje, kako lahko obdelamo neko tekstovno datoteko. Poljubno tekstovno datoteko s pomočjo OpenFileDialoga najprej odpremo, vsebino zapišemo v gradnik TextBox, naredimo poljubne spremembe, nato pa jo zopet shranimo pod poljubnim imenom. Na obrazec postavimo meni in v njem naredimo štiri opcije, tako kot prikazuje slika. Potrebovali bomo še OpenFileDialog in SaveFileDialog.

TextBox (lastnost Dock = fill)

Koda, ki ustreza posameznim opcijam menija:

• Opcija Odpri datoteko:

private void odpriDatotekoToolStripMenuItem_Click(object sender, EventArgs e) { openFileDialog1.Filter = "Tekstovne datoteke (*.txt)|*.txt"; if (openFileDialog1.ShowDialog() == DialogResult.OK) { //Objekt Datoteka kaže na datoteko, ki smo jo odprli z OpenFileDialog-om //Stream Datoteka = openFileDialog1.OpenFile(); //objektu beriDatoteko povemo, katero datoteko želimo brati //StreamReader beriDatoteko = new StreamReader(Datoteka); StreamReader beriDatoteko = new StreamReader(openFileDialog1.OpenFile()); int vrstic = 0; string vrstica = ""; int i = 0, znakov = 0; while (true) { vrstica = beriDatoteko.ReadLine(); if (vrstica != null) { znakov = znakov + vrstica.Length; textBox1.Text = textBox1.Text + vrstica + "\r\n"; i++; vrstic++; } else break; } beriDatoteko.Close(); MessageBox.Show("Skupaj vrstic: " + vrstic + "\r\nSkupaj znakov: " + znakov); //Še uporaba foreach zanke vrstic = 0; foreach (string line in textBox1.Lines) { vrstic++; } //zaradi avtomatske konverzije pri izpisu spremenljivke tipa int "vrstic" //ni potrebna sprememba v string z metodo ToString MessageBox.Show("V gradniku TextBox je skupaj " + vrstic + " vrstic"); } }

• Opcija Obdelaj datoteko:

private void obdelajDatotekoToolStripMenuItem_Click(object sender, EventArgs e) { //skupno število vrstic gradnika TextBox int vrstice = textBox1.Lines.Length;

Page 194: PROŠNJA - uranic.tsckr.siuranic.tsckr.si/VISUAL C#/VISUAL C#.pdf · MICROSOFT VISUAL C# .NET Srečo Uranič . Visual C# . ... Za začetek novega projekta v okolju Visual C# Standard

Visual C# . NET

Algoritmi in programski jeziki – 4.letnik

194

StreamReader beriDatoteko = new StreamReader(openFileDialog1.OpenFile()); int vrstic=0,samoglasnikov = 0; string vrstica = ""; //foreach (string line in editData.Lines) while (true) { vrstica = beriDatoteko.ReadLine(); if (vrstica != null) { vrstica = vrstica.ToUpper(); for (int i = 0; i < vrstica.Length; i++) { if ((vrstica[i] == 'A') || (vrstica[i] == 'E') || (vrstica[i] == 'I') ||

(vrstica[i] == 'O') || (vrstica[i] == 'U')) samoglasnikov++; } } else break; } beriDatoteko.Close(); MessageBox.Show("Skupaj vrstic: " + vrstice +"\r\nSkupaj samoglasnikov: " + samoglasnikov); }

• Opcija Shrani datoteko:

private void shraniDatotekoToolStripMenuItem_Click(object sender, EventArgs e) { saveFileDialog1.Filter = "Tekstovne datoteke (*.txt)|*.txt"; if (saveFileDialog1.ShowDialog() == DialogResult.OK) { //odpremo datoteko z dovoljeni za branje in pisanje Stream shraniDat = saveFileDialog1.OpenFile(); //razred StreamWriter kot naslednik razred TextWriter s svojimi metodami

//omogoča zapisovanje množice znakov StreamWriter saveWriter = new StreamWriter(shraniDat); foreach (string line in textBox1.Lines) { //v izbrano datoteko zapisujemo vsako vrstico posebej saveWriter.WriteLine(line); } saveWriter.Close(); textBox1.Clear(); } }

Vaja: Še en primer odpiranja, obdelave in shranjevanja podatkov v tekstovno datoteko. V tekstovno datoteko želimo shranjevati rezultate treh metov kocke poljubnega števila tekmovalcev. Izpis mora biti formatiran (ime 10 naj ima mest, priimek 15 mest, vsi trije meti kocke pa naj bodo formatirani na dve mesti. Predpostavimo, da

datoteka že obstaja in jo želimo obdelovati, dodajati zapise, shraniti pod drugim imenom in po želji označeno vsebino tudi pobrisati. Za dodajanje novega zapisa v datoteko bomo modalno odprli poseben obrazec za vnos podatkov. Osnovni obrazec naj izgleda takole: Gradnik TextEdit (lastnost Multiline=True, ScrollBars=Vertical) button2 button1 button3 Nevizuelni gradniki

Page 195: PROŠNJA - uranic.tsckr.siuranic.tsckr.si/VISUAL C#/VISUAL C#.pdf · MICROSOFT VISUAL C# .NET Srečo Uranič . Visual C# . ... Za začetek novega projekta v okolju Visual C# Standard

Visual C# . NET

Algoritmi in programski jeziki – 4.letnik

195

Koda, ki ustreza posameznim opcijam menija in gumbom, ki so na obrazcu:

• Dogodek Load glavnega obrazca:

private void Form1_Load(object sender, EventArgs e) { button1.Enabled =false; button2.Enabled = false; button3.Enabled = false; shraniToolStripMenuItem.Enabled = false;//opcija Shrani v glavnem meniju shraniKotToolStripMenuItem.Enabled = false; //opcija Shrani kot… v glavnem meniju }

• Pred kreiranjem ostalih metod deklarirajmo nekaj globalnih spremenljivk:

string imedatoteke,vrstica; string novoime, novpriimek; int vrstic = 0, najpik = 0; int met1, met2, met3;

• Opcija Odpri v glavnem meniju

private void odpriToolStripMenuItem_Click(object sender, EventArgs e) { vrstic = 0; //šteli bomo vrstice openFileDialog1.Filter = "Kocka (*.kck)|*.kck"; openFileDialog1.FileName = ""; if (openFileDialog1.ShowDialog() == DialogResult.OK) { textBox1.Clear(); //Za branje iz tekstovne datoteke uporabimo razred StreamReader StreamReader beriDatoteko = new StreamReader(openFileDialog1.OpenFile()); //ime odprte datoteke bomo rabili kasneje pri zapisovanju v datoteko imedatoteke = openFileDialog1.FileName; while (true) { vrstica = beriDatoteko.ReadLine(); if (vrstica != null)//če branje uspešno { vrstic++; string ime = vrstica.Substring(0, 10);//prvih 10 znakov je ime string priimek = vrstica.Substring(10, 15);//nadalnjih 15 je priimek //v int pretvorjena dva znaka za 1. met met1=Convert.ToInt32(vrstica.Substring(25,2)); met2 = Convert.ToInt32(vrstica.Substring(27, 2));//dva znaka 2. met met3 = Convert.ToInt32(vrstica.Substring(29, 2));//dva znaka za 3. met if (vrstic==1) //zapis v gradniku TextBox mora biti enak kot v datoteki textBox1.Text = textBox1.Text + vrstica; else textBox1.Text = textBox1.Text + "\r\n"+vrstica; if (met1 + met2 + met3 >= najpik) najpik = met1 + met2 + met3; } else break; } button1.Enabled = true; button2.Enabled = true; button3.Enabled = true; shraniToolStripMenuItem.Enabled = true; shraniKotToolStripMenuItem.Enabled = true; beriDatoteko.Close(); } }

• Gumb Obdelava

private void button2_Click(object sender, EventArgs e) { string zmagovalci = "";

Page 196: PROŠNJA - uranic.tsckr.siuranic.tsckr.si/VISUAL C#/VISUAL C#.pdf · MICROSOFT VISUAL C# .NET Srečo Uranič . Visual C# . ... Za začetek novega projekta v okolju Visual C# Standard

Visual C# . NET

Algoritmi in programski jeziki – 4.letnik

196

//obdelava podatkov ki so v gradniku textBox1 for (int i = 0; i < vrstic; i++)//število vrstic smo dobili pri odpiranju dat. { //iz tekoče vrstice izrežimo posamezne mete kocke met1 = Convert.ToInt32(textBox1.Lines[i].Substring(25, 2)); met2 = Convert.ToInt32(textBox1.Lines[i].Substring(27, 2)); met3 = Convert.ToInt32(textBox1.Lines[i].Substring(29, 2)); if (met1 + met2 + met3 == najpik) //zmagovalcev je lahko več! zmagovalci = zmagovalci + textBox1.Lines[i].Substring(0, 10) + ' ' +

textBox1.Lines[i].Substring(10, 15) + "\n"; } MessageBox.Show("Skupno število vrstic: " + vrstic.ToString() + "\n\nZmagovalci ("+Convert.ToInt32(najpik)+" pik) :\n" + zmagovalci); }

• Opcija Shrani v glavnem meniju

private void shraniToolStripMenuItem_Click(object sender, EventArgs e) { //odpremo datoteko z dovoljeni za branje in pisanje //razred StreamWriter kot naslednik razred TextWriter s svojimi metodami omogoča //zapisovanje množice znakov //drugi parameter metode StreamWriter(false) pomeni prepis preko stare vsebine StreamWriter pisiVDatoteko = new StreamWriter(imedatoteke, false,

System.Text.Encoding.Default); foreach (string line in textBox1.Lines) { //v izbrano datoteko zapisujemo vsako vrstico posebej pisiVDatoteko.WriteLine(line); } pisiVDatoteko.Close(); }

• Opcija Shrani kot … v glavnem meniju

private void shraniKotToolStripMenuItem_Click(object sender, EventArgs e) { saveFileDialog1.Filter = "Kocka (*.kck)|*.kck"; saveFileDialog1.FileName = ""; if (saveFileDialog1.ShowDialog() == DialogResult.OK) { Stream shraniDat = saveFileDialog1.OpenFile(); StreamWriter pisiVDatoteko = new StreamWriter(shraniDat); foreach (string line in textBox1.Lines) { //v izbrano datoteko zapisujemo vsako vrstico posebej pisiVDatoteko.WriteLine(line); } pisiVDatoteko.Close(); } }

• Gumb Briši

private void button3_Click(object sender, EventArgs e) { if (MessageBox.Show("Brišem označene vrstice?", "Brisanje", MessageBoxButtons.OKCancel,

MessageBoxIcon.Question) == DialogResult.OK) { textBox1.ReadOnly = false; textBox1.Cut(); textBox1.ReadOnly = true; } }

Za dodajanje novega zapisa (nove vrstice) v datoteko, pa pripravimo poseben obrazec, ki se odpre ob kliku na gumb button1/Dodaj na glavnem obrazcu. Gradikom na tem obrazcu ni potrebno prirejati nobenega dogodka. Gumb Prekliči ima nastavljeno lastnost DialogResult na Cancel, gumb Shrani pa na OK.

Page 197: PROŠNJA - uranic.tsckr.siuranic.tsckr.si/VISUAL C#/VISUAL C#.pdf · MICROSOFT VISUAL C# .NET Srečo Uranič . Visual C# . ... Za začetek novega projekta v okolju Visual C# Standard

Visual C# . NET

Algoritmi in programski jeziki – 4.letnik

197

gradniki &umericUpDown

Koda, ki pripada dogodku Click gumba button1/ Dodaj na glavnem obrazcu private void button1_Click(object sender, EventArgs e) { FKocka FKocka= new FKocka();//dinamično kreiranje novega, že pripravljenega obrazca if (FKocka.ShowDialog() ==DialogResult.OK) //preverimo, kako je uporabnik zaprl obrazec { string kocka1, kocka2, kocka3; //do vsebine gradnikov obrazca FKocka dostopamo preko lastnosti Control novoime = FKocka.Controls["textBox1"].Text; //tekst LEVO poravnan,desni del zapolnjen s presledki, skupna dolžina bo 10 znakov //metoda PadRight(N) tekst LEVO poravna; če je skupna dolžina manj kot 10 znakov na

//desni strani doda presledke, tako da je skupna dožina 10 znakov //metoda Substr(I,N) iz stringa odreže N znakov od I-tega dalje novoime=(novoime.PadRight(10)).Substring(0,10); novpriimek = FKocka.Controls["textBox2"].Text; //novpriimek mora pred vpisom v datoteko zavzemati točno 15 znakov novpriimek=(novpriimek.PadRight(15)).Substring(0,15); //kocka1,kocka2 in kocka3 morajo pred vpisom v datoteko zavzemati točno 2 znaka kocka1 = " "+FKocka.Controls["numericUpDown1"].Text; kocka2 = " "+FKocka.Controls["numericUpDown2"].Text; kocka3 = " "+FKocka.Controls["numericUpDown3"].Text; //Za pisanje v tekstovno datoteko uporabimo razred StreamWriter StreamWriter pisiVDatoteko = new StreamWriter(imedatoteke, true); vrstica="\r\n"+novoime+novpriimek+kocka1+kocka2+kocka3; pisiVDatoteko.Write(vrstica); pisiVDatoteko.Close(); FKocka.Dispose(); //še zapis vrstice v gradnik textBox1 na obrazcu textBox1.Text = textBox1.Text +"\r\n"+novoime+novpriimek+kocka1+kocka2+kocka3; vrstic++; } }

Page 198: PROŠNJA - uranic.tsckr.siuranic.tsckr.si/VISUAL C#/VISUAL C#.pdf · MICROSOFT VISUAL C# .NET Srečo Uranič . Visual C# . ... Za začetek novega projekta v okolju Visual C# Standard

Visual C# . NET

Algoritmi in programski jeziki – 4.letnik

198

Tiskalnik Gradnike, ki so Visual C# namenjeni tiskalniškim poslom, najdemo v oknu Toolbox v skupini Printing. Gradniki so nevizuelni – na obrazec jih postavimo tako kot ostale gradnike, zato se po postavitvi na obrazec prikažejo v polju pod obrazcem. Uporabljamo jih za tiskanje besedila in za tiskanje slik, za vse dodatne nastavitve pa moramo poskrbeti programsko. Polna verzija Visual C# pa ima v oknu ToolBox še skupino Crystal Reports – gradnike, ki so namenjeni oblikovanju poročil – reportov. Za tiskanje dokumenta oz. slike potrebujemo gradnik PrintDocument. Gradnik se uporablja za nastavitev lastnosti, s katerimi povemo kaj želimo tiskati, z njegovo pomočjo pa dokument nato tudi natisnemo z metodo Print. Najpomembnejša lastnost, ki jo moramo določiti pred tiskanjem, pa je lastnost Graphics. Določimo jo v dogodku PrintPage gradnika PrintDocument. Z njo določimo kaj bomo tiskali: to je lahko vrstica teksta (DrawString), slika (DrawImage), geometrijski lik (DrawRectangle), črto (DrawLine), elipso/krog (DrawEllipse), … Gradnik PrintDocument navadno uporabljamo v tesni povezavi z gradnikom PrintDialog, s pomočjo katerega izberemo ustrezen tiskalnik, določimo del dokumenta, ki ga želimo natisniti, število kopij in še nekatere druge nastavitve. Vaja: Projekt Tiskanje prikazuje, kako aktiviramo dialog za odpiranje pogovornega okna za nastavitve tiskalnika, dialog za nastavitev strani tiskanja, in kako lahko izbrane nastavitve shranimo oz. prikažemo v gradniku ListBox. V projektu je natančno prikazan postopek za tiskanje poljubne tekstovne datoteke in za tiskanje poljubne slike. Prikazana pa je tudi uporaba gradnika PagePreviewDialog, ki uporabniku omogoča predogled tiskanja

Gradnik ListBox Potrebni gradniki – večinoma s palete Printing

Koda, ki ustreza posameznim opcijam menija:

• Opcija menija PrintDialog – nastavitve tiskalnika

private void printDialogNastavitveTiskalnikaToolStripMenuItem_Click(object sender, EventArgs e)

{ printDialog1.ShowDialog();//odpiranje dialoga za nastavitev tiskalnika }

Page 199: PROŠNJA - uranic.tsckr.siuranic.tsckr.si/VISUAL C#/VISUAL C#.pdf · MICROSOFT VISUAL C# .NET Srečo Uranič . Visual C# . ... Za začetek novega projekta v okolju Visual C# Standard

Visual C# . NET

Algoritmi in programski jeziki – 4.letnik

199

• Opcija menija PageSetupDialog – nastavitve strani: za samostojno odpiranje PageSetupDialog-a moramo s pomočjo lstnosti Document najprej seveda določiti dokument (objekt) ki ga želimo poslati tiskalniku, šele nato lahko v pogovornem oknu spreminjamo nastavitve za ta dokument.

private void toolStripMenuItem1_Click(object sender, EventArgs e) { pageSetupDialog1.Document = printDocument1; pageSetupDialog1.ShowDialog(); }

• Opcija menija PageSetupDialog – shranitev nastavitev v gradnik ListBox: izbrane lastnosti

PageSetupDialoga lahko tudi shranimo oz prikažemo uporabniku. V tem primeru so le-te prikazene s pomočjo gradnika ListBox

private void pageSetupNastavitevStraniToolStripMenuItem_Click(object sender, EventArgs e) { pageSetupDialog1.Document = printDocument1; if (pageSetupDialog1.ShowDialog() == DialogResult.OK) { listBox1.Items.Add(pageSetupDialog1.PageSettings.Margins); object[] results = new object[]{ pageSetupDialog1.PageSettings.Margins, pageSetupDialog1.PageSettings.PaperSize, pageSetupDialog1.PageSettings.Landscape, pageSetupDialog1.PrinterSettings.PrinterName, pageSetupDialog1.PrinterSettings.PrintRange}; listBox1.Items.AddRange(results); } }

• Opcija menija Tiskanje slike prikazuje, kako natisnemo poljubno sliko. Sliko moramo najprej s

pomočjo OpenFileDialoga prenesti v dinamično kreiran objekt tipa Bitmap. Tiskanje nam obogoča gradnik razreda PrintDocument (ime gradnika printDocument2), ki pa mu moramo seveda prirediti metodo PrintPage, kjer s pomočjo razreda Graphics povemo, da gre za tiskanje slike.

Bitmap slika; private void tiskanjeSlikToolStripMenuItem_Click(object sender, EventArgs e) { if (openFileDialog1.ShowDialog() == DialogResult.OK) //izberemo sliko { //s PrintDilaogom izbiremo tiskalnik if (printDialog1.ShowDialog() == DialogResult.OK) { //nastavitve za tiskanje naj bodo take, kot smo jih določili v PrintDialogu printDocument2.PrinterSettings = printDialog1.PrinterSettings; //določimo ime TISKARNIŠKEGA posla. TO NI IME dokumenta,ki ga želimo natisniti!! printDocument2.DocumentName = "Tiskanje SLIKE: " + openFileDialog1.FileName; slika=new Bitmap(openFileDialog1.FileName,true); //Pred dokončnim tiskanjem MORAMO definirati še dogodek PtintPage, kjer povemo, // kaj bomo sploh tiskali printDocument2.Print();//dokument sedaj lahko natisnemo } } } //v dogodku PrintPage gradnika printDocument2 povemo, da gre za tiskanje slike private void printDocument2_PrintPage(object sender, System.Drawing.Printing. PrintPageEventArgs e) { //DrawImage natisne sliko na določeno pozicijo. Slika je v originalni velikosti //Pozicija 0,0 določa, da bo slika povsem v zgornjem levem robu izpisa e.Graphics.DrawImage( slika,0,0); }

Page 200: PROŠNJA - uranic.tsckr.siuranic.tsckr.si/VISUAL C#/VISUAL C#.pdf · MICROSOFT VISUAL C# .NET Srečo Uranič . Visual C# . ... Za začetek novega projekta v okolju Visual C# Standard

Visual C# . NET

Algoritmi in programski jeziki – 4.letnik

200

• Opcija menija Tiskanje Tekstovnega dokumenta: primer prikazuje kako natisnemo poljubno tekstovno datoteko. Datoteko izberemo s pomočjo OpenFileDialog – a, za branje pa jo odpremo s pomočjo novega objekta razreda StreamReader. Tiskanje nam omogoča gradnik razreda PrintDocument (ime gradnika printDocument1), ki pa mu moramo seveda prirediti metodo PrintPage, kjer s pomočjo razreda Graphics povemo, da gre za tiskanje besedila (DrawString).

� Pozor: Pred uporabo razreda StreamReader moramo s stavkom using System.IO;

vključiti imenski prostor za delo z vhodno izhodnimi operacijami (IO). Če tega imenskega prostora ne vključimo, pa se na razred StreamReader lahko sklicijemo takole: System.IO.StreamReader

//globalna spremenljivka fileToPrint je objekt razreda StreamReader, ki je namenjen //odpiranju tekstovne datoteke za branje. Nahaja se v imenskem prostoru System.IO StreamReader fileToPrint; Font printFont; string strPrint; private void tiskanjeDokumentaToolStripMenuItem_Click(object sender, EventArgs e) { if (openFileDialog1.ShowDialog() == DialogResult.OK) { //z OpenFile dialogom izberemo datoteko za tiskanje in jo odpremo za branje fileToPrint = new StreamReader(openFileDialog1.FileName); //s PrintDilaogom izbiremo tiskalnik if (printDialog1.ShowDialog() == DialogResult.OK) { //nastavitve za tiskanje naj bodo take, kot smo jih določili v PrintDialogu //če naslednjega stavka na pišemo, bomo tiskali na privzeti tiskalnik!!! printDocument1.PrinterSettings = printDialog1.PrinterSettings; //preverimo, če so nastavitve pravilne if (printDocument1.PrinterSettings.IsValid) { //določimo ime TISKARNIŠKEGA posla-TO NI IME dok.,ki ga želimo natisniti printDocument1.DocumentName = "Ime dokumenta " + openFileDialog1.FileName; //Vsebino datoteke shranimo (preberemo) v string strPrint StreamReader beri = File.OpenText(openFileDialog1.FileName); strPrint = beri.ReadToEnd();//vsebino tekstovne datoteke shranimo v string beri.Close(); //določimo še font za tiskanje. Spremenljivka printFont je definirana kot //globalna spremenljivka printFont = new Font("Arial", 12, FontStyle.Bold | FontStyle.Italic); //Pred dokončnim tiskanjem MORAMO definirati še metodo PrintPage, kjer //povemo, kaj bomo sploh tiskali(glej metodo printDocument1_PrintPage!!!) printDocument1.Print();//dokument sedaj lahko natisnemo } else { MessageBox.Show("Izbrani tiskalnik ni pravilen"); } } } } //Metoda printDocument1_PrintPage je metoda gradnika printDocument1 //v tej metodi povemo, kaj in kako bomo sploh tiskali //razred PrintPageEventArgs hrani nastavitve tiskalnikove strani (rob, font, ...) private void printDocument1_PrintPage(object sender, System.Drawing.Printing.

PrintPageEventArgs e) { int charCount = 0; //charCount: število znakov v vrstici int lineCount = 0; //lineCount: število vrstic na stran //strPrint: tekst, ki ga bomo natisnili //printFont: font, ki smo ga nastavili že prej! Če bi hoteli uporabiti privzeti font, //bi uporabili kar this.Font //metoda MeasureString določi potrebne parametre za tiskanje posamezne strani e.Graphics.MeasureString(strPrint, printFont, e.MarginBounds.Size, StringFormat.GenericTypographic, out charCount, out lineCount); //Natisnemo vrstico e.Graphics.DrawString(strPrint, printFont, Brushes.Black, e.MarginBounds, StringFormat.GenericTypographic); //Odstranimo del stringa, ki je bil že natiskan

Page 201: PROŠNJA - uranic.tsckr.siuranic.tsckr.si/VISUAL C#/VISUAL C#.pdf · MICROSOFT VISUAL C# .NET Srečo Uranič . Visual C# . ... Za začetek novega projekta v okolju Visual C# Standard

Visual C# . NET

Algoritmi in programski jeziki – 4.letnik

201

strPrint = strPrint.Substring(charCount); //Preverimo, če je še kaj za natisniti if (strPrint.Length > 0) e.HasMorePages = true; //Tiskanje se bo nadaljevalo, saj so še nove strani else e.HasMorePages = false; //Tiskanje je končano }

• Opcija menija Tiskanje slike – predogled prikazuje, kako realiziramo predogled tiskanja poljubne

slike. Sliko moramo najprej s pomočjo OpenFileDialoga prenesti v dinamično kreiran objekt tipa Bitmap. Tiskanje nam obogoča gradnik tipa PrintDocument (ime gradnika printDocument2), ki pa mu moramo seveda prirediti metodo PrintPage, kjer s pomočjo razreda Graphics povemo, da gre za tiskanje slike. Predogled nato realiziramo s pomočjo metode ShowDialog razreda PrintPreviewDialog.

private void tiskanjeSlikePredogledToolStripMenuItem_Click(object sender, EventArgs e) { //izberemo sliko if (openFileDialog1.ShowDialog() == DialogResult.OK) { //izberemo tiskalnik if (printDialog1.ShowDialog() == DialogResult.OK) { //določimo ime TISKARNIŠKEGA posla - POZOR: TO NI IME dokumenta, ki ga //želimo natisniti!! printDocument2.DocumentName = "Tiskanje SLIKE: " + openFileDialog1.FileName; //dinamično kreiranje objekta za shranjevanje slike, ki jo želimo natisniti slika = new Bitmap(openFileDialog1.FileName, true); //Pred dokončnim tiskanjem MORAMO definirati še dogodek PtintPage, kjer //povemo, kaj bomo sploh tiskali – dogodek je enak kot pri tiskanju slike printPreviewDialog1.Document = printDocument2; //predogled - sliko si najprej ogledamo na ekranu in jo po želji natisnemo printPreviewDialog1.ShowDialog(); } } }

Vaja: Projekt Menjalnica, ki je bil predstavljen v poglavju o varovalnih blokih nadgradimo tako, da na obrazec dodamo še dva gumba: gumb &atisni (za takojšnje tiskanje podatkov o trenutni menjavi) in gumb &atisni – Predogled (za predogled tiskanja). Pri tiskanju poročila bomo pokazali tudi metodo za risanje vodoravne linije na tiskalniku.

Dodana gumba &atisni in &atisni - Predogled

Page 202: PROŠNJA - uranic.tsckr.siuranic.tsckr.si/VISUAL C#/VISUAL C#.pdf · MICROSOFT VISUAL C# .NET Srečo Uranič . Visual C# . ... Za začetek novega projekta v okolju Visual C# Standard

Visual C# . NET

Algoritmi in programski jeziki – 4.letnik

202

Tiskanje bomo realizirali ne da bi prej podatke shranjevali v datoteko – podatke bomo pošiljali neposredno v tiskalnik. Izpis bo seveda grafičen, zaradi česar bomo morali sami poskrbeti za obliko in položaj izpisa na papirju. Pri izpisu bomo seveda uporabili podatke s trenutnega obrazca. Gumba sta na začetku onemogočena. Šele ko uporabnik prvič pritisne gumb Izračunaj in se izračun tudi izvrši, postaneta gumba aktivna in s tem tudi možen prvi izpis. Ker bomo pri oblikovanju poročila uporabljali različne fonte, potrebujemo še objekt tipa Font – imenovali smo ga printFont, deklarirali pa smo ga kot globalno spremenljivko/objekt pred pisanjem metode za izpis. Gumb &atisni Font printFont; //globalna spremenljivka/objekt razreda Font private void bNatisni_Click(object sender, EventArgs e) { //s PrintDilaogom izbiremo tiskalnik if (printDialog1.ShowDialog() == DialogResult.OK) { //nastavitve za tiskanje naj bodo take, kot smo jih določili v PrintDialogu printDocument1.PrinterSettings = printDialog1.PrinterSettings; //določimo ime TISKARNIŠKEGA posla - TO NI IME dokumenta, ki ga tiskamo!! printDocument1.DocumentName = "Tiskanje dokumenta - menjalnica" ; //Pred dokončnim tiskanjem MORAMO definirati še dogodek PrintPage, kjer povemo, //kaj bomo sploh tiskali printDocument1.Print();//dokument sedaj lahko natisnemo } }

Dogodek PrintPage gradnika printDialog1: //Metoda printDocument1_PrintPage je metoda gradnika printDocument1 //v vtej metodi povemo, kaj in kako bomo sploh tiskali private void printDocument1_PrintPage(object sender,

System.Drawing.Printing.PrintPageEventArgs e) { float yPos = 0; //e.MarginBounds je pravokotno področje namenjeno tiskanju na tiskalnikovi strani float leviRob = e.MarginBounds.Left; //oddaljenost od levega roba strani float zgornjiRob = e.MarginBounds.Top;//oddaljenost od vrha strani string line; line = "Menjalnica Tolarček"; //Naslov poročila printFont = new Font("Arial", 15, FontStyle.Bold);//pisave bo večja, font Bold yPos = zgornjiRob + printFont.GetHeight(e.Graphics); //stiskamo prvi string e.Graphics.DrawString(line, printFont, Brushes.Black, leviRob, yPos, new StringFormat()); printFont = new Font("Arial", 10, FontStyle.Regular); //manjše črke, font Regular line = "Menjava EUR v "+valuta; yPos = yPos + 5*printFont.GetHeight(e.Graphics); //pozicija naslednje vrstice //stiskamo drugi string e.Graphics.DrawString(line, printFont, Brushes.Black, leviRob, yPos, new StringFormat()); line = "Znesek EUR : "+tEZnesek.Text; yPos = yPos + 2*printFont.GetHeight(e.Graphics); //stiskamo tretji string e.Graphics.DrawString(line, printFont, Brushes.Black, leviRob, yPos, new StringFormat()); line = "Menjalni tečaj : " + tecaj; yPos = yPos + 2 * printFont.GetHeight(e.Graphics); //stiskamo cetrti string e.Graphics.DrawString(line, printFont, Brushes.Black, leviRob, yPos, new StringFormat()); //Natisnemo vodoravno linijo. e.Graphics.DrawLine(new Pen(Color.Black, 1), new Point(100, 300), new Point(300, 300)); line = "Znesek v " + valuta + " : " + tERezultat.Text; yPos = yPos + 3 * printFont.GetHeight(e.Graphics); //stiskamo peti string e.Graphics.DrawString(line, printFont, Brushes.Black, leviRob, yPos, new StringFormat()); //Natisnemo dve vodoravni liniji e.Graphics.DrawLine(new Pen(Color.Black, 1), new Point(100, 345), new Point(300, 345)); e.Graphics.DrawLine(new Pen(Color.Black, 1), new Point(100, 347), new Point(300, 347)); //z lastnostjo HasMorePages povemo, da je poročilo zaključeno in da lahko pričnemo // s tiskanjem e.HasMorePages = false;

Page 203: PROŠNJA - uranic.tsckr.siuranic.tsckr.si/VISUAL C#/VISUAL C#.pdf · MICROSOFT VISUAL C# .NET Srečo Uranič . Visual C# . ... Za začetek novega projekta v okolju Visual C# Standard

Visual C# . NET

Algoritmi in programski jeziki – 4.letnik

203

}

Gumb Natisni – Predogled: vsak izpis si lahko pred tiskanjem ogledamo na zaslonu. Uporabimo gradnik PrintPreviewDialog. Tudi za kreiranje predogleda potrebujemo najprej gradnik PrintDocument, saj tu določimo dogodek PrintPage s katerim povemo, kaj bomo tiskali. Dogodek PrintPage, ki je potreben tudi za naš predogled, je enak kot pri tiskanju neposredno na tiskalnik. private void bPredogledTiskanja_Click(object sender, EventArgs e) { //določimo ime TISKARNIŠKEGA posla printDocument1.DocumentName = "Tiskanje dokumenta - menjalnica"; //povežemo gradnika printPreviewDialog1 in printDocument1 printPreviewDialog1.Document = printDocument1; //Pred dokončnim tiskanjem MORAMO definirati še dogodek PrintPage, kjer povemo, kaj //bomo sploh tiskali printPreviewDialog1.ShowDialog();//klic pedogleda }

Predogled tiskanja je na ekranu prikazan v oknu, ki vsebuje nekaj dodatnih gumbov: natisni na tiskalniku povečava prikaz ene, dveh, treh, štirih in šestih strani hkrati zapiranje predogleda Premik po straneh

Page 204: PROŠNJA - uranic.tsckr.siuranic.tsckr.si/VISUAL C#/VISUAL C#.pdf · MICROSOFT VISUAL C# .NET Srečo Uranič . Visual C# . ... Za začetek novega projekta v okolju Visual C# Standard

Visual C# . NET

Algoritmi in programski jeziki – 4.letnik

204

MDI – Multi Document Interface (vmesnik z več dokumenti) Zahtevnejši projekti navadno vsebujejo več obrazcev, saj z enim samim obrazcem uporabniku ne moremo ponuditi vse potrebnih rešitev oz. opcij. Dejstvo pa je, da uporabniku ponudimo en sam glavni obrazec, glede na njegove želje pa mu potem prikazujemo nove obrazce. Ti so lahko standardna pogovorna okna okolja Windows, lahko so pogovorna okna, ki jih ustvarimo na obrazcu, ali pa čisto navadni obrazci, ki jih odpiramo s pomočjo statične metode Show. Pri oblikovanju uporabniškega vmesnika in povezovanju obrazcev pa ločimo:

• Povezovanje obrazcev pri vmesniku SDI (Single Document Interface – vmesnik z enim samim dokumentom). Na tak način smo obrazce povezovali v vseh dosedanjih projektih. Izraz SDI ni najbolj posrečen, saj imamo lahko pri večličnih obrazcih tudi znotraj enega okna več odprtih dokumentov. SDI bi bolje prevedli kot vmesnik enakovrednih oken.

• Povezovanje obrazcev pri vmesniku MDI (Multi Document Interface – vmesnik z več dokumenti). MDI je vrsta uporabniškega vmesnika, kjer si okna s stališča programerja (in tudi uporabnika) niso enakovredna.

MDI uporabniški vmesnik navadno vsebuje natanko en obrazec tipa MDI (lastnost obrazca IsMdiContainer postavimo na true). Temu obrazcu pravimo tudi MDI parent (starševski obrazec). V zagnanem programu lahko uporabnik odpre poljubno število enakovrednih podobrazcev in ima v vsakem od njih drugačno vsebino – tem obrazcem pa pravimo MDI child (otroški obrazci). Primer MDI uporabniškega vmesnika so na primer urejevalniki besedil, v katerih imamo lahko odprtih več oken z različnimi besedili, pri čemer imamo dostop do skupnih urejevalnih orodij. Vsebniški odnos med obrazci se vzpostavi šele ob zagonu programa. Posledica tega je, da se otroški obrazci (podobrazci) postavijo znotraj glavnega obrazca (obrazca tipa MDI). Med izvajanjem programa podoken ne moremo postaviti zunaj glavnega okna. Pri takem poskusu dobi glavno okno na robovih drsnike.

Glavni meni Orodjarna MDI parent obrazec (glavno okno) MDI child (otroški obrazec) MDI child (otroški obrazec)

Statusna vrstica

Na glavni obrazec (MDI parent) navadno postavimo glavni meni, statusno vrstico in orodjarno. Vsi meniji, ki so na voljo otroškim obrazcem, morajo biti na starševskem obrazcu. Če namreč uporabnik preklaplja med otroškimi obrazci, se mora glavni meni nanašati na izbrani otroški obrazec. Ker lahko uporabnik odpre poljubno število otroških obrazcev (podoken), vseh obrazcev za vsa podokna ne moremo izdelati vnaprej, ampak izoblikujemo le enega, ki služi kot vzorec za izdelavo, dejanske podobrazce pa kreiramo dinamično.

Page 205: PROŠNJA - uranic.tsckr.siuranic.tsckr.si/VISUAL C#/VISUAL C#.pdf · MICROSOFT VISUAL C# .NET Srečo Uranič . Visual C# . ... Za začetek novega projekta v okolju Visual C# Standard

Visual C# . NET

Algoritmi in programski jeziki – 4.letnik

205

Vaja: Kreirajmo preprost primer MDI aplikacije. Glavni obrazec oblikujmo takole: Obrazcu naj bo ime fMDIParent, lastnost IsMdiContainer postavimo na true – na ta način smo obrazec označili kot starševski obrazec, ki bo vseboval otroške obrazce. V glavnem meniju imamo tri opcije (Datoteka,

Okno in Pomoč), postavka Datoteka ima tri podopcije (&ova, Zapri in Izhod), postavka Okno pa ravno tako tri podopcije (Kaskade, Horizontalna razporeditev in Vertikalna razporeditev).

Pred pisanjem kode kreiramo še vzorec otroškega obrazca: meni Project, nato Add Windows Form … . Za ime

obrazca zapišimo MDIChild:

POZOR: V starejših verzijah Visual C# se je za oblikovanje menija uporabljal gradnik MainMenu, v novejših pa se uporablja gradnik MenuStrip (še vedno pa lahko uporabljamo tudi gradnik MainMenu – Copy/Paste) . Največja razlika med njima je nastavitev ustreznih lastnosti glede zlivanja menijev na starševskem in na otroških obrazcih. Pri gradniku MainMenu realiziramo zlivanje s pomočjo nastavitev lastnosti MergeType in MergeOrder (postopek je opisan v naslednji vaji), pri gradniku MenuStrip pa s pomočjo lastnosti MergeAction. Če npr. želimo, da meni na otroškem obrazcu nadomesti meni na glavnem obrazcu, nastavimo lastnost MergeAction na Replace. Otroški obrazec MDIChild naj bo takle:

Page 206: PROŠNJA - uranic.tsckr.siuranic.tsckr.si/VISUAL C#/VISUAL C#.pdf · MICROSOFT VISUAL C# .NET Srečo Uranič . Visual C# . ... Za začetek novega projekta v okolju Visual C# Standard

Visual C# . NET

Algoritmi in programski jeziki – 4.letnik

206

Meni otroškega obrazca (ime gradnika = childMenu). Meni naj ima eno samo opcijo: Datoteka in pod njo opcijo Shrani. Lastnost MergeType nastavimo kot MergeItems. S to lastnostjo smo določili, da se bo ob odpiranju otroškega obrazca meni otroškega obrazca zlil (spojil) z menijem glavnega obrazca. Kliknimo še opcijo Shrani in nastavimo lastnost MergeOrder na 1. TextBox (ime gradnika = editData). Gradniku priredimo lastnost BackColor = Window in lastnost Dock = fill (gradnik TextBox bo tako zavzel celotno površino znotraj otroškega obrazca, pa še kasneje, ko bo program zagnan, se bo velikost gradnika prilagajala velikosti okna!). Gradnik SaveFileDialog z imenom saveFileDialog bomo potrebovali za shranjevanje vsebine TextBox-a v poljubno datoteko, ki jo bomo poiskali

preko SaveFileDialoga. Odzivna metoda za shranjevanje (dogodek Click) opcije Shrani je takale: private void shraniItem_Click(object sender, EventArgs e) { //ime datoteke izberemo s pomočjo SaveFileDialoga if (saveFileDialog.ShowDialog() == DialogResult.OK) { string datoteka = saveFileDialog.FileName; //kreiramo novo datoteko. Če datoteka s tem imenom že obstaja, jo prepišemo z novo vsebino StreamWriter pisi = File.CreateText(datoteka); try { foreach (string line in editData.Lines) { pisi.WriteLine(line); //posamezno vrstico gradnika editData zapišemo v datoteko } } finally { pisi.Close(); } } else MessageBox.Show("Podatki niso shranjeni!"); }

Za shranjevanje podatkov tekstovno datoteko smo uporabili razreda Stream in StreamWriter. Za njuno uporabo moramo najprej na začetku datoteke s kodo obrazca MDIChild zapisati stavek using System.IO; - dodajanje ustreznega imenskega prostora, v katerem sta deklarirana in definirana oba razreda Stream in StreamWriter, ki ju bomo rabili v nadaljevanju pri pisanju kode. Metoda saveFileDialog.OpenFile() skuša odpreti datoteko, ki smo jo zbrali v SaveFileDialog-u. Rezultat odpiranja je vrednost null, če odpiranje ni uspelo, sicer pa metoda vrne kazalec na izbrano datoteko. Vrednost, ki jo metoda OpenFile vrne smo v kodi shranili v instanco razreda Stream z imenom shraniDat. Nato smo kreirali nov objekt tipa StreamWriter z imenom saveWriter, ki deduje lastnosti objekta shraniDat. Objekti tipa StreamWriter poznajo številne metode za delo s tekstovnimi datotekami, med njimi tudi metodo WriteLine, ki smo jo v zgornjem primeru uporabili za pisanje posameznih vrstic gradnika TextBox v datoteko. V zanki foreach pa smo za vsako vrstico gradnika TextBox (ime gradnika je editData) zapisali kodo za vpis v datoteko (saveWriter.WriteLine(line)). Metoda saveWriter.Close() zapre objekt saveWriter (ki je tipa StreamWriter) in obenem še pripadajoči objekt tipa stream z imenom shraniDat. Še kratka razlaga lastnosti MergeOrder, ki smo jo postavki Shrani v meniju nastavili na 1. Pri zlivanju menijev lastnost MergeOrder določa vrstni red, po katerem se bodo prikazale postavke menija. Obstaja tudi možnost, da imajo različni meniji nastavljeno enako vrednost MergeOrder. V takem primeru ne pride do napake v delovanju programa, ampak se nekatere postavke prikažejo prej, druge kasneje: postavke glavnega menija (na glavnem meniju je privzeta vrednost MergeOrder enaka 0) imajo prednost pred postavkami na menijih ostalih obrazcev. Lastnost MergeOrder postavimo na 1 tudi postavki separator (ločilna črta) menija na otroškem obrazcu.

Page 207: PROŠNJA - uranic.tsckr.siuranic.tsckr.si/VISUAL C#/VISUAL C#.pdf · MICROSOFT VISUAL C# .NET Srečo Uranič . Visual C# . ... Za začetek novega projekta v okolju Visual C# Standard

Visual C# . NET

Algoritmi in programski jeziki – 4.letnik

207

� Opomba: Pri zlivanju menijev moramo paziti, da imata oba menija, tako starševski kot tisti na

otroškem obrazcu, lastnost MergeType nastavljeno na MergeItems – v nasprotnem primeru zlivanje menijev ne bo pravilno.

Obrazec MDIChild sedaj shranimo. Uporabnik bo lahko odprl poljubno število izvodov (instanc) tega obrazca. Odprimo spet glavni obrazec fMDIParent. Kliknimo na gradnik mdiMenu , nato na njegovo postavko Datoteka in ji Lastnost MergeType nastavimo kot MergeItems (enako kot smo to storili že na otroškem obrazcu). Podobno nastavimo lastnost MergeOrder opcije &ova in Zapri na 0, opcije Izhod pa na 2. To pomeni, da bo opcija Izhod v zlitem meniju prikazana za opcijo Shrani. Deklarirajmo in inicializirajmo še celoštevilsko spremenljivko childCount, s pomočjo katere bomo sledili, koliko otroških oken je že odprtih. Deklaracijo zapišimo kamorkoli znotraj razreda fMDIParent (Code View), najbolje kar takoj na začetku. public partial class fMDIParent : Form { //spremenljivka, ki hrani število odprtih otroških oken private int childCount = 0; ... Ostale metode ... ... }

Napišimo zdaj dogodek Click menijske postavke Datoteka -> &ova: preklopimo na Designer View in menijski postavki Datoteka -> &ova: priredimo dogodek Click s kodo za dinamično odpiranje otroškega obrazca. private void novaItem_Click(object sender, EventArgs e) { //Dinamično kreiramo novo otroško okno; //Obrazec smo prej že pripravili, njegovo ime je MDIChild MDIChild childForm = new MDIChild();//Dinamično kreiranje novega otroškega okna //Določimo starševsko okno. Kazalec this kaže seveda na nglavno okno MDIParent childForm.MdiParent = this; childCount++; childForm.Text = childForm.Text + " " + childCount; //Napis na otroškem oknu childForm.Show();//Odpremo //Odprli smo novo otroško okno, zato povečamo spremenljivko ki hrani skupno število //odprtih otroških oken }

Ob vsakem kliku na postavko &ova menija Datoteka, se bo odprlo novo otroško okno, v imenu okna pa bo

podatek, koliko otroških oken je že odprtih.

Page 208: PROŠNJA - uranic.tsckr.siuranic.tsckr.si/VISUAL C#/VISUAL C#.pdf · MICROSOFT VISUAL C# .NET Srečo Uranič . Visual C# . ... Za začetek novega projekta v okolju Visual C# Standard

Visual C# . NET

Algoritmi in programski jeziki – 4.letnik

208

Postavki Zapri v glavnem meniju pa priredimo dogodek Click za zapiranje tekočega otroškega obrazca: private void zapriItem_Click(object sender, EventArgs e) { //ugotovimo če je še izbrano kakšno aktivno otroško okno if (this.ActiveMdiChild != null) this.ActiveMdiChild.Close(); }

Metoda ActiveMdiChild vrne podatek o tem, katero otroško okno je trenutno aktivno, da ga bomo zaprli. Če še nobeno okno ni odprto, metoda ActiveMdiChild vrne vrednost null. Zapisati moramo še odzivne metode za razporejanje oken iz glavnega menija:

• Postavitev oken v kaskade

private void cascadeItem_Click(object sender, EventArgs e) { this.LayoutMdi(MdiLayout.Cascade); }

• Horizontalna razporeditev oken

private void horizontalItem_Click(object sender, EventArgs e) { this.LayoutMdi(MdiLayout.TileHorizontal); }

• Vertikalna razporeditev oken

private void verticalItem_Click(object sender, EventArgs e) { this.LayoutMdi(MdiLayout.TileVertical); }

Ostalo nam je še okno za pomoč. Okno za prikaz pomoči kreiramo in tudi odpremo kot običajen obrazec.

Izberimo opcijo glavnega menija Project, nato Add Windows Form … . Za ime obrazca zapišimo Vizitka. Obrazec shranimo in nato napišimo dogodek Click postavke menija Pomoč:

private void aboutItem_Click(object sender, EventArgs e) { fVizitka aboutDialog = new fVizitka(); aboutDialog.ShowDialog(); }

Obrazec Vizitka smo odprli modalno, ker pa to ni otroški obrazec (saj smo ga odprli z metodo ShowDialog in ne z metodo Show() ), ga lahko postavimo kamorkoli izven starševskega obrazca.

Page 209: PROŠNJA - uranic.tsckr.siuranic.tsckr.si/VISUAL C#/VISUAL C#.pdf · MICROSOFT VISUAL C# .NET Srečo Uranič . Visual C# . ... Za začetek novega projekta v okolju Visual C# Standard

Visual C# . NET

Algoritmi in programski jeziki – 4.letnik

209

Meniji in menijske postavke imajo lastnosti MergeOrder in MergeType, ki nam omogočajo, da sami določimo, kako se bodo meniji zlivali. To je zelo uporabno, saj otroški obrazci tako vedno prikažejo svoj meni znotraj menija na glavnem obrazcu. Seveda pa to ne velja za orodjarno. Vsak otroški obrazec lahko uporablja svojo orodjarno kot del obrazca. Če hočemo orodjarno glavnega obrazca zliti z orodjarno otroškega obrazca, moramo to narediti programsko, s kodo.

Page 210: PROŠNJA - uranic.tsckr.siuranic.tsckr.si/VISUAL C#/VISUAL C#.pdf · MICROSOFT VISUAL C# .NET Srečo Uranič . Visual C# . ... Za začetek novega projekta v okolju Visual C# Standard

Visual C# . NET

Algoritmi in programski jeziki – 4.letnik

210

Razhroščevalnik / Debugger – izvajanje in iskanje napak Kreiranje programskega vmesnika, nastavitev lastnosti gradnikom in zapis programske kode je le prva faza izdelave nekega projekta. Druga faza je prevajanje, povezovanje in izvajanje programa, njegov preizkus ter odpravljanje napak. Če smo pri pisanju kode naredili sintaktično (skladenjsko) napako ali nepravilno uporabili podatkovne tipe, nas na to v večini primerov opozori že prevajalnik (Compile Time Error oz. Run Time Error – o tem je bilo govora že v prvem delu te literature). Preveden program je tako že precej očiščen napak, a žal le sintaktičnih, ne pa tudi semantičnih oz. logičnih napak (pomenskih). Logičnih napak v programu ne odkrije noben prevajalnik. Tudi sami jih včasih zelo težko odkrijemo in odpravimo. Pri tem pa nam Visual C# pomaga z vgrajenim razhroščevalnikom (debuggerjem). Na voljo imamo nastavljanje prekinitvenih točk, vrednotenje izrazov kar med samim izvajanjem ter spremljanje in popravljanje vrednosti spremenljivk. Program pa lahko med izvajanjem prekinemo in ga izvajamo po korakih.

Koračno izvajanje programa Vsak program lahko zaženemo brez prekinitve (v meniju Debug kliknimo opcijo Start Without Debugging oziroma Ctrl + F5 ali pa Start Debugging oziroma F5 ali pa kliknemo gumb Start Debugging v orodjarni), lahko pa ga izvajamo stavek za stavkom – koračno. Pri koračnem izvajanju programa so na voljo tri opcije

• Debug -> Step Into (F11) - ob vsakem klicu lastne metode (funkcije) se poglobimo še v njegovo kodo; • Debug -> Step Over (F10) – premikanje preko klicev metod (funkcij) na nov stavek (ne zanima nas

notranjost neke metode, torej grem kar preko!). • Run To Cursor (Ctrl+F10) – za preskok nezanimivih delov kode

Praviloma ob zagonu programa ne pričnemo takoj s koračnim izvajanjem, ampak program poženemo, ga prekinemo in od te točke naprej izvajamo koračno. Takšne točke lahko postavimo po različnih mestih znotraj našega projekta. Pravimo jim prekinitvene točke (breakpoints).

Prekinitvene točke - breakpoints

Prekinitvene točke lahko postavimo ali zbrišemo na tri načine. Najprej izberemo vrstico, kjer želimo nastaviti prekinitveno točko, nato pa izberemo eno od treh možnosti:

• Debug -> Toggle Breakpoint • Tipka F9 • Klik z miško v levem robu ustrezne vrstice s kodo

Na ekranu vidimo prekinitveno točko kot rdečo piko na levem robu okna s kodo, celotna vrstica pa je pobarvana rdeče. Prekinitvene točke

Page 211: PROŠNJA - uranic.tsckr.siuranic.tsckr.si/VISUAL C#/VISUAL C#.pdf · MICROSOFT VISUAL C# .NET Srečo Uranič . Visual C# . ... Za začetek novega projekta v okolju Visual C# Standard

Visual C# . NET

Algoritmi in programski jeziki – 4.letnik

211

Po končanem nastavljanju prekinitvenih točk program poženemo in v njem nemoteno delamo. Ko pridemo do prekinitvene toče, se izvajanje programa ustavi. Program je še vedno v pomnilniku, le delovanje je ustavljeno za toliko časa, da bomo pregledali vrednosti spremenljivk, oziroma raziskali del programa ki ne dela tako kot smo pričakovali. Od tu dalje ga lahko izvajamo korak za korakom, s pomočjo tipke F11 (Step Into) ali F10 (Step Over).

� Opomba: Ogled vseh nastavljenih prekinitvenih točk je možen v Debug -> Windows - > Breakpoints – a le v polni verziji Visual C#. Visula C# Express te opcije nima!

Na naslednjo prekinitveno točko se prestavimo s tipko F5. Postopek imenujem razhroščevanje – debugging.

Razhroščevanje - debugging Razhroščevanje nam ponuja številne možnosti za spremljanje poteka programa in vrednosti spremenljivk. Poglejmo le nekatere izmed njih:

• Dodatne prekinitvene točke lahko poljubno nastavljamo kar med samim razhroščevanjem. Postopek je enak kot pri začetnem nastavljanju prekinitev;

• Visualizer: med razhroščevanjem se lahko postavimo na poljubno spremenljivko, in pod njo se pokaže

vrstica z vrednostjo te spremenljivke in LUPO- če kliknemo nanjo se vrednost spremenljivke pokaže v posebnem oknu ( to je UPORABNO predvsem če ima spremenljivka neko

dolgo vrednost, npr. nek dolg string );

• Med razhroščevanjem se lahko v tekoči vrstici postavimo na neko spremenljivko in ji poljubno spremenimo vrednost:

• Med samim razhroščevanjem lahko v okno Watch potegnemo (drag & drop) poljubno spremenljivko in v tem oknu nato spreminjamo vrednosti spremenljivk. Opcija je dvosmerna (kar naredimo v Watch oknu se odseva v programu in obratno!!!);

• V Watch okno lahko pišemo izraze. V oknu Watch se postavimo na določeno spremenljivko, kliknemo

desni miškin gumb, izberemo opcijo Edit value in nato v oknu zapišemo ustrezen izraz:

• V Watch oknu imamo na voljo še nekaj možnosti. Če kliknemo z levo miškino tipko kamorkoli v to okno, lahko nato z desnim klikom miške odpremo meni za kopiranje, lepljenje, editiranje, dodajanje, brisanje ene ali brisanje vseh prekinitev;

Page 212: PROŠNJA - uranic.tsckr.siuranic.tsckr.si/VISUAL C#/VISUAL C#.pdf · MICROSOFT VISUAL C# .NET Srečo Uranič . Visual C# . ... Za začetek novega projekta v okolju Visual C# Standard

Visual C# . NET

Algoritmi in programski jeziki – 4.letnik

212

• Med razhroščevanjem lahko postavljamo tudi nove pogoje ( a le v kompletni verziji Visual C#); • Med razhroščevanjem lahko vklopimo prikaz okna Locals (Debug -> Windows -> Locals). V tem

oknu so prikazane vse lokalne spremenljivke tekočega bloka. Informacije v tem oknu lahko tudi spreminjamo, a mora biti debugger v načinu break;

• Med razhroščevanjem lahko popravljamo kodo in nadaljujemo z razhroščevanjem ne da bi projekt

ponovno zagnali;

• V oknu Call Stack (Debug -> Windows -> Call Stack) so navedeni vsi klici metod, ki so bile izvedene preden je prišlo do tekoče prekinitve (breakpointa)

• Okno Immediate (Debug -> Windows -> Immediate) se uporablja za ugotavljanje trenutne vrednosti

spremenljivk, vrednosti izrazov oz. izvajanju stavkov, ki jih želimo izvesti kar med samim razhroščevanjem. Če hočemo izvedeti za trenutno vrednost neke spremenljivke, moramo v tekoči vrstici okna Immediate najprej obvezno zapisati znak > , nato znak ? in potem še ime spremenljivke (lahko pa tudi zapišemo poljuben izraz) in končno stisnemo tipko <Enter>. V naslednji vrstice se nam prikaže ustrezna vrednost. (namesto znaka ? lahko napišemo tudi Debug.Print).

• Med razhroščevanjem lahko v meniju Debug izberemo opcijo Restart in tako razhroščevanje

poženemo od začetka;

• Okno Debug -> Windows -> Output lahko uporabimo za prikaz statusnih sporočil za različne posebnosti razvojnega okolja.

Polna verzija Visual C# ponuja še nekaj drugih možnosti, a opcije ki jih ponuja Express verzija nam zaenkrat zadoščajo!

Page 213: PROŠNJA - uranic.tsckr.siuranic.tsckr.si/VISUAL C#/VISUAL C#.pdf · MICROSOFT VISUAL C# .NET Srečo Uranič . Visual C# . ... Za začetek novega projekta v okolju Visual C# Standard

Visual C# . NET

Algoritmi in programski jeziki – 4.letnik

213

Osnove grafike: GDI+ (Graphical Device Interface)

Razumevanje geometrije okenskih obrazcev GDI+ je del operacijskega sistema, ki zagotavlja dvodimenzionalno vektorsko grafiko, slikanje in tipografijo. V dosedanjem delu smo grafično podobo projekta gradili tako, da smo nanj postavljali gradnike (objekte), sedaj pa bomo prikazali, kako lahko rišemo oz. slikamo neposredno na obrazec, ali pa na ostale gradnike, na katerih je možna poslikava (to so npr. gradniki Button, CheckBox, Label, &umericUpDown, PictureBox, FlowLayoutPanel, GroupBox, Panel, TableLayoutPanel,…). Vsi našteti gradniki, vključno z obrazcem, zaznavajo sistemski dogodek Paint. S tem dogodkom Okna sporočajo gradniku, da ga je treba ponovno poslikati. To se na primer zgodi, ko gradnik postane nanovo dejaven, ko postane nanovo viden, ali pa ko se mu spremeni velikost. Ti gradniki imajo v oknu Properties med svojimi dogodki tudi dogodek Paint. Za temeljno razumevanje risanja pa je pomembno da vemo naslednje. Kadar za neko poslikavo želimo, da se izvede samostojno (npr. da se obrazec poslika že ob odpiranju, ali pa da se npr. neka plošča oz. Panel poslika z našo grafiko že ob prvem odpiranju, ..), bomo kodo namenjeno poslikavi napisali kar v dogodek Paint teh gradnikov. Če pa želimo, da se poslikava izvede npr. ob kliku na nek gumb ali pa npr. ob premiku miške nad gradnikom, moramo v ustreznem dogodku tega gradnika (dogodek Click, dogodek MouseMove, …) ta gradnik najprej pripraviti za poslikavo (kreirati objekt razreda Graphics), nato pa z ustrezno metodo gradnik poslikati (npr. z metodo DrawString, DrawLine, DrawRectangle,…). Več o tem bo zapisano v nadaljevanju, najprej pa moramo spoznati nekaj osnovnih pojmov oz. razredov, ki jih bomo potrebovali pri uporabi grafičnih metod.

Določanje pozicije s pomočjo strukture Point

Struktura Point predstavlja lokacijo/pozicijo na dvodimenzionalni površini obrazca v okenski aplikaciji. Najpogosteje se uporablja za določanje pozicije obrazcev oziroma gradnikov na njem, ter za določanje meja pri risanju črt, pravokotnikov in drugih likov. Lastnosti strukture Point:

Lastnost Razlaga

X določa koordinato x, oz. vodoravni položaj strukture Point, enota je pika (pixel)

Y določa koordinato y, oz. navpični položaj strukture Point.

isEmpty Ugotavljanje, ali je struktura Point prazna.

Primer: Point pt = new Point();//pt je nov objekt tipa Point, ki pa še nima določenih koordinat if (pt.IsEmpty) { //pt je prazna struktura tipa Point } int x = 15;//vrednost v pikah (pixel) int y = 15; //pTocka je nov objekt tipa Point, ki ima že definarani koordinati Point pTocka = new Point(x, y);

Page 214: PROŠNJA - uranic.tsckr.siuranic.tsckr.si/VISUAL C#/VISUAL C#.pdf · MICROSOFT VISUAL C# .NET Srečo Uranič . Visual C# . ... Za začetek novega projekta v okolju Visual C# Standard

Visual C# . NET

Algoritmi in programski jeziki – 4.letnik

214

Struktura PointF je podobna strukturi Point, omogoča pa večjo natančnost, saj sta koordinati take točke števili tipa float. Lastnosti strukture PointF:

Lastnost Razlaga

X določa koordinato x (lahko je vrednost tipa float), oz. vodoravni položaj strukture Point.

Y določa koordinato y(lahko je vrednost tipa float), oz. navpični položaj strukture Point.

isEmpty Ugotavljanje, ali je struktura Point prazna.

Primer: PointF pF = new Point();//pt je nov objekt tipa PointF, ki pa še nima določenih koordinat if (pF.IsEmpty) { //pF je prazna struktura tipa Point } float x1 = 15.4f; float y1 = 15.5f; //pTocka1 je nov objekt tipa PointF, ki ima že definarani koordinati PointF pTocka1 = new PointF(x1, y1);

Določanje velikosti vidnih elementov

Za določanje velikosti (dimenzije) vidnih elementov v okenskih aplikacijah (obrazcev, gradnikov in ostalih pravokotnih elementov) uporabljamo strukturo Size. Lastnosti strukture Size:

Lastnost Razlaga

Width Nastavitev oz. pridobivanje vodoravne komponente.

Height Nastavitev oz. pridobivanje navpične komponente.

isEmpty Ugotavljanje, ali ima objekt višino in širino enako 0.

Nov objekt tipa Size lahko ustvarimo na dva načina:

• Posredujemo mu podatke o že obstoječi točki. V tem primeru pomeni x koordinata točke širino, y koordinata pa višino novega objekta

Point pt = new Point(10,20); Size mySize=new Size(pt); //Širina je 10, višina pa 20

• Širino in višino mu posredujemo neposredno

int visina = 10; int sirina = 20; Size mySize1 = new Size(visina,sirina);

Struktura SizeF je podobna strukturi Size, omogoča pa večjo natančnost, saj sta lastnosti Width in Height tipa float.

Page 215: PROŠNJA - uranic.tsckr.siuranic.tsckr.si/VISUAL C#/VISUAL C#.pdf · MICROSOFT VISUAL C# .NET Srečo Uranič . Visual C# . ... Za začetek novega projekta v okolju Visual C# Standard

Visual C# . NET

Algoritmi in programski jeziki – 4.letnik

215

Struktura Rectangle (pravokotnik)

Struktura Rectangle določa pozicijo in velikost pravokotnika in se uporablja za opisovanje obrazcev in gradnikov. Lastnosti strukture Rectangle:

Lastnost Razlaga

Bottom Pridobivanje y–koordinate, ki je vsota lastnosti Y in Height.

Height Pridobivanje oz. določanje višine strukture.

IsEmpty Ugotavljanje, ali imajo vse numerične lastnosti strukture vrednost 0.

Left Pridobivanje x-koordinate levega roba strukture.

Location Pridobivanje ali določanje zgornjega levega roba strukture.

Right Pridobivanje x-koordinate, ki je vsota lastnosti X in Width.

Size Pridobivanje oz. določanje dimenzije strukture.

Top Pridobivanje y-koordinate zgornjega roba strukture

Width Pridobivanje oz. določanje širine strukture

X Pridobivanje oz. določanje x-koordinate zgornjega levega roba strukture

Y Pridobivanje oz. določanje y-koordinate zgornjega levega roba strukture

Nov objekt tipa Rectangle lahko ustvarimo na dva načina:

• Posredujemo mu podatke o že obstoječi točki in že obstoječi dimenziji:

Point zacetnaTocka = new Point(10,20); Size Dimenzija=new Size(200,300); //zgorni levi rob predstavlja točka (10,20), širina pravokotnika je 200, višina pa 300 Rectangle pravokotnik = new Rectangle(zacetnaTocka, Dimenzija);

• Posredujemo mu eksplicitne podatke o začetni točki in dimeziji:

int levo = 10; //x koordinata pravokotnikovega zgornjega levega roba int zgoraj = 20; //y koordinata pravokotnikovega zgornjega levega roba int sirina = 200; int visina = 300; Rectangle pravokotnik = new Rectangle(levo,zgoraj,sirina,visina);

Struktura Rectangle se najpogosteje uporablja pri risanju oz. pri operacijah na uporabnikovi delovni površine (to je npr. del obrazca pod naslovno vrstico in menijem (če le ta obstaja) ter orodjarno (če le ta obstaja) ipd. Velikost tega pravokotnika določimo tako, da mu določimo kar lastnost ClientRectangle, ki hrani velikost obrazca. //velikost pravokotnika je enaka razpoložljivi delovni površini obrazca Rectangle MojPravokotnik = ClientRectangle;

Struktura RectangleF je podobna strukturi Rectangle, omogoča pa večjo natančnost, saj so njene numerične lastnosti tipa float.

Uporaba razreda Graphics Razred Graphics je eden izmed osnovnih razredov, ki jih uporabljamo pri programiranju obrazcev in GDI+. Preko tega razreda imamo npr. dostop do zaslonske resolucije ali pa do zmožnosti naprav. Že na začetku tega

Page 216: PROŠNJA - uranic.tsckr.siuranic.tsckr.si/VISUAL C#/VISUAL C#.pdf · MICROSOFT VISUAL C# .NET Srečo Uranič . Visual C# . ... Za začetek novega projekta v okolju Visual C# Standard

Visual C# . NET

Algoritmi in programski jeziki – 4.letnik

216

poglavja smo omenili, da moramo v primeru, da želimo izvesti poslikavo npr. ob kliku na gumb, ob premiku miške ipd. površino gradnika najprej pripraviti za poslikavo. To storimo z deklaracijo objekta tipa Graphics. //priprava obrazca za poslikavo (kazalec this kaže na obrazec) Graphics graphics = this.CreateGraphics(); //lahko tudi Graphics graphics = CreateGraphics();

V primeru, da pa želimo da se poslikava izvede ob dogodku Paint (ob poslikavi) tega gradnika pa deklaracija objekta razreda Graphics ni potrebna, saj že sam dogodek Paint vsebuje parameter PaintEventArgs e, ki predstavlja referenco na ta objekt tipa Graphics. Potrebno je le še uporabiti ustrezno metodo za poslikavo (npr. DrawLine, …). private void Form1_Paint(object sender, PaintEventArgs e) { e.Graphics.DrawLine( . . . ); }

Dogodek Paint

V vseh okenskih programih se obrazci in gradniki na njem posodabljajo (ali pa narišejo) kot odgovor na dogodek Paint. Dogodkov Paint se navadno sploh ne zavedamo, saj se izvajajo avtomatsko, za to pa poskrbi operacijski sistem. Samo za informacijo omenimo metodo, ki dogodke tipa Paint obdela: imenuje se PaintEventHandler, metoda pa je definirana takole: Paint+=new PaintEventHandler(Parameter tipa PaintEventArgs);

Parameter te metode vsebuje dve dodatni lastnosti, ki sta bistveni za dogodek Paint. To sta lastnosti Graphics (vrne instanco oz. primerek razreda Graphics, ki se uporablja za grafiko) in lastnost ClipRectangle (vrne pravokotnik za poslikavo). V okenskih aplikacijah se torej tekstovne informacije, ali pa kakršnekoli poslikave na obrazcih in gradnikih nasploh, generirajo s pomočjo upravljalnika dogodkov Paint (Paint event handler), za katerega sprožanje poskrbi operacijski sistem sam. Poslikavo pa lahko sprožimo tudi sami, a v tem primeru moramo ustrezen gradnik najprej pripraviti za poslikavo (deklarirati objekt tipa Graphics). Razred Graphic vsebuje preko 50 različnih metod za poslikave. Te metode omogočajo, da na gradnike lahko rišemo, pišemo ali pa postavljamo slike. V splošnem metode za risanje potrebujejo enega ali več parametrov, s katerim opišemo poslikavo. Ti parametri so Font (pisava, ki jo uporabljajo grafične metode pri risanju znakov), Brush (čopič – barva in vzorec, ki jo uporabljajo risalne metode za izpolnjevanje notranjosti izrisanih črt ali likov), Pen (pisalo – vrsta pisala za risanje črt in geometrijskih likov), Point (točka z dvema koordinatama), Rectangle (pravokotnik, znotraj katerega bomo slikali), Image (slika, ki jo bomo postavili na gradnik), Icon (ikona, ki jo bomo postavili na gradnik), String (zaporedje znakov, ki ga bomo grafično oblikovali),… Omenili bomo le nekaj najpomembnejših metod za izris grafike in zanje prikazali tudi primer uporabe.

Metoda DrawString - risanje teksta s pomočjo GDI+

Za pisanje tekstovnih sporočil na obrazcih in ostalih gradnikih uporabljamo eno izmed šestih metod tipa DrawString. //pripravimo obrazec za poslikavo - deklariramo objekt tipa Graphics Graphics graphics = this.CreateGraphics(); Font mojfont = new Font("Arial",20);

Page 217: PROŠNJA - uranic.tsckr.siuranic.tsckr.si/VISUAL C#/VISUAL C#.pdf · MICROSOFT VISUAL C# .NET Srečo Uranič . Visual C# . ... Za začetek novega projekta v okolju Visual C# Standard

Visual C# . NET

Algoritmi in programski jeziki – 4.letnik

217

//napišimo sporočilo na obrazcu.Pričetek izpisa je 30 pixlov od levega roba in 50 pixlov pod //vrhom obrazca, poslikava je običajna (SystemBrushes.WindowText) graphics.DrawString("Tekst na obrazcu", mojfont, SystemBrushes.WindowText,30,50);

Vaja: Na obrazcu izpišimo pozdravno sporočilo. Začetek izpisa je na poziciji s koordinatama x= 15 in y=50. Kodo vstavimo npr. v dogodek Click gumba na obrazcu. //pripravimo obrazec za poslikavo - deklariramo objekt tipa Graphics Graphics graphics = this.CreateGraphics(); //objekt Tocka bo določil začetek poslikave Point Tocka = new Point(15, 50); Font font = new Font(this.Font, FontStyle.Bold);//določimo font ta izpis besedila na obrazcu //Za izpis teksta uporabimo metodo DrawString //posredovali smo ji 4 param.: tekst za izpis, font,vrsto poslikave in začetni položaj izpisa graphics.DrawString("Pozdravljeni!", font, SystemBrushes.WindowText, Tocka); string izpis = "Tole je grafični izpis s pomočjo \nmetode DrawString."; tocka = new Point(15, 70); //parameter SystemBrushes.xxx določa poslikavo izrisanega elementa z debelino enega pixla graphics.DrawString(izpis, font, SystemBrushes.WindowText, tocka); Rectangle pravokotnik = new Rectangle(15, 120, 250, 200); graphics.DrawString("Tale tekst je znotraj nevidnega okvirja širine 250 in višine 200.

Poslikava je tipa GrayText (siva).", font, SystemBrushes.GrayText, pravokotnik); pravokotnik = new Rectangle(15, 200, 250, 200); //določimo še format izpisa - tekst znotraj pravokotnika bo centriran StringFormat format1 = new StringFormat(); format1.Alignment = StringAlignment.Center; graphics.DrawString("Tale tekst pa je CENTRIRAN v nevidnem okvirju širine 250 in višine 200.

Poslikava je tipa GrayText (siva).", font, SystemBrushes.GrayText, pravokotnik,format1);

Rezultat, ki ga dobimo na obrazcu:

Metodi DrawLine in DrawLines- risanje ravnih črt – daljic (Lines)

Razred Graphics vsebuje tudi metode za risanje črt in lomljenih črt. Najenostavnejša med njimi je metoda DrawLine, ki se uporablja za povezovanje dveh točk, metoda DrawLines pa se uporablja za hkratno povezovanje več točk. Vse točke je potrebno prej določiti.

Page 218: PROŠNJA - uranic.tsckr.siuranic.tsckr.si/VISUAL C#/VISUAL C#.pdf · MICROSOFT VISUAL C# .NET Srečo Uranič . Visual C# . ... Za začetek novega projekta v okolju Visual C# Standard

Visual C# . NET

Algoritmi in programski jeziki – 4.letnik

218

Vaja: Narišimo diagonalo obrazca, posamezno daljico in še več daljic hkrati.

private void diagonalaToolStripMenuItem_Click(object sender, EventArgs e) { //določimo zgornji levi rob delovne površine obrazca Point zgorajLevo = ClientRectangle.Location; zgorajLevo.Y += menuStrip1.Height; //določimo spodnji ndesni rob delovne površine obrazca Point spodajDesno = new Point(ClientRectangle.Right, ClientRectangle.Bottom); //pripravimo obrazec za poslikavo - deklariramo objekt tipa Graphics Graphics graphics = this.CreateGraphics(); graphics.DrawLine(SystemPens.WindowText, zgorajLevo, spodajDesno); } private void daljicaToolStripMenuItem_Click(object sender, EventArgs e) { //Levi rob daljice Point tocka1 = new Point(5, 100); //desni rob daljice Point tocka2 = new Point(250, 100); //pripravimo obrazec za poslikavo - deklariramo objekt tipa Graphics Graphics graphics = this.CreateGraphics(); //narišemo daljico //parameter SystemPens.xxx določa barvo izrisanega elementa z debelino enega pixla graphics.DrawLine(SystemPens.WindowText, tocka1,tocka2); } private void daljiceToolStripMenuItem_Click(object sender, EventArgs e) { //tabela štirih točk, ki predstavljajo ogljišča pravokotnika Point[] tabelaTock = new Point[]{ new Point(40,40), new Point(40,200), new Point(200,200), new Point(200,40), new Point(40,40) }; //pripravimo obrazec za poslikavo - deklariramo objekt tipa Graphics Graphics graphics = this.CreateGraphics(); //narišemo kombinacijo štirih daljic, ki skupaj tvorijo pravokotnik //parameter SystemPens.xxx določa barvo izrisanega elementa z debelino enega pixla graphics.DrawLines(SystemPens.WindowText, tabelaTock); }

Metodi DrawRectangle in DrawRectangles - risanje in barvanje pravokotnikov

Razred Graphics vsebuje dve metodi za risanje pravokotnikov, to sta metodi DrawRectangle za risanje enega pravokotnika in metoda DrawRectangles, za risanje več pravokotnikov hkrati, ter analogno dve metodi za barvanje pravokotnikov: metoda FillRectangle in FillRectangles. Barvo polnila napovemo s pomočjo prvega parametra, ki je tipa Brush. //Določimo barvo polnila Brush barva = Brushes.Yellow;

Page 219: PROŠNJA - uranic.tsckr.siuranic.tsckr.si/VISUAL C#/VISUAL C#.pdf · MICROSOFT VISUAL C# .NET Srečo Uranič . Visual C# . ... Za začetek novega projekta v okolju Visual C# Standard

Visual C# . NET

Algoritmi in programski jeziki – 4.letnik

219

Vaja: Narišimo nekaj pravokotnikov, nekatere med njimi pa še pobarvajmo:

private void pravokotnikToolStripMenuItem_Click(object sender, EventArgs e) { Rectangle skatla = new Rectangle(10, 30, 300, 200); //pripravimo obrazec za poslikavo - deklariramo objekt tipa Graphics Graphics graphics = this.CreateGraphics(); //parameter SystemPens.xxx določa barvo izrisanega elementa z debelino enega pixla graphics.DrawRectangle(SystemPens.WindowText,skatla); } private void kvadratToolStripMenuItem_Click(object sender, EventArgs e) { int zgoraj = 40; int levo = 40; int sirina = 100; int visina = 100; Graphics graphics = this.CreateGraphics(); //parameter SystemPens.xxx določa barvo izrisanega elementa z debelino enega pixla graphics.DrawRectangle(SystemPens.WindowText, levo,zgoraj,sirina,visina); } private void pravokotnikiToolStripMenuItem1_Click(object sender, EventArgs e) { Rectangle[] pravokotniki = new Rectangle[]{ new Rectangle(150,50,50,30), new Rectangle(160,120,30,70), new Rectangle(210,60,30,10) }; Graphics graphics = this.CreateGraphics(); //parameter SystemPens.xxx določa barvo izrisanega elementa z debelino enega pixla // graphics.DrawRectangles(SystemPens.WindowText, pravokotniki); graphics.FillRectangles(Brushes.Red, pravokotniki); } private void toolStripMenuItem1_Click(object sender, EventArgs e) { Rectangle skatla = new Rectangle(260, 40, 25, 40); //pripravimo obrazec za poslikavo - deklariramo objekt tipa Graphics Graphics graphics = this.CreateGraphics(); //Določimo barvo pravokotnika Brush barva = Brushes.Yellow; graphics.FillRectangle(barva, skatla); }

Metodi DrawEllipse in FillElipse - risanje krivulj

Page 220: PROŠNJA - uranic.tsckr.siuranic.tsckr.si/VISUAL C#/VISUAL C#.pdf · MICROSOFT VISUAL C# .NET Srečo Uranič . Visual C# . ... Za začetek novega projekta v okolju Visual C# Standard

Visual C# . NET

Algoritmi in programski jeziki – 4.letnik

220

Razred Graphics vsebuje dve metodi za risanje krivulj, to sta metodi DrawEllipse in FillEllipse za risanje krivulj. Razlika med njimi je za, da z metodo DrawEllipse narišemo krivuljo brez polnila, pri metodi narisan z metodo FillEllipse pa določimo še polnilo. Razlika je opazna le kadar ima krivulja večjo debelino. Vaja: Ko stisnemo miškin gumb in miško premaknemo, rišemo po obrazcu. Risanje se prekine, ko spustimo gumb.

Omogočanje in onemogočanje risanja smo zagotovili z globalno spremenljivko, ki jo ob kliku gumba postavimo na true (pričetek risanja), ko pa gumb spustimo pa jo postavimo na false (risanje končano).

//globalna spremenljivka za omogočanje/onemogočanje risanja bool risanjeOmogoceno = false; // whether to paint //dogodek obrazca – ko spustimo miškin gumb, se risanje prekine private void Form1_MouseUp(object sender, MouseEventArgs e) { risanjeOmogoceno = false; } //dogodek obrazca – risanje po obrazcu sprožimo, ko stisnemo miškin gumb in premaknemo miško private void Form1_MouseMove(object sender, MouseEventArgs e) { //ker želimo poslikavo sprožiti sami, moramo obrazec za poslikavo prej pripraviti Graphics graphics = this.CreateGraphics(); if (risanjeOmogoceno) { graphics.FillEllipse(new SolidBrush( Color.BlueViolet ),e.X, e.Y, 4, 4 ); } }

Metodi DrawPie in FillPie - risanje tortic

Za risanje tortic, ki jih na primer uporabljamo za grafičen prikaz rezultatov neke meritve, uporabljamo metodi DrawPie in FillPie. Vaja: Ob kliku na opciji v meniju se narišeta navadna in barvna tortica.

Page 221: PROŠNJA - uranic.tsckr.siuranic.tsckr.si/VISUAL C#/VISUAL C#.pdf · MICROSOFT VISUAL C# .NET Srečo Uranič . Visual C# . ... Za začetek novega projekta v okolju Visual C# Standard

Visual C# . NET

Algoritmi in programski jeziki – 4.letnik

221

private void torticaToolStripMenuItem_Click(object sender, EventArgs e) { //Barva in debelina črte Pen blackPen = new Pen(Color.Black, 3); //Okvir, znotraj katerega bomo risali Rectangle rect = new Rectangle(0, 0, 200, 100); //Začetni kot in kot, ki ga bomo opisali. float startAngle = 0.0F; float sweepAngle = 75.0F; Graphics grafika = CreateGraphics(); //Na obrazcu narišemo tortico. grafika.DrawPie(blackPen, rect, startAngle, sweepAngle); } private void barvnaTorticaToolStripMenuItem_Click(object sender, EventArgs e) { //Barva polnila SolidBrush redBrush = new SolidBrush(Color.Red); //Okvir, znotraj katerega bomo risali Rectangle rect = new Rectangle(0, 60, 200, 100); //Začetni kot in kot, ki ga bomo opisali. float startAngle = 0.0F; float sweepAngle = 180.0F; Graphics grafika = CreateGraphics(); //Na obrazcu narišemo barvno tortico. grafika.FillPie(redBrush, rect, startAngle, sweepAngle); }

Metodi DrawImage in DrawIcon – postavljanje slike in ikone na gradnik

Sliko postavimo v neg gradnik s pomočjo metode DrawImage, ikono pa z metodo DrawIcon. Vaja: Ob kliku na opciji v meniju se na obrazec postavi slika in ikona.

private void vstavljanjeSlikeToolStripMenuItem_Click(object sender, EventArgs e) { //Kreiramo najprej objekt za sliko. Predpostavimo, da je slika v mapi ..\bin\Debug Image newImage = Image.FromFile("Avtobusi.jpg"); //Določimo koordinate zgornjega levega roba slike. float x = 30.0F; float y = 30.0F;

Page 222: PROŠNJA - uranic.tsckr.siuranic.tsckr.si/VISUAL C#/VISUAL C#.pdf · MICROSOFT VISUAL C# .NET Srečo Uranič . Visual C# . ... Za začetek novega projekta v okolju Visual C# Standard

Visual C# . NET

Algoritmi in programski jeziki – 4.letnik

222

//Obrazec pripravimo za poslikavo. Graphics slika = CreateGraphics(); //Sliko postavimo na obrazec. slika.DrawImage(newImage, x, y); } private void vstavljanjeIkoneToolStripMenuItem_Click(object sender, EventArgs e) { //Kreiramo najprej objekt za ikono. Predpostavimo, da je ikona v mapi ..\bin\Debug Icon newIcon = new Icon("Home_32.ico"); //Določimo koordinate zgornjega levega roba ikone. int x = 50; int y = 200; //Obrazec pripravimo za poslikavo. Graphics slika = CreateGraphics(); //Ikono postavimo na obrazec. slika.DrawIcon(newIcon, x, y); }

Page 223: PROŠNJA - uranic.tsckr.siuranic.tsckr.si/VISUAL C#/VISUAL C#.pdf · MICROSOFT VISUAL C# .NET Srečo Uranič . Visual C# . ... Za začetek novega projekta v okolju Visual C# Standard

Visual C# . NET

Algoritmi in programski jeziki – 4.letnik

223

Podatkovna skladišča (baze) in upravljanje s podatki V naslednjih poglavjih bo razložena in prikazana manipulacija s podatkovnimi bazami. Naučili se bomo kreirati podatkovno bazo kar znotraj okolja Visual C# Express Edition, kasneje pa bomo uporabljali že izdelane primere podatkovnih baz, ki že vsebujejo podatkovne tabele s testnimi podatki. Za izdelavo podatkovnih baz pa obstajajo seveda tudi druga, bolj specializirana orodja, npr. Microsoftov SQL Server Management Studio Express. Razumevanje naslednjih poglavij bo tako možno le v primeru, da poznamo osnove relacijskih podatkovnih baz.

Uporaba baze podatkov Za izdelavo okenskih aplikacij, ki znajo upravljati z bazami podatkov, je v okolju Visual Studio namenjena skupina objektov ADO.&ET. To je v bistvu nadgradnja objektov ActiveX Data Objects (ADO), ki pa je posebej načrtovana in optimizirana za .NET okolje (.&ET Framework).

Uporaba ADO.&ET podatkovnih baz

S prihodom .&ET orodij, se je Microsoft odločil tudi za nadgradnjo svojega modela dostopa do podatkovnih baz (ActiveX Data Objects – ADO) in tako je nastal ADO.&ET. S pomočjo mehanizmov ADO.&ET, je lahko povezava s podatkovno bazo uporabljena v različnih aplikacijah, pri čemer obstaja možnost začasne prekinitve povezave in ponovne vzpostavitve kar znotraj aplikacije. Tak način dela mnogokrat predstavlja občuten prihranek časa.

Kreiranje nove baze podatkov

Razvojno Visual C# Express Edition 2008 (pa tudi vse prejšnje in pa seveda višje verzije) omogoča tudi kreiranje podatkovne baze in seveda ustreznih tabel, indeksov in vsega ostalega znotraj baze. Seveda nam morajo biti prej poznani ključni pojmi glede baz podatkov: kaj pravzaprav baza je, čemu služi, kaj so to podatkovne tabele znotraj baze, kaj je to polje, tipi polj, pojem zapisa oz. recorda, indeksne datoteke, … Pri kreiranju baze kar znotraj razvojnega okolja Visual C# Express Edition, imamo na izbiro dve vrsti baze podatkov:

• Local Database in • Service Based Database.

Local Database je t.i. compact edition baza, ki temelji na datotekah. Za neposreden dostop do podatkov v taki bazi potrebujemo le ustrezen gonilnik (Driver). Taka baza ne podpira t.i. stored procedur, ki predstavljajo zelo močan mehanizem, ki skrbi za varnost podatkov. Datoteka, v kateri je baza shranjena, ima končnico SDF (SQL Server Compact Edition format). Za dostop do take baze na lokalnem računalniku ni potrebna instalacija lokalnega strežnika. Service Based Database pa je baza podatkov, ki je dostopna le s pomočjo SQL strežnika. Datoteka, v kateri je baza shranjena, ima končnico MDF, ki predstavlja SQL Server format. Za priklop na SQL Server bazo podatkov je na lokalnem računalniku potreben zagon SQL Server servisa, saj le preko tega servisa lahko dostopamo do podatkovnih tabel v bazi.

Pri izdelavi aplikacij, ki delajo s podatkovnimi skladišči, se bomo morali s pomočjo Database Explorerja priključiti na ustrezno bazo. V primeru, da so naše nastavitve v SQL Server Configuration Managerju napačne, bomo pri poskusu uspešnosti povezave na to bazo dobili obvestilo Failed to generate a user instance of SQL Server due to failure in starting the process for the user instance. Rešitev (ki je meni požrla teden dni časa in živcev) je naslednja:

Page 224: PROŠNJA - uranic.tsckr.siuranic.tsckr.si/VISUAL C#/VISUAL C#.pdf · MICROSOFT VISUAL C# .NET Srečo Uranič . Visual C# . ... Za začetek novega projekta v okolju Visual C# Standard

Visual C# . NET

Algoritmi in programski jeziki – 4.letnik

224

odprite SQL Server Configuration Manager, nato dvokliknite na SQLServer (SQLEXPRESS) in v spustnem seznamu Buil-in-account izberite Local System.

Pobrišite (ali pa bolje le preimenujte) mapo C:\Users\[user]\AppData\Local\Microsoft\Microsoft SQL Server Data\SQLEXPRESS

Novo bazo lahko kreiramo tudi s pomočjo datoteke Microsoft SQL Server Query File (.sql), to je datoteke, v kateri so zapisani vsi potrebni SQL stavki za kreiranje nove baze. Tak način kreiranja nove baze je pomemben tedaj, kadar želimo projekt, ki dela s to bazo instalirati na nekem lokalnem računalniku, ali pa zato, da imamo pač shranjeno varnostno kopijo celotne STRUKTURE baze. Če na računalniku baza, ki jo želimo kreirati s SQL Server Query datoteko, še

ne obstaja, jo kreiramo tako, da v Microsoft SQL Server Management Studiu (MSSMS) zaženemo ustrezen query (zapisan v datoteki .sql). Nova baza se bo ustvarila v mapi C:\Program Files\Microsoft SQL

Server\MSSQL10.SQLEXPRESS\MSSQL\DATA (ustvarita se dve datoteki: baza s končnico MDF in log datoteka s končnico LDF) obenem pa se baza prikaže tudi v mapi Databases znotraj MSSMS. Če želimo to bazo prenesti (kopirati) v neko drugo mapo, moramo v MSSMS najprej prekiniti ustrezno povezavo do te baze. To storimo takole: v Databases najprej izbremo ustrezno bazo, nato kliknemo desni miškin gumb→Tasks→Detach… in nato v sporočilnem oknu s klikom na OK le še potrdimo našo odločitev. Povezava je sedaj prekinjena in bazo (skupaj z log datoteko) lahko prenesemo oz. prekopiramo v poljubno drugo mapo. Če želimo sedaj z bazo delati v okviru MSSMS je potreben ponoven priklop nanjo (v MSSMS izberemo mapo Databases→Desni miškin gumb→Attach…→nato pa s klikom na gumb Add odpremo okno v katerem poiščemo in izberemo bazo in izbiro potrdimo s klikom na gumb OK). Kreirajmo nov projekt in ga poimenujmo Pisatelji. Novo bazo, ki jo bomo v tem projektu uporabili, kreiramo

tako, da v meniju Project izberemo Add �ew Item… V oknu izberimo Service-based Database, bazo poimenujmo Pisatelji.mdf in kliknimo Add. V Solution Explorerju se pojavi

nova datoteka Pisatelji.mdf, na

ekranu pa se čez nekaj časa pojavi okno Data Source Configuration Wizard. V njem je opozorilo, da je baza, ki smo jo izbrali, nova oz. da še ne vsebuje nobenega objekta (npr. nobene podatkovne tabele). Kliknimo gumb Finish: skreiral se bo prazen DataSet. Kasneje, ko bomo v bazo že dodali (skreirali) podatkovne tabele, bomo ta DataSet ponovno odprli in vanj dodajali tabele.

Page 225: PROŠNJA - uranic.tsckr.siuranic.tsckr.si/VISUAL C#/VISUAL C#.pdf · MICROSOFT VISUAL C# .NET Srečo Uranič . Visual C# . ... Za začetek novega projekta v okolju Visual C# Standard

Visual C# . NET

Algoritmi in programski jeziki – 4.letnik

225

Dvokliknimo sedaj na datoteko Pisatelji.mdf v Solution Explorerju in (običajno na levi strani) se nam odpre novo okno Database Explorer.

V Database Explorer-ju Izberimo vrstico Tables, kliknimo desni miškin gumb in nato Add �ew Table. Odpre se urejevalnik polj za novo tabelo. Pisatelja bomo opisali z naslednjimi polji:

Ko smo vnesli imena in tipe polj, se z miško postavimo na zavihek nad urejevalnikom polj in kliknimo desni miškin gumb. Pojavi se okno za vnos imena podatkovne tabele: vnesimo Pisatelj.

Z miško se postavimo še v polje Šifra in kliknimo desni gumb. V oknu, ki se prikaže, izberimo Set Primary Key:

nastavili smo t.i. primarni ključ (vrednost polja Šifra se v tabeli ne more nikoli ponoviti).

Polju Šifra določimo še lastnost Auto Increment - za polja, ki imajo to lastnost je značilno, da bo SQL Server njihovo vrednost določal avtomatično. Postopek je naslednji: izberimo polje Šifra, nato pa v oknu Column

Properties nastavimo lastnost Identity Specification: podlastnost (is Identity) nastavimo na Yes, podlastnosti Identity Increment (vrednost, za katero se bo polje avtomatično povečevalo) in Identity Seed (izhodiščna vrednost) pa nastavimo po želji (privzeti vrednosti sta 1). V našem primeru smo pustili vrednosti privzeti, kar pomeni, da bo šifra prvega pisatelja v tabeli enaka 1, vsem ostalim pa se bo vrednost avtomatično povečevala za 1.

Končno se ponovno postavimo z miško na zavihek nad urejevalnikom polj, kliknimo desni miškin gumb in izberimo opcijo Save Pisatelj. Kreiranje prve tabele znotraj naše baze podatkov Pisatelji je tako končano.

Page 226: PROŠNJA - uranic.tsckr.siuranic.tsckr.si/VISUAL C#/VISUAL C#.pdf · MICROSOFT VISUAL C# .NET Srečo Uranič . Visual C# . ... Za začetek novega projekta v okolju Visual C# Standard

Visual C# . NET

Algoritmi in programski jeziki – 4.letnik

226

Sedaj lahko pričnemo z vpisovanjem testnih podatkov v to tabelo. V DataBase Explorerju napravimo desni klik na tabelo Pisatelji in izberimo opcijo Show Table Data. V tabelo, ki se prikaže, lahko že kar na tem vnesemo nekaj testnih podatkov.

Pri kreiranju zgornje baze Pisatelji in v njej tabele Pisatelj, nam je Visual C# skreiral tudi prazen DataSet in ga poimenoval PisateljiDataSet (ime smo pustili kar privzeto). V ta DataSet bomo sedaj dodali našo tabelo Pisatelji

in jo tako naredili dostopno gradnikom na obrazcu. V Solution Explorer –ju dvokliknimo na PisateljiDataSet, nato pa v okno, ki se odpre, povlecimo našo tabelo Pisatelj. Tabela je sedaj pripravljena in do nje lahko dostopamo tudi preko lastnosti gradnikov. Na tem mestu lahko tudi vidimo predogled vsebine podatkovne tabele Pisatelj: miško postavimo v naslovno vrstico in ob desnem kliku se odpre meni, v katerem izberemo Preview Data. Odpre se okno Preview Data, v katerem samo še kliknemo gumb Preview.

Vsebino tabele Pisatelj bomo sedaj prikazali npr. v gradniku DataGridView. Na zaenkrat še prazen obrazec našega projekta (na začetku smo ga poimenovali Pisatelji) dodajmo gradnik DataGridView (njegovo ime naj bo kar privzeto – dataGridView1). Lastnost Dock mu natavimo na Fill, da se razširi čez celoten obrazec. V tem gradniku bi sedaj radi prikazali vsebino tabele Pisatelj naše baze Pisatelji. Postopek je sledeč: izberimo gradnik dataGridView1 in v oknu Properties lastnost DataSource. V spustnem seznamu izberemo Other Data Sources. Kliknemo na znak +, da se seznam razširi, nato razširimo še ProjectDataSources, pa PisateljiDataSet in končno izberemo Pisatelj. Naš obrazec sedaj izgleda takole: v gradniku dataGridView1 so nastali stolpci,

ki so dobili enaka imena kot so polja v tabeli Pisatelji (seveda lahko napise na vrhu stolpcev poljubno preimenujemo – v našem primeru smo na vrh prvega stolpca napisali Šifra namesto Sifra). Na obrazcu je vsebina tabele Pisatelji zaenkrat še nevidna, a če projekt zaženemo, je gradnik dataGridView1 napolnjen z ustreznimi podatki tabele Pisatelj. Ko smo gradili projekt nam je Visual C# na obrazec dodal nekaj gradnikov, katerih pomen in uporabo bomo spoznali v nadaljevanju.

Page 227: PROŠNJA - uranic.tsckr.siuranic.tsckr.si/VISUAL C#/VISUAL C#.pdf · MICROSOFT VISUAL C# .NET Srečo Uranič . Visual C# . ... Za začetek novega projekta v okolju Visual C# Standard

Visual C# . NET

Algoritmi in programski jeziki – 4.letnik

227

Izdelava novega projekta, ki dela nad obstoječo bazo podatkov – uporaba čarovnika

Pri kreiranju novega projekta pa navadno uporabljamo že obstoječo bazo podatkov (bodisi smo jo naredili že kdaj prej, bodisi jo je za nas naredil že kdo drug). Obstoječo bazo podatkov lahko vključimo v naš projekt s pomočjo čarovnika na dva načina: • Prvi način: v Solution Explorerju označimo naš projekt, kliknemo desni gumb miške, izberemo opcijo Add

in nato Add Existing Item. Odpre se okno za dodajanje obstoječe datoteke v projekt. V spustnem

seznamu določimo vrsto datoteke, ki jo bomo dodali v projekt (Data Files - datoteke tipa .mdf), nato pa izberemo ustrezno datoteko. Če smo le-to prej skopirali v mapo z našim projektom jo le izberemo, sicer pa se premaknemo v ustrezno mapo na disku, ki vsebuje bazo podatkov in jo izberemo. S klikom na gumb Add jo vključimo v naš projekt (v konkretnem primeru smo v projekt vključili bazo �abavaSQL). POZOR: bazo smo na ta način

prekopirali v mapo našega projekta, tako da dejansko delamo s kopijo prave baze!

Čez nekaj časa se prikaže okno Data Source Configuration Wizzard in v njem seznam tabel, ki pripadajo

izbrani bazi podatkov. Odkljukajmo tiste (ali pa vse), s katerimi bomo v projektu delali in kliknimo gumb Finish.

Razvojno orodje bo za nas skreiralo ustrezen gradnik Dataset z imenom �abavaSQLDataSet, ki ga vidimo v oknu Solution Explorer. Če nanj dvokliknemo, dobimo grafični prikaz izbranih tabel v bazi, obenem pa še povezave med njimi (odvisno od primarnih ključev v tabelah). V Solution Explorerju se pojavi tudi datoteka app.config, ki vsebuje podatke o povezavi do baze podatkov (connectionString) – če bomo kasneje bazo premaknili v drugo mapo, je potrebno samo spremeniti ustrezno pot do nje v tej datoteki.

Page 228: PROŠNJA - uranic.tsckr.siuranic.tsckr.si/VISUAL C#/VISUAL C#.pdf · MICROSOFT VISUAL C# .NET Srečo Uranič . Visual C# . ... Za začetek novega projekta v okolju Visual C# Standard

Visual C# . NET

Algoritmi in programski jeziki – 4.letnik

228

• Drugi način: V meniju Project izberemo ADD �ew Item, izberemo Dataset in ga poimenujemo npr. pisateljiDataSet. Prikaže se okno Dataset Designer. V oknu kliknemo na Database Explorer, da se prikaže okno Database Explorer

Z miško se postavimo v okno Database Explorer in z desnim klikom se nam prikaže okno, v katerem izberemo opcijo Add TableAdapter... .

V primeru, da z bazo delamo prvič, se prikaže se okno Add Connection: za Data source izberemo Microsoft SQL Server Database File, nato pa s klikom na gumb Browse poiščemo našo bazo podatkov (v našem primeru Pisatelji.mdb). Po želji še preverimo, če povezava z bazo deluje (klik na gumb Test Connection), nato s klikom na gumb OK okno zapremo.

Če pa smo z bazo že delali (npr. v prejšnjem projektu), se prikaže okno TableAdapter Configuration Wizard, v katerem izberemo obstoječo povezavo z bazo, ali pa s klikom na gumb �ew Connection odpremo okno Add Connection.

Za Data source izberemo Microsoft SQL Server Database File, nato pa s klikom na gumb Browse poiščemo bazo. Izbiro potrdimo s klikom na gumb Open, nato pa le še sledimo navodilom, ki nam jih ponuja čarovnik! Tudi pri tem, drugem načunu, se v Solution Explorerju pojavi datoteka app.config, ki vsebuje podatke o povezavi do baze podatkov (connectionString) – če bomo kasneje bazo premaknili v drugo mapo, je potrebno samo spremeniti ustrezno pot do nje v tej datoteki.

Pri obeh načinih lahko sedaj podatke izbrane baze podatkov prikažemo npr. v gradniku DataGridView. Gradnik postavimo na obrazec, kliknemo na ikono v desnem zgornjem kotu tega gradnika in kliknemo Choose Data Source (ali pa izberemo gradnik DataGridView1 in v oknu Properties odpremo spustni seznam DataSource): razširimo Other Data Source->Project Data Sources->�abavaSQL (oz. Other Data Source->Project Data Sources->Pisatelji ) in končno izberemo tabelo Artikli (oz. Pisatelji)! OPOZORILO: v fazi razvoja projekta so v gradniku DataGridView prikazana le imena stolpcev baze Pisatelji, vsebina pa se pokaže šele ko projekt zaženemo!

Page 229: PROŠNJA - uranic.tsckr.siuranic.tsckr.si/VISUAL C#/VISUAL C#.pdf · MICROSOFT VISUAL C# .NET Srečo Uranič . Visual C# . ... Za začetek novega projekta v okolju Visual C# Standard

Visual C# . NET

Algoritmi in programski jeziki – 4.letnik

229

Instalacija in uporaba že obstoječih baz podatkov

Preden nadaljujemo z delom, moramo poskrbeti za instalacijo vsaj ene podatkovne baze, ki nam bo služila kot osnova za nadaljnje delo. Sestavni del orodja Visual Studio je tudi že izdelana testna baza podatkov z imenom

&orthwind. Gre za bazo, ki predstavlja neko fiktivno podjetje, ki se ukvarja s prodajo hrane. Baza vsebuje vrsto tabel z informacijami o izdelkih, ki jih podjetje prodaja, o svojih kupcih, pošiljateljih in o svojih zaposlenih. Slika prikazuje vse tabele, ki sestavljajo celotno bazo in način, kako so tabele med seboj povezane.

Podatkovno bazo &orthwind pa je potrebno na naš računalnik še instalirati. Instalacijo bomo najlaže opravili kar v ukaznem oknu operacijskega sistema Windows (Command Prompt)

• Angleška verzija oken (gumb Start, AllPrograms, Accessories in nato Command Prompt) • Slovenska verzija oken (gumb Start, Vsi programi, Pripomočki in nato Ukazni poziv)

V ukazni vrstici se prestavimo v mapo \Microsoft Press\Visual CSharp Step By Step\Chapter 23 znotraj mape MyDocuments in vtipkajmo naslednji ukaz: sqlcmd –S imeRačunalnika\SQLExpress –E –iinstnwnd.sql pri čemer moramo seveda namesto imeRačunalnika vnesti dejansko ime našega računalnika

� POZOR: Ime svojega računalnika lahko preprosto izvemo tudi tako, da v ukazni vrstici (Command Prompt) zapišemo ukaz hostname

V tej vrstici smo uporabili ukaz sqlcmd za vzpostavitev povezave z lokalno instanco serverja SQL Server 2008 Express in nato pognali skripto iinstnwnd.sql. Ta skripta vsebuje SQL stavke, ki skreirajo podatkovno bazo &orthwind in njene pripadajoče tabele, obenem pa te tabele še napolni s testnimi podatki. Ko se izvajanje skripte zaključi (vse skupaj traja nekaj sekund), lahko ukazno okno zapremo.

� POZOR: Za izvedbo primerov in vaj v tem poglavju, je potrebna predhodna instalacija orodja Microsoft SQL Server 2008. Microsoft SQL Server 2008 je sicer sestavni del orodja Visual

Page 230: PROŠNJA - uranic.tsckr.siuranic.tsckr.si/VISUAL C#/VISUAL C#.pdf · MICROSOFT VISUAL C# .NET Srečo Uranič . Visual C# . ... Za začetek novega projekta v okolju Visual C# Standard

Visual C# . NET

Algoritmi in programski jeziki – 4.letnik

230

Studio 2008, možen pa je tudi download s strani http://www.microsoft.com/sql/default.mspx.

Dostop do baze podatkov

Osnova vsake aplikacije, ki dela z bazo podatkov, je seveda priklop (connect) na to bazo. V Visual Studiu lahko priklop na bazo in prikaz vsebine poljubne tabele ali več tabel iz baze realiziramo s pomočjo čarovnika, ali pa seveda čisto programsko. V tem delu bomo pokazali, kako se z bazo povežemo s pomočjo čarovnika. Za uspešno realizacijo naslednjega primera pa potrebujemo vsaj verzijo Visual Studio Academic ali višjo verzijo. Kreirajmo nov projekt in ga poimenujmo npr. Database_&orthwind1 in ga shranimo v poljubno mapo.

Odprimo meni Data in nato ADD &ew Data Source. Zažene se čarovnik z imenom Data Source Configuration, ki v splošnem omogoča priklop na poljuben podatkovni izvor – bazo podatkov, objekt ali pa na spletni (Web) servis. Izberimo ikono Database, nato pa kliknimo gumb &ext.

V naslednjem koraku bomo s pomočjo čarovnika določili vrsto povezave za bazo. Odpre se okno Data Source Configuration Wizard:Vzpostaviti želimo novo povezavo s podatki, zato kliknimo gumb &ew Connection. Odpre se okno Choose Data Source. Izbrati moramo podatkovni izvor (Data source), s katerim določimo tip baze, ki jo želimo uporabiti, nato pa še podatkovni skrbnik (Data provider), s katerim bomo določili, kako se

bomo na to podatkovno bazo priklopili. Povezavo na SQL server lahko dosežemo z uporabo .&ET Framework Data Provider for SQL Server, ali s pomočjo .&ET Framework Data Provider for OLE DB. Pri tem velja, da je .�ET Framework Data Provider for SQL Server optimiziran za povezave z bazami tipa SQL Server, .�ET Framework Data Provider for OLE DB pa z množico različnih podatkovnih izvorov, ne le z bazami tipa SQL Server.

Kliknimo na gumb &ew Connection, da se odpre pogovorno okno za določanje podatkovnega izvora (Data source) in podatkovnega skrbnika (Data provider)

Page 231: PROŠNJA - uranic.tsckr.siuranic.tsckr.si/VISUAL C#/VISUAL C#.pdf · MICROSOFT VISUAL C# .NET Srečo Uranič . Visual C# . ... Za začetek novega projekta v okolju Visual C# Standard

Visual C# . NET

Algoritmi in programski jeziki – 4.letnik

231

V našem primeru izberimo za Data source opcijo Microsoft SQL Server, za Data provider pa .&ET Framework Data Provider for SQL Server. S klikom na gumb OK okno zapremo in vrnemo se nazaj v okno Add Connection. V okno Server name vtipkajmo ime_serverja\SQLExpress, pri čemer je ime_serverja ime našega računalnika. Za logiranje na strežnik (server) nato izberimo opcijo Use Windows Authentication – ta opcija za priklop na bazo uporabi kar ime našega računa (tak način logiranja je priporočen). Končno izberemo še ustrezno bazo podatkov, v našem primeru je to baza &orthwind. Vse izbire potrdimo s klikom na gumb OK in vrnemo se v

okno Data Source Configuration Wizard: nova podatkovna povezava je dobila ime ime_računalnika\sqlexpress.&orthwind.dbo. Če kliknemo na gumb + Connection string, se nam v oknu prikaže stavek/informacija, ki vsebuje podrobnosti povezave, ki smo ja pravkar določili. Ta informacija je zapisana v formatu, ki jo v nadaljevanju uporabi SQL provider za priklop na strežnik.

Kliknimo gumb �ext, da se odpre okno za shranjevanje informacije o povezavi v ustrezno konfiguracijsko datoteko. Ta opcija nam omogoča, da lahko string s podatki o povezavi na strežnik kadarkoli kasneje spremenimo, ne da bi bilo potrebno aplikacijo ponovno prevajati. To je še posebej pomembno, če že v tej fazi izgradnje aplikacije predvidevamo, da bomo kadarkoli kasneje uporabili bazo z drugim imenom, ali pa bomo spremenili pot do nje. Informacijo o povezavi shranimo kar pod imenom, ki nam jo ponudi čarovnik (&orthwindConnectionString). Kliknimo gumb &ext, da se odpre okno, v katerem iz izbrane podatkovne baze (&orthwind) izberemo podatke, ki jih želimo v nadaljevanju uporabiti. Podatke lahko pridobimo iz tabel (Tables), ki so sestavni del naše baze, ali pa iz baznih pogledov (Views). Ob tem bo čarovnik zgeneriral objekt DataSet imenovan &orthwindDataSet, ki ga bomo kasneje lahko uporabili za manipuliranje z pridobljenimi podatki. Objekt DataSet je v bistvu v delovnem pomnilniku shranjena kopija izbranih tabel in stolpcev iz podatkovne baze. Končno kliknemo gumb Finish in s tem je uporaba čarovnika zaključena.

Page 232: PROŠNJA - uranic.tsckr.siuranic.tsckr.si/VISUAL C#/VISUAL C#.pdf · MICROSOFT VISUAL C# .NET Srečo Uranič . Visual C# . ... Za začetek novega projekta v okolju Visual C# Standard

Visual C# . NET

Algoritmi in programski jeziki – 4.letnik

232

Med uporabo čarovnika za priklop na bazo smo nekje pri koncu tudi shranili informacijo o povezavi v ustrezno konfiguracijsko datoteko. Ta datoteka se imenuje app.config. Njeno ime se pojavi tudi v Solution Explorerju – gre za XML datoteko in si jo lahko kadarkoli tudi ogledamo: <?xml version="1.0" encoding="utf-8" ?> <configuration> <configSections> </configSections> <connectionStrings> <add name="Database_Northwind1.Properties.Settings.NorthwindConnectionString" connectionString="Data Source=sreco-PC\SQLExpress;Initial Catalog=Northwind;Integrated Security=True" providerName="System.Data.SqlClient" /> </connectionStrings> </configuration>

String za povezavo s podatkovno bazo se nahaja v delu datoteke označenim z <connectionStrings>. Ko aplikacijo prevajamo, se vsebina datoteke app.config uporabi za generiranje datoteke Database_<orthwind1.exe.config, ki vsebuje natanko iste informacije, v enakem formatu, shranjena pa je v isti mapi kot izvršilni program. Ta datoteka se imenuje application configuration file in jo moramo skupaj z izvršilnim programom skopirati na ciljni računalnik (tja, kjer pač želimo aplikacijo uporabljati). Če se želimo na tem računalniku priklopiti na drugo bazo podatkov (ali pa mogoče le spremeniti pot do baze z enakim imenom), lahko s poljubnim urejevalnikom le spremenimo vsebino datoteke v delu <connectionStrings>. Ko bomo ponovno zagnali našo aplikacijo, bo avtomatično uporabljena nova povezava.

Razumevanje pojmov DataSet, DataTable in TableAdapter

Objektni model ADO.NET uporablja za pridobivanje podatkov iz podatkovne baze objekte tipa DataSet. Aplikacija, v kateri so definirani DataSet objekti lahko izvaja poizvedbe preko katerih pridobi podatke, nato pa le-te prikaže ali pa jih ažurira. Navznoter pa objekt tipa DataSet vsebuje enega ali več objektov DataTable; vsak DataTable ustreza neki tabeli, ki smo jo označili pri definiciji objekta DataSet. V naslednji vaji bomo v objektu &orthwindDataSet prikazali dva objekta DataTable, imenovana Products in Suppliers. V podatkovni bazi �orthwind imata tabeli Products in Suppliers relacijo več proti ena (many-to-one): vsak produkt je dobavljen od enega samega dobavitelja, a vsak dobavitelj lahko dostavi več produktov. To relacijsko povezavo zagotavlja baza �orthwind z uporabo primarnih (primary) in zunanjih (foreign) ključev. Čarovnik Data Source Configuration uporabi to informacijo za kreiranje objekta DataRelation ki je del objekta &orthwindDataSet. Objekt DataRelation zato zagotavlja, da je enaka relacija, kot obstaja podatkovni bazi, podprta tudi med objektoma Products in Suppliers v delovnem pomnilniku. Ostane še vprašanje, kako naša aplikacija v času izvajanja zgradi ustrezni objekt DataSet. Odgovor leži v objektu imenovanem TableAdapter. Ta vsebuje metode, ki jih lahko uporabimo za izgradnjo objekta DataSet. Najpogostejši sta metodi imenovani Fill in GetData. Metoda Fill napolni obstoječi DataSet in vrne celo število, ki pove število vrstic pridobljenih podatkov, metoda GetData pa skreira nov, s podatki napolnjen DataSet. Poglejmo sedaj, kako vse skupaj izgleda v praksi: V meniju Data kliknimo Preview Data. Prikaže se pogovorno okno Preview Data, ki omogoča vpogled v

podatke, ki smo jih pridobili s pomočjo objekta DataSource, ki smo ga pravkar kreirali. V tem oknu odprimo spustni seznam Select an object to preview in prikazala se bo drevesna struktura, ki vsebuje izbrano vsebino našega objekta �orthwindDataSet. Prikazana sta dva objekta imenovana Products in Suppliers, pod vsakim od teh dveh objektov pa je še vozlišče imenovano Fill, GetData() (če vozlišče ni vidno, kliknite na znak +). Ta prikaz seveda ustreza objektu TableAdapter za vsak TableAdapter.

Page 233: PROŠNJA - uranic.tsckr.siuranic.tsckr.si/VISUAL C#/VISUAL C#.pdf · MICROSOFT VISUAL C# .NET Srečo Uranič . Visual C# . ... Za začetek novega projekta v okolju Visual C# Standard

Visual C# . NET

Algoritmi in programski jeziki – 4.letnik

233

Kliknimo vrstico Fill, GetData() pod vozliščem Products, nato pa še gumb Preview. V spodnjem oknu z

imenom Results se prikažejo vrstice iz tabele Products baze �orthwind.

Če bi si želeli ogledati vsebino tabele Suppliers, postopek ponovimo za vrstico Fill, GetData() pod vozliščem Suppliers. Okno zaprimo s klikom na gumb Close.

Prikaz podatkov iz neke podatkovne tabele v naši aplikaciji

Pridobljene podatke prikažimo sedaj še na obrazcu naše aplikacije. V Naši aplikaciji Database_&orthwind1 preklopimo obrazec na Designer View in lastnost Text preimenujmo v Suppliers and Products. Velikost obrazca nastavimo npr. na 800x410. V meniju Data kliknimo Show Data Sources. Prikaže se okno Data Sources in v njem objekt �orthwindDataSet z objektoma DataTable - to sta seveda objekta Products in Suppliers. S klikom na vozlišče + razširimo oba objekta. Kliknimo tabelo (DataTable) Supliers. Prikaže se spustni seznam/meni in izberimo opcijo Details. Na ta način bo vrstica tabele Suppliers prikazana kot množica podatkovnih polj in ne v tabelarični obliki (oblika DataGridView). Prikaz podatkov s pomočjo opcije Details je zelo uporaben za prikaz podatkov na strani »ena« v relacijski povezavi več proti ena, medtem ko je prikaz DataGridView bolj primeren na strani »več«.

Page 234: PROŠNJA - uranic.tsckr.siuranic.tsckr.si/VISUAL C#/VISUAL C#.pdf · MICROSOFT VISUAL C# .NET Srečo Uranič . Visual C# . ... Za začetek novega projekta v okolju Visual C# Standard

Visual C# . NET

Algoritmi in programski jeziki – 4.letnik

234

Kliknimo še v v vrstico SupplierID v tabeli Suppliers. Prikaže se nov spustni seznam: odprimo ga in prikažejo se nam različne možnosti prikaza podatkov SuplierID. Ker pa je polje SupplierID klučno polje v tabeli Suppliers in ga kot takega ne moremo (ne smemo) spreminjati, bomo v ponujenem spustnem seznamu izbrali opcijo Label. Nato celotno tabelo Suppliers primimo z miško in jo potegnimo v zgornji levi rob našega obrazca. Na obrazcu se prikažejo polja iz tabele Suppliers, pod obrazcem pa se pojavi nekaj novih gradnikov.

� OPOMBA: Če so polja na obrazcu prikazana previsoko na obrazcu in prekrijejo gradnik za navigacijo, jih (medtem ko so še označena) samo potegnimo nekoliko niže po obrazcu.

Komponenta Razlaga

northwindDataSet Podatkovni izvor uporabljen s pomočjo obrazca. To je objekt �orthwindDataSet. Z njegovo pomočjo imamo dostop do metod za ažuriranje podatkov v podatkovni bazi

suppliersBindingSource

Naloga te komponente je nekakšna povezava med gradniki na obrazcu in podatkovnim izvorom. Komponenta BindingSource poskrbi za pravilen prikaz podatkov v vrsticah. Vsebuje pa tudi metode za navigacijo po objektu DataSet (dodajanje, brisanje in ažuriranje podatkov)

suppliersTableAdapter Objekt TableAdapter za tabelo Suppliers, ki zagotavlja metode za pridobitev podatkov iz tabele Suppliers podatkovne baze �orthwind

suppliersBinding&avigator

Gradnik Binding�avigator je nekakšen standardiziran mehanizem za navigacijo po vrsticah v objektu DataSet. Ta gradnik je na obrazcu tudi viden, pojavi se pod vrhom obrazca, vsebuje pa gradnike za najpomembnejše operacije za podatkovno tabelo

Na obrazcu moramo sedaj prikazati še vsebino tabele Products. Kliknimo objekt DataTable z imenom Products ki je gnezden znotraj tabele Suppliers in ga povlecimo na obrazec, desno od polj tabele Suppliers. Na obrazcu se prikaže gradnik DataGridView, pod obrazcem pa dva nova gradnika: productsBindingSource (za koordinacijo vrstic v DataGridView z objektom northwindDataSet) in productsTableAdapter (za pridobitev vrstic podatkov iz podatkovne baze v Products Data Table).

� OPOMBA: Prepričajmo se, da je objekt Products, ki smo ga potegnili na obrazec res tisti, ki je gnezden znotraj objekta Suppliers in ne tisti, ki je na vrhu okna Data Sources.

Gradnik DataGridView nato razširimo čez celotno preostalo površino našega obrazca.

Page 235: PROŠNJA - uranic.tsckr.siuranic.tsckr.si/VISUAL C#/VISUAL C#.pdf · MICROSOFT VISUAL C# .NET Srečo Uranič . Visual C# . ... Za začetek novega projekta v okolju Visual C# Standard

Visual C# . NET

Algoritmi in programski jeziki – 4.letnik

235

V zgornjem desnem robu gradnika DataGridView (medtem, ko je ta še izbran) kliknimo na ročico, da se odpre dialog Data Grid View Tasks. To pogovorno okno lahko uporabljamo za hitro spreminjanje najpogosteje uporabljenih lastnosti gradnika DataViewGrid, za spreminjanje lastnosti prikazanih stolpcev in za spreminjanje opravil ki so podprta s tem gradnikom. Da ne bi po nepotrebnem v naši tabeli naredili nezaželene spremembe, zaenkrat odstranimo kljukice, ki omogočajo ažuriranje podatkov (Enable Editing, Enable Deleing in Enable Column Reordering).

Poženimo sedaj našo aplikacijo. V levem robu obrazca je prikazan prvi dobavitelj, desno pa izdelki, ki nam jih ta dobavitelj dobavlja. S pomočjo navigatorja (gradnik nad polji, ki

prikazujejo dobavitelja) se lahko premikamo po dobaviteljih, obenem pa se v desni tabeli

prikazujejo izdelki izbranega dobavitelja. S pomočjo navigatorja lahko dodajamo nove dobavitelje, oz. brišemo obstoječe.

� POZOR: Dejanske spremembe v bazi bodo izvedene le v primeru, da po koncu ažuririranja (dodajanja, spreminjanja ali brisanja) kliknemo ikono za shranjevanje.

Programska uporaba ADO.&ET (brez čarovnika) Pri izdelavi bolj kompleksnih aplikacij nam uporaba čarovnika v večini primerov ne bo zadoščala. Dostop do baze podatkov in kreiranje ustrezne poizvedbe bo v takih primerih potrebno realizirati programsko, s pisanjem

Page 236: PROŠNJA - uranic.tsckr.siuranic.tsckr.si/VISUAL C#/VISUAL C#.pdf · MICROSOFT VISUAL C# .NET Srečo Uranič . Visual C# . ... Za začetek novega projekta v okolju Visual C# Standard

Visual C# . NET

Algoritmi in programski jeziki – 4.letnik

236

kode. Programski dostop do baze podatkov in primeri, ki bodo sledili, so možni tudi v verzijah Express Edition. Pri programskem dostopu do baze podatkov potrebujemo dostop do razredov, ki tak dostop omogočajo. Ti razredi se nahajajo v imenskem prostoru System.Data.SqlClient, ki ga dodamo v naš projekt. using System.Data.SqlClient;

Imenski prostor System.Data.SqlClient vsebuje specializirane ADO.NET razrede, ki se uporabljajo za dostop do SQL strežnika. Deklarirajmo še nov objekt SQLConnection (stavek vrinimo npr. za konstruktorjem razreda Form1). SQLConnection je podrazed, ki je namenjen samo povezavam z bazami podatkov tipa SQL Server. SqlConnection dataConnection = new SqlConnection();

Nadaljnji stavki pa so odvisni od tega, ali želimo kreirati samo neko poizvedbo (npr. nek SELECT stavek) ali pa želimo zvesti pravo SQL transakcijo v bazi podatkov (npr. INSERT ali pa UPDATE stavek). Tako pri poizvedbi, kot tudi pri pravi SQL transakciji je nujna uporaba varovanega bloka, saj pri povezavah lahko pride do številnih problemov in obvestilo o napaki ter vrsti napake je še kako potrebno in dobrodošlo.

Splošna oblika kreiranja poizvedbe iz baze podatkov

Splošna oblika poizvedbe iz neke podatkovne tabele neke baze podatkov ima naslednjo obliko: try { SqlConnection sqlConnection = new SqlConnection("Integrated Security=true;Initial

Catalog=imeBazePodatkov;Data Source=imeRacunalnika\\SQLExpress"); SqlCommand dataCommand = new SqlCommand(); dataCommand.CommandText = "SELECT . . . "; dataCommand.Connection = sqlConnection; sqlConnection.Open();

//obdelava .. } catch (Exception ep) { //Obvestilo o napaki, oz. obdelava napak! } finally { //Na koncu prekinemo povezavo z bazo, da bodo spremembe res ažurirane dataConnection.Close(); }

Primer: Naslednji primer prikazuje, kako bi izvedli poizvedbo iz baze nabavaSQL (testna baza se nahaja na http://uranic.tsckr.si/BAZE_MS_SQL/). V bazi so tri tabele (Artikli, Dobavitelji in Skupine_artiklov). Radi bi naredili poizvedbo, s počjo katere bi iz tabele Artikli dobili vse tiste artikle, ki imajo vneseno polje Kolicina (torej je polje Kolicina različno od <ULL). Nato želimo še ugotoviti in izpisati skupno količino vseh artiklov v tabeli Artikli. To nam omogoča razred SqlDataReader, ki vsebuje metode za branje oz. pridobivanje posameznih vrednosti podatkov. Celotno kodo lahko zapišemo npr. v dogodek Load poljubnega obrazca. Ko bomo obrazec odprli, bomo v sporočilnem oknu dobili izpisano skupno število artiklov v tabeli Artikli znotraj podatkovne baze nabavaSQL. //Kreiramo nov objekt za povezavo z bazo podatkov SqlConnection dataConnection = new SqlConnection(); //Objektu za povezavo z bazo določimo parametre, ki so potrebni za povezavo dataConnection.ConnectionString = "Integrated Security=true;" + "Initial Catalog=nabavaSQL;" + "Data Source= mojRacunalnik \\SQLExpress";

Page 237: PROŠNJA - uranic.tsckr.siuranic.tsckr.si/VISUAL C#/VISUAL C#.pdf · MICROSOFT VISUAL C# .NET Srečo Uranič . Visual C# . ... Za začetek novega projekta v okolju Visual C# Standard

Visual C# . NET

Algoritmi in programski jeziki – 4.letnik

237

/*lahko tudi krajše: SqlConnection dataConnection = new SqlConnection("Integrated Security=true;Initial Catalog=nabavaSQL;Data Source=mojRacunalnik\\SQLExpress"); */ //Kreiramo nov objekt za SQL poizvedbo //SqlCommand je razred v ADO.NET, načrtovan za pridobivanje dostopa do SQL Server-ja SqlCommand dataCommand = new SqlCommand(); //napišemo še katere podatke želimo pridobiti in iz katere tabele ->zapišemo SELECT stavek dataCommand.CommandText = "SELECT Kolicina From Artikli WHERE Kolicina IS NOT NULL"; //lastnost Connecton sedaj nastavimo na ustrezni objekt dataConnection dataCommand.Connection = dataConnection; //Odpremo povezavo z bazo dataCommand.Connection.Open(); //Kreiramo podatkovni tok za branje in obdelavo vrstic pridobljenih podatkov /*najhitrejši način za pridobivanje podatkov iz SQL Server baze podatkov je z uporabo razreda SqlDataReader. Ta razred pridobi vrstice iz baze podatkov najhitreje kot omrežje sploh omogoča in podatke preda naši aplikaciji*/ SqlDataReader dataReader = dataCommand.ExecuteReader(); int skupaj=0; //metoda Read vrne true, če je poizvedba uspešna, sicer vrne false. while (dataReader.Read())//dokler obstajajo podatki { try { /*ker smo v SELECT stavku navedli le en podatek iz vsake vrstice tabele Artikli (Kolicina) ima ta podatek znotraj enega stavka indeks enak 0!*/ skupaj = skupaj + Convert.ToInt32(dataReader.GetValue(0)); } catch { MessageBox.Show("Napaka pri branju podatkov!"); } } dataReader.Close();//zapremo podatkovni tok dataCommand.Connection.Close(); //Zapremo povezavo z bazo

� POZOR: V stavku ConnectionString moramo seveda mojRacunalnik nadomestiti z pravim imenom našega računalnika.

Vsebina stavka ConnectionString objekta dataConnection je povsem enaka, kot jo je zgeneriral čarovnik v primeru, da smo za dostop in prikaz podatkov podatkovne baze �orthwind uporabili čarovnika. Posamezni deli stavka ConnectionString so med seboj ločeni s podpičjem. V stavku smo označili, da bomo za povezavo z bazo naše lokalne instance SQL Server-ja uporabili kar takšno prijavo, kot jo imamo v okolju Windows (Windows Authentication). Tak način je priporočljiv, saj v tem primeru uporabniku ni potrebno vnašati njegovega uporabniškega imena in gesla. Kadar pa bomo pisali aplikacijo namenjeno uporabnikom, ki bodo do nje dostopali npr. preko omrežnih povezav ali celo preko interneta (remote users), bomo v stavku ConnectionString zapisali tudi uporabniško ime in geslo, npr. takole:

string username= . . .; string password= . . .; //uporabniku bomo ponudili npr. dve vnosni polji za vnos up. imena in gesla SqlConnection MojaPovezava = new SqlConnection(); MojaPovezava.ConnectionString = "User ID=" + username + ";Password=" + password + ";Initial Catalog=Northwind;Data Source=ime_Racunalnika\\SQLExpress";

Splošna oblika SQL transakcije nad bazo podatkov

Splošna oblika SQL transakcije nad neko bazo podatkov ima takole obliko: try { SqlConnection sqlConnection = new SqlConnection("Integrated Security=true;Initial

Catalog=imeBazePodatkov;Data Source=imeRacunalnika\\SQLExpress");

Page 238: PROŠNJA - uranic.tsckr.siuranic.tsckr.si/VISUAL C#/VISUAL C#.pdf · MICROSOFT VISUAL C# .NET Srečo Uranič . Visual C# . ... Za začetek novega projekta v okolju Visual C# Standard

Visual C# . NET

Algoritmi in programski jeziki – 4.letnik

238

SqlCommand dataCommand = new SqlCommand(); dataCommand.CommandText = "Insert INTO . . . ')"; dataCommand.Connection = sqlConnection; sqlConnection.Open(); dataCommand.ExecuteNonQuery(); catch (Exception ep) { //Obvestilo o napaki, oz. obdelava napak! } finally { //Na koncu prekinemo povezavo z bazo, da bodo spremembe res ažurirane dataConnection.Close(); }

Primer: Naslednji primer prikazuje, kako bi izvedli pravo SQL transakcijo v tabeli Artikli baze nabavaSQL (testna baza se nahaja na http://uranic.tsckr.si/BAZE_MS_SQL/). V bazi so tri tabele (Artikli, Dobavitelji in Skupine_artiklov). V tabelo Skupine_Artiklov bi radi vstavili novo vrstico. V vsaki vrstici tabele sta dve polji (dva podatka): ID_Skupina (ključno polje, Auto Increment polje) in Skupina (niz 50 znakov). Ker je polje ID_Skupina deklarirano kot AutoIncrement (vrednost se pri dodajanju nove vrstice v tabelo dodeli avtomatično), moramo v novo vrstico tabele Skupine_artiklov dodati le vrednost polja Skupina. Celotno kodo lahko zapišemo npr. v odzivni dogodek Click nekega gumba na poljubnem obrazcu. Ob kliku na ta gumb se bo v tabelo Skupina_Artiklov dodala nova vrstica. //Kreiramo nov objekt za povezavo z bazo podatkov SqlConnection dataConnection = new SqlConnection(); //Objektu za povezavo z bazo določimo parametre, ki so potrebni za povezavo dataConnection.ConnectionString = "Integrated Security=true;" + "Initial Catalog=nabavaSQL;" + "Data Source= mojRacunalnik \\SQLExpress"; /*lahko tudi krajše: SqlConnection dataConnection = new SqlConnection("Integrated Security=true;Initial Catalog=nabavaSQL;Data Source=mojRacunalnik\\SQLExpress"); */ //Kreiramo nov objekt za SQL poizvedbo SqlCommand dataCommand = new SqlCommand(); //Trenutno poizvedbo vežemo na ustrezno povezavo z bazo dataCommand.Connection = dataConnection; try { //Kreiramo transakcijski SQL stavek, npr: dataCommand.CommandText = "Insert INTO Skupine_artiklov (Skupina) VALUES ('Bla bla')"; //Napovemo tip poizvedbe: privzeti tip je Text, zato naslednji stavek v tem primeru NI //potreben dataCommand.CommandType = CommandType.Text; //Odpremo povezavo z bazo dataConnection.Open(); //dejansko izvedemo transakcijski SQL stavek; metoda vrne celo število ki pove, na koliko //vrstic je vpilvala naša transakcija! dataCommand.ExecuteNonQuery(); } catch (Exception ep) { //Obvestilo o napaki, oz. obdelava napak! MessageBox.Show(e.ToString()); } finally { //Na koncu prekinemo povezavo z bazo, da bodo spremembe res ažurirane dataConnection.Close(); }

Vaja:

Page 239: PROŠNJA - uranic.tsckr.siuranic.tsckr.si/VISUAL C#/VISUAL C#.pdf · MICROSOFT VISUAL C# .NET Srečo Uranič . Visual C# . ... Za začetek novega projekta v okolju Visual C# Standard

Visual C# . NET

Algoritmi in programski jeziki – 4.letnik

239

Odprimo nov projekt in ga poimenujmo �orthwind_Customer. Obrazec preklopimo na kodirni pogled (v Solution Explorerju kliknimo View Code, ali pa v meniju View kliknimo Code) in dodajmo using stavek using System.Data.SqlClient;

Na obrazec postavimo nekaj gradnikov, v katerih bomo prikazali podatke o določeni stranki tabele Customers podatkovne baze �orthwind. Vnosno polje, v katerega bomo vnesli ID stranke, katere podatki nas zanimajo. Po kliku na gumb Išči… (button1) se prične iskanje stranke v tabeli Customers. Koda, ki pripada dogodku Click gumba Išči… je naslednja (zaradi enostavnosti v tem primeru nismo uporabili varovalnega bloka!!!): private void button1_Click(object sender, EventArgs e) { //SqlCommand je razred v ADO.NET, načrtovan za pridobivanje dostopa do SQL Server-ja SqlCommand dataCommand = new SqlCommand();//kreiranje objekta SqlCommand //lastnost Connecton sedaj nastavimo na ustrezni objekt dataConnection dataCommand.Connection = dataConnection; //napišemo še katere podatke želimo pridobiti in iz katere tabele ->zapišemo SELECT stavek dataCommand.CommandText = "SELECT CustomerID,CompanyName,ContactName,ContactTitle,Address, City, Region, PostalCode, Country,Phone,Fax "; dataCommand.CommandText += "From Customers WHERE CustomerID='" + customerID.Text + "'"; /*najhitrejši način za pridobivanje podatkov iz SQL Server baze podatkov je z uporabo razreda SqlDataReader. Ta razred pridobi vrstice iz baze podatkov najhitreje kot omrežje sploh omogoča in podatke preda naši aplikaciji*/ SqlDataReader dataReader = dataCommand.ExecuteReader(); //metoda Read vrne true, če je poizvedba uspešna, sicer vrne false. if (dataReader.Read() == true) //preverimo če smo zahtevano stranko našli {

//če je neko polje v bazi, katerega vsebino želimo prebrati, enako null, metode //dataReager.GetXXX vrnejo napako (izjemo)zato uporabimo metodo

//dataReader.GetValue(X), prebrano vrednost pa shranimo v spremenljivko tipa object object pomozna; pomozna = dataReader.GetValue(1);//1 pomeni polje iz SELECT stavka z indeksom //število 1 (začetni inedks je 0!!!) tBPodjetje.Text = pomozna.ToString(); pomozna = dataReader.GetValue(2); tBKontaktna.Text = pomozna.ToString(); pomozna = dataReader.GetValue(3); tbFunkcija.Text = pomozna.ToString(); pomozna = dataReader.GetValue(4); tBNaslov.Text = pomozna.ToString(); pomozna = dataReader.GetValue(5);

Page 240: PROŠNJA - uranic.tsckr.siuranic.tsckr.si/VISUAL C#/VISUAL C#.pdf · MICROSOFT VISUAL C# .NET Srečo Uranič . Visual C# . ... Za začetek novega projekta v okolju Visual C# Standard

Visual C# . NET

Algoritmi in programski jeziki – 4.letnik

240

tBMesto.Text = pomozna.ToString(); pomozna = dataReader.GetValue(6); tBRegija.Text = pomozna.ToString(); pomozna = dataReader.GetValue(7); tBPosta.Text = pomozna.ToString(); pomozna = dataReader.GetValue(8); tBDrzava.Text = pomozna.ToString(); pomozna = dataReader.GetValue(9); tBTelefon.Text = pomozna.ToString(); pomozna = dataReader.GetValue(10); tBFax.Text = pomozna.ToString(); } else MessageBox.Show("Stranka s tem imenom ne obstaja!"); //ko je poizvedba zaključena, sprostimo odprte vire za dostop do podatkov dataReader.Close(); }

Ko delo z bazo podatkov končamo, zapremo (prekinemo) še povezavo z podatkovno bazo: private void Form1_FormClosing(object sender, FormClosingEventArgs e) { dataConnection.Close(); } Vaja: Kreirajmo obrazec, ki bo za izbrano stranko iz tabele Customers podatkovne baze �orthwind prikazal vsa njena naročila. Dodajmo using stavek using System.Data.SqlClient;

Deklarirajmo še nov objekt SQLConnection (stavek vrinimo npr. za konstruktorjem razreda Form1). SqlConnection dataConnection = new SqlConnection();

V dogodek Load obrazca Form1 zapišimo varovalni blok, znotraj katerega bomo skušali vzpostaviti povezavo z bazo podatkov �orthwind. Če povezava uspe, napolnimo gradnik ComboBox (ime gradnika je comboBox1) z imeni (CustomerID) vseh strank iz tabele Customers. Ko projekt zaženemo in se obrazec odpre, imamo v gradniku ComboBox tako že vse stranke iz tabele Customer. private void Form1_Load(object sender, EventArgs e) { try { dataConnection.ConnectionString = "Integrated Security=true;" + "Initial Catalog=Northwind;" + "Data Source=mojRacunalnik\\SQLExpress"; dataConnection.Open(); SqlCommand dataCommand = new SqlCommand(); dataCommand.Connection = dataConnection; //kreiramo poizvedbo dataCommand.CommandText = "SELECT CustomerID "; dataCommand.CommandText += "From Customers"; SqlDataReader dataReader = dataCommand.ExecuteReader(); //v comboBox1 zapišemo vse stranke iz tabele Customers while (dataReader.Read())//dokler obstajajo podatki { comboBox1.Items.Add(dataReader.GetValue(0).ToString()); } dataReader.Close();//zapremo podatkovni tok } catch { MessageBox.Show("Napaka pri dostopu do baze podatkov!");

Page 241: PROŠNJA - uranic.tsckr.siuranic.tsckr.si/VISUAL C#/VISUAL C#.pdf · MICROSOFT VISUAL C# .NET Srečo Uranič . Visual C# . ... Za začetek novega projekta v okolju Visual C# Standard

Visual C# . NET

Algoritmi in programski jeziki – 4.letnik

241

} }

Na obrazec postavimo sedaj gradnik ListView in mu nastavimo naslednje lastnosti:

• Columns – kliknimo na tropičje in nato v oknu, ki se prikaže dodajmo tri stolpce (3 x kliknemo na gumb Add). Stolpce poimenujemo (lastnost Text) Zap.Številka (TextAlign Left), Datum naročila (TextAlign Center) in Cena (TextAlign Right).

• GridLines = true • HeaderStyle = &onclickable • View = Details

Na obrazec dodamo še tri gradnike Label, za prikaz vseh matičnih podatkov (ime, naslov) izbrane stranke.

ComboBox

ListWiew

Ob izbiri stranke v gradniku ComboBox želimo, da se v gradniku ListBox prikažejo vsa naročila te stranke. Ustrezno kodo zapišemo v dogodek SelectedIndexChange našega gradnika ComboBox. private void comboBox1_SelectedIndexChanged(object sender, EventArgs e) { listView1.Items.Clear();//brišemo prejšnjo vsebino gradnika ListBox //iz tabele Customers najprej pridobimo matične podatke izbrane stranke SqlCommand dataCommand = new SqlCommand(); dataCommand.Connection = dataConnection; dataCommand.CommandText="SELECT CustomerID,CompanyName,ContactName,Address,City,Country "; dataCommand.CommandText += "From Customers WHERE CustomerID='" + comboBox1.Text + "'"; SqlDataReader dataReader = dataCommand.ExecuteReader(); if (dataReader.Read() == true) { object pom; pom = dataReader.GetValue(1); //matične podatke izbrane stranke zapišemo na obe labeli label2.Text = "Naziv/podjetje: " + (dataReader.GetValue(1)).ToString()+", kontaktna oseba: " + (dataReader.GetValue(2)).ToString(); label3.Text = "Naslov: "+(dataReader.GetValue(3)).ToString()+", "+(dataReader.GetValue(4)).ToString()+", "+(dataReader.GetValue(5)).ToString(); dataReader.Close();//zapremo podatkovni tok } //iz tabele Orders pridobimo vsa naročila izbrane stranke dataCommand.CommandText = "SELECT OrderDate,Freight,ShipName,ShipAddress,ShipCity, ShipCountry "; dataCommand.CommandText += "From Orders WHERE CustomerID='" + comboBox1.Text + "'"; dataReader = dataCommand.ExecuteReader(); int i = 0; string naslov = ""; while (dataReader.Read())//dokler ne zmanjka podatkov { //vsako vrstico iz tabele Order na primeren način zapišemo v vrstico gradnika ListBox object pomozna; listView1.Items.Add((i + 1).ToString());//v prvi koloni je zaporedna številka vrstice pomozna = dataReader.GetValue(0);//0 = inndeks polja iz SELECT stavka

Page 242: PROŠNJA - uranic.tsckr.siuranic.tsckr.si/VISUAL C#/VISUAL C#.pdf · MICROSOFT VISUAL C# .NET Srečo Uranič . Visual C# . ... Za začetek novega projekta v okolju Visual C# Standard

Visual C# . NET

Algoritmi in programski jeziki – 4.letnik

242

listView1.Items[i].SubItems.Add(pomozna.ToString()); pomozna = dataReader.GetValue(1); listView1.Items[i].SubItems.Add(pomozna.ToString()); pomozna = dataReader.GetValue(2); listView1.Items[i].SubItems.Add(pomozna.ToString()); naslov = (dataReader.GetValue(3)).ToString() + " " + (dataReader.GetValue(4)).ToString() + " " + (dataReader.GetValue(5)).ToString(); i++; //povečamo indeks vrstice gradnika ListBox } label1.Text = "Naslov: " + naslov; //na labelo zapišemo strankin naslov dataReader.Close();//zapremo podatkovni tok }

Ko delo z bazo podatkov končamo, zapremo (prekinemo) še povezavo z podatkovno bazo: private void Form1_FormClosing(object sender, FormClosingEventArgs e) { dataConnection.Close(); }

Razred SqlDataReader

Med največjimi pomanjkljivostmi večuporabniških aplikacij, ki delajo z bazami podatkov, je zaklepanje podatkov. Pogosto se zgodi, da pri pridobivanju podatkov (vrstic) iz tabel, aplikacije te vrstice v tabeli zaklenejo saj jih na ta način zaščitijo, da jih ne bi med tem ažurirali drugi uporabniki. V ekstremnih primerih aplikacija drugim uporabnikom celo prepreči branje vrstic podatkov, ki so zaklenjene. Če je poizvedba taka, da je število vrstic zelo veliko, se seveda zgodi, da je zaklenjen kar dobršen del tabele, iz katere se izvaja poizvedba. Ob tem se seveda lahko še zgodi, da tabelo istočasno uporablja veliko število porabnikov. Uporabniki morajo zaradi tega čakati na sproščanje zaklenjenih vrstic, kar pa ima seveda za posledico počasno delovanje programa, zmedo in slabo voljo uporabnikov. Razred SqlDataReader je bil narejen prav z namenom, da do prej opisanih težav ne bi prihajalo. Podatke iz tabel pridobiva enega za drugim in ne pušča zaklenjenih vrstic v tabeli, ko je vsebina vrstice enkrat pridobljena. Tak način pridobivanja podatkov (vrstic) iz tabel pa seveda pomeni veliko prednost pri izboljšanju konkurenčnosti naše aplikacije. Tudi pri enostavnem pridobivanju podatkov nudi razred SqlDataReader večje zmožnosti kot pa uporaba razreda DataSet. Objekti tipa DataSet za predstavitev podatkov ki jih hranijo uporabljajo XML. Tak način seveda nudi veliko fleksibilnost, saj lahko vsaka komponenta, ki zna brati XML format, te podatke kasneje tudi obdeluje. Razred SqlDataReader pa uporablja naraven SQL Server data-transfer format za pridobivanje podatkov neposredno iz baze, tako da podatke ni potrebno najprej konvertirati v nek drug format, npr. XML. Razred SqlDatReader se torej odlikuje z boljšimi v performansami, na drugi strani pa nam razred DataSet nudi več fleksibilnosti.

Zapiranje povezav z bazo podatkov

V starejših aplikacijah so programerji uporabljali pristop, da se je povezava z bazo vzpostavila ob zagonu aplikacije, prekinila pa šele po zaključku dela z aplikacijo. Tak pristop so zagovarjali zaradi tega, ker je bila operacija odpiranja in zapiranja povezave z bazo dragocena in časovno potratna. Slaba stran takih aplikacij je seveda ta, da ima vsak uporabnik, ki uporablja to aplikacijo, z bazo vzpostavljeno neprekinjeno povezavo, ne glede nato, ali jo uporablja ali pa ne (tudi če je šel npr. na kosilo). Poleg tega ima večina baz tudi omejeno število uporabnikov, ki lahko do nje hkrati dostopajo (pogosto je temu tako tudi zaradi omejenega števila licenc!). Večina .�ET Framework podatkovnih skrbnikov (data providers), med njimi seveda tudi SQL Server provider, je opremljenih s tehnologijo imenovano connection pooling. Podatkovne povezave so kreirane in zadržane v nekem skupnem bazenu (pool). Če aplikacija zahteva povezavo, podatkovni skrbnik (data access provider) povzame naslednjo prosto povezavo iz tega bazena, ob zapiranju pa je le ta vrnjena v bazen in postane dostopna naslednji aplikaciji, ki zahteva povezavo. To pa pomeni, da odpiranje in zapiranje povezav s podatkovnimi

Page 243: PROŠNJA - uranic.tsckr.siuranic.tsckr.si/VISUAL C#/VISUAL C#.pdf · MICROSOFT VISUAL C# .NET Srečo Uranič . Visual C# . ... Za začetek novega projekta v okolju Visual C# Standard

Visual C# . NET

Algoritmi in programski jeziki – 4.letnik

243

tabelami ni več neka potratna operacija, saj zapiranje in odpiranje povezave ne pomeni več priklop oz. odklop iz baze podatkov. Odpiranje povezave je preprosto le stvar pridobitve že odprte povezave iz bazena. Zaradi tega se pri izdelavi aplikacij, ki delajo z bazami podatkov držimo načela, da neko povezavo odpremo šele tedaj, ko jo potrebujemo, ko pa je ne potrebujemo več pa jo takoj zapremo.

Delo s povezavami podatkov (Data Binding) in objekti razreda DataSet V prejšnjem poglavju smo spoznali temelje uporabe orodja Microsoft ADO.NET za kreiranje SQL poizvedb in za ažuriranje baze podatkov. Uporabili smo bodisi čarovnika, ki nam ga nudi razvojno okolje Visual C#, bodisi smo enostavne poizvedbe kreirali sami – programsko. Za potrebe povezave s podatki smo napisali zelo malo kode. V tem poglavju pa bomo spoznali, da nam Microsoft Visual Studio omogoča tudi pisanje mnogo bolj kompleksnih aplikacij, s katerimi bomo dosegli veliko večje funkcionalnosti, obenem pa bomo ob pisanju spoznali ozadje takih povezav.

Povezava okenskih gradnikov s podatkovnim izvorom

Mnoge izmed okenskih gradnikov lahko povežemo z nekim podatkovnim izvorom, ali pa so temu namenjeni. Ko je povezava vzpostavljena, je lastnost gradnika, ki je namenjena povezavi, nadomeščena s lastnostjo, ki poskrbi za povezavo in obratno. Okenski obrazci sicer podpirajo dva tipa povezav z izvorom podatkov: enostavnega in kompleksnega.

Objekt DataSet in enostavno povezovanje s podatkovnim izvorom

Izvor podatkov je lahko praktično karkoli: od celice v gradniku DataSet, do vrednosti neke lastnosti in preproste spremenljivke. Preproste povezave s podatki lahko izvedemo z uporabo lastnosti DataBinding izbranega gradnika. Naslednji primer prikazuje kreiranje objekta DataSet, ki bo predstavljal izvor podatkov, ki bo vrnil en sam podatek, nato pa bomo s tem podatkom povezali lastnost Text gradnika Label. Kreirajmo torej nov projekt in ga poimenujmo ProductMaintenance. V meniju Project kliknimo opcijo Add &ew Item. Odpre se pogovorno okno Add &ew Item, ki prikazuje seznam predlog/objektov, ki jih lahko dodamo k projektu. Izberimo predlogo DataSet in jo poimenujmo �orthWindDataSet.xsd (kratica xsd je obvezna: definiran DataSet je bistvu XML shema, ki jo Visual Studio uporabi za generiranje kode ko gradimo aplikacijo).

Page 244: PROŠNJA - uranic.tsckr.siuranic.tsckr.si/VISUAL C#/VISUAL C#.pdf · MICROSOFT VISUAL C# .NET Srečo Uranič . Visual C# . ... Za začetek novega projekta v okolju Visual C# Standard

Visual C# . NET

Algoritmi in programski jeziki – 4.letnik

244

Kliknimo Add in na ekranu se odpre okno DatSet Designer.

V oknu Toolbox (orodjarna), razširimo skupino DataSet in izberimo na gradnik TableAdapter. Nato kliknimo kamorkoli v okno DataSet Designer. V to okno se s tem dodata objekta DataTable in TableAdapter, prikaže pa se čarovnik TableAdapter Configuratin Wizard.

V oknu izberimo ustrezno povezavo (le to smo skreirali že v eni od prejšnjih vaj, če pa povezave še nimamo, jo skreiramo tako, kot smo to naredili v poglavju Uporaba baze podatkov).

Izbiro povezave potrdimo s klikom na gumb �ext. Prikaže se okno Save the connection string to the application configuration file – konfiguracijo shranimo pod privzetim imenom &orthwindConnectionStrings in kliknimo na gumb �ext. Odpre se okno Choose a Command Type.

V oknu lahko izberemo, kako bo naš TableAdapter dostopal do baze podatkov. Zagotovimo si lahko lastne SQL stavke, lahko dostopamo do čarovnika za generiranje t.i. stored procedur, ki bodo za nas enkapsulirale SQL stavke, ali pa lahko uporabimo že obstoječe stored procedure. Za našo vajo izberimo opcijo Use SQL statements in kliknimo gumb �ext.

Page 245: PROŠNJA - uranic.tsckr.siuranic.tsckr.si/VISUAL C#/VISUAL C#.pdf · MICROSOFT VISUAL C# .NET Srečo Uranič . Visual C# . ... Za začetek novega projekta v okolju Visual C# Standard

Visual C# . NET

Algoritmi in programski jeziki – 4.letnik

245

Odpre se okno Enter a SQL Statement, v katerega vtipkajmo naslednji SQL SELECT stavek, ki bo ugotovil število vrstic podatkov v tabeli Products: SELECT COU&T (*) AS &umProducts FROM Products Po vnosu SELECT stavka kliknimo gumb Advanced Options in prikaže se okno Advanced Options Dialog Box. Gradnik oz. objekt TableAdapter nam namreč poleg pridobivanja podatkov iz baze podatkov omogoča tudi vstavljanje, ažuriranje in brisanje vrstic. Čarovnik nam tako lahko avtomatično zgenerira tudi SQL INSERT, UPDATE in DELETE stavke za podatkovno tabelo, ki smo jo določili v SELECT stavku.

V našem primeru teh dodatnih stavkov ne bomo potrebovali, saj želimo izvedeti le za število vrstic v tabeli Products, zato odstranimo oznako (kljukico) v gradnikih Check Box v oknu Advanced Options in kliknimo gumb OK.

Zopet se vrnemo v okno Choose a Command Type, kjer le kliknimo gumb �ext. Odpre se pogovorno okno Choose Methods to Generate. Objekt TableAdapter namreč lahko zgenerira dve metodi za zasedanje objekta DatSet s podatki pridobljenimi z baze podatkov

• metoda Fill pričakuje za parameter že obstoječi objekt DataTable ali pa DataSet, ki je napolnjen s podatki

• metoda GetData kreira nov objekt DataTable in ga napolni s podatki. V našem primeru pustimo izbrani obe metodi in kliknimo gumb �ext. Vse informacije, ki smo jih v prejšnjih oknih določili oz. napisali sedaj čarovnik uporabi in zgenerira nov razred TableAdapter. Čarovnika sedaj zaprimo s klikom na gumb Finish. V oknu DataSet Designer se prikažeta razreda TableAdapter, imenovan DataTable1TableAdapter in pripadajoči razred DataTable imenovan DataTable1. V oknu izberimo postavko DataTable1 in nato v oknu Properties ime (lastnost Name) spremenimo v &umProductsTable. Nato izberimo še postavko DataTable1TableAdapter in ime spremenimo v &umProductsTableTableAdapter. Okno sedaj izgleda takole:

V meniju Build sedaj kliknimo opcijo Build Solution in Visual C# nam bo zgeneriral kodo in objekte potrebne za nadaljevanje te vaje. Adapter �umProductsTableAdapter torej iz baze pridobi ustrezen podatek (število zapisov podatkovne tabele Products) in le-tega shrani v

Page 246: PROŠNJA - uranic.tsckr.siuranic.tsckr.si/VISUAL C#/VISUAL C#.pdf · MICROSOFT VISUAL C# .NET Srečo Uranič . Visual C# . ... Za začetek novega projekta v okolju Visual C# Standard

Visual C# . NET

Algoritmi in programski jeziki – 4.letnik

246

objekt DataTable z imenom �umPoducts, vse skupaj pa je sestavni del objekta DataSet z imenom �orthwindDataSet. V nadaljevanju bomo ta podatek (število zapisov tabele Products) povezali z lastnostjo Text gradnika Label. Ko bomo kasneje aplikacijo zagnali, se bo na labeli izpisal ustrezen podatek o številu zapisov. Obrazec Form1 sedaj najprej preimenujmo v ProductsForm.cs, spremenimo njegovo lastnost Text prav tako v ProductsForm in na obrazec postavimo eno poleg druge dve labeli. Levi labeli spremenimo lastnost Text v Število produktov: , desno labelo pa poimenujmo (lastnost Name) numProducts. Nato razširimo lastnost DataBindings druge labele (labele z imenom steviloProduktov), kliknimo na lastnost Text (znotraj DataBindings) in kliknimo na padajoči meni, ki se prikaže. Meni prikazuje drevesni pogled na dostopne vire podatkov. Razširimo vozel Other Data Sources, razširimo ProjectData Sources, razširimo &orthwindDataSet, razširimo &umProductsTable in končno izberimo &umProducts. S tem smo povezali lastnost Text naše labele s stolpcem �umProducts v objektu DataTable z imenom &umProductsTable. Visual Studio nam je ob tem skreiral instanco razreda �orthwindDataSet in ga poimenoval northwindDataSet, instanco razreda �umProductsTableTableAdapter in ga poimenoval numProductsTableTableAdapter, in objekt BindingSource imenovan numProductsTableBindingSource. Vsi trije objekti so v projektu vidni pod obrazcem. Kliknimo na objekt numProductsTableBindingSource pod obrazcem. V oknu Properties kliknimo na lastnost DataSource. Ta lastnost določa objekt DataSet, ki ga objekt BindingSource uporablja za priklop na bazo podatkov. Kliknimo na lastnost DataMember. Ta lastnost določa, da objekt DataTable v northwindDataSet, ki se obnaša kot izvor podatkov; nastavljen je na �umProductsTable. Kliknimo še na labelo numProducts in razširimo lastnost DataBinding. Lastnost Text je nastavljena numProductsTableBindingSource – �umProducts. Poglejmo še kodo, ki pripada obrazcu. Preklopimo pogled obrazca na View – Code in si oglejmo metodo Produkti_Load. Kodo je zgeneriral Visual Studio sam, izvede pa se seveda ob zagonu naše aplikacije, pred prvim odpiranjem obrazca. private void Produkti_Load(object sender, EventArgs e) { this.numProductsTableTableAdapter.Fill(this.northwindDataSet.NumProductsTable); }

Ta stavek nam pove, kako naša labela pridobi podatke iz baze:

• adapter numProductsTableTableAdapter se poveže z bazo in z izvedbo SQL SELECT stavka vrne podatek o številu vrstic tabele Products

• ko se obrazec zažene, adapter numProductsTableTableAdapter nato to informacijo uporabi za napolnitev objekta DataTable z imenom &umProductsTable

• objekt numProductsTableBindingSource poveže stolpec �umProducts v objektu &umProductsTable (ki je tipa DataSet) z lastnostjo Text labele na obrazcu.

Končno poženimo našo aplikacijo. Na labeli na našem obrazcu se izpiše število vrstic tabele Products (če v tabeli nismo delali nikakršnih sprememb je ta številka enaka 77). V zgornjem primeru smo za povezavo lastnosti gradnika Label in podatkovnim izvorom uporabili čarovnika. Lastnost DataBinding nekega gradnika pa lahko nastavimo tudi dinamično, tako da napišemo kodo, ki se bo izvedla v času izvajanja naše aplikacije. Lastnost DataBinding vsebuje tudi metodo Add, ki jo lahko uporabimo za povezavo lastnosti gradnika z podatkovnim izvorom. Na obrazec npr. postavimo še eno labelo in jo poimenujmo mojaLabela. Lastnost Text znotraj lastnosti DataBindings lahko sedaj povežemo s stolpcem �umProducts v numProductsTableBindingSource takole: mojaLabela.DataBindings.Add("Text", numProductsTableBindingSource, "NumProducts");

Page 247: PROŠNJA - uranic.tsckr.siuranic.tsckr.si/VISUAL C#/VISUAL C#.pdf · MICROSOFT VISUAL C# .NET Srečo Uranič . Visual C# . ... Za začetek novega projekta v okolju Visual C# Standard

Visual C# . NET

Algoritmi in programski jeziki – 4.letnik

247

Parametri, ki smo jih posredovali metodi Add so ime lastnosti gradnika, ki ga želimo povezati s podatkovnim izvorom, ime objekta, ki vsebuje podatke ki jih želimo povezati , tretji parameter pa je član, ki je trenutno zadolžen za oskrbo s podatki. Metodo Add lahko uporabimo tudi za povezavo z lastnostjo drugih gradnikov, neodvisno od podatkovnih izvorov. Naslednji primer prikazuje povezavo lastnosti Text labele mojaLabela z lastnostjo Text gradnika TextBox z imenom mojText. mojaLabela.DataBindings.Add("Text", mojText, "Text");

Ko tipkamo besedilo v gradnik mojText, se labela mojaLabela avtomatično spreminja skladno z vpisanim tekstom.

Kompleksno povezovanje s podatkovnim izvorom

V prejšnjih primerih smo pokazali, kako uporabimo preprosto podatkovno povezavo med lastnostjo nekega objekta in posamezno vrednostjo v podatkovnem izvoru. Če pa želimo prikazati večjo količino podatkov nekega podatkovnega izvora, pa je podatkovno povezovanje bolj zamotano, kompleksno. V naslednjem primeru bomo podatkovno povezovanje uporabili za prikaz imen dobaviteljev iz baze podatkov v gradniku ComboBox in nato izpisali podatek SupplierID dobavitelja, ki ga bo uporabnik izbral. Odprimo nov projekt in ga poimenujmo npr. �orthwind_ComboBox. Na obrazec postavimo tri labele in en

ComboBox. Lastnost Text obrazca spremenimo v Dobavitelji. Gradnik ComboBox na obrazcu poimenujmo supplierList, spodnjo labelo pa supplierID.

V meniju Project izberimo Add �ew Item, nato pa v oknu izberimo predlogo DataSet, jo poimenujmo northwindDataSet in kliknimo gumb Add. Prikaže se okno DataSetDesigner. Z uporabo okna Toolbox dodajmo v DataSet nov TableAdapter. Odpre se okno TableAdapter Configuration Wizard. V oknu izberimo ustrezno povezavo - ime_računalnika\sqlexpress.�orthwind.dbo (le to smo skreirali že v eni od prejšnjih vaj, če pa povezave še nimamo, jo skreiramo tako, kot smo to naredili v poglavju Uporaba baze podatkov). Izbiro povezave potrdimo s klikom na gumb �ext. Prikaže se okno Save the connection string to the application configuration file – konfiguracijo shranimo pod privzetim imenom �orthwindConnectionString in kliknimo na gumb �ext. Odpre se okno Choose a Command Type, v katerem izberemo opcijo Use SQL statements in kliknimo �ext. V oknu Enter a Statement page kliknimo Query Builder. Odpreta se pogovorni okni Query Builder in Add Table,

ki nam omogočata oblikovanje SELECT stavkov s pomočjo čarovnika. V oknu Add Table izberimo tabelo Suppliers, kliknimo Add in nato Close. Tabela Suppliers se doda v pogovorno okno Query Builder. Odkljukajmo vrstici SupplierID in Company�ame.

Page 248: PROŠNJA - uranic.tsckr.siuranic.tsckr.si/VISUAL C#/VISUAL C#.pdf · MICROSOFT VISUAL C# .NET Srečo Uranič . Visual C# . ... Za začetek novega projekta v okolju Visual C# Standard

Visual C# . NET

Algoritmi in programski jeziki – 4.letnik

248

V spodnjem delu tega okna se prikaže SQL stavek, ki ustreza naši izbiri: SELECT SupplierID, Company&ame FROM Suppliers Če v oknu kliknemo gumb Execute Query, se nad gumbom prikaže rezultat poizvedbe. Okno zaprimo s klikom na gumb OK, da se vrnemo v okno TableAdapter Configuration Wizzard. SELECT stavek se ob tem skopira v to okno. Ker aplikacija, ki jo pišemo ne bo spreminjala dobaviteljevih podatkov (izvedli bomo le poizvedbo), kliknimo gumb Advanced Options in odstranimo kljukico v kontrolnih poljih Generate Insert, Update in Delete. Kliknimo OK in nato Finish. V okno DataSet Designer se dodata dva nova razreda: nov razred Suppliers tipa DataTable in nov razred SuppliersTableAdapter tipa DataTableAdapter. Končno moramo še povezati gradnik ComboBox z našo podatkovno tabelo Suppliers. Na obrazcu izberimo gradnik ComboBox in oknu Properties kliknimo lastnost DataSource. Prikaže se spustni meni v katerem razširimo Other Data Sources, razširimo Project Data Sources, razširimo �orthwindDataSet in kliknimo Suppliers. Gradnik ComboBox sm s tem povezali s podatkovno tabelo Suppliers in generirali nov objekt tipa BindingSource, ki se imenuje suppliersBindingSource, ter instanco razreda SuppliersTableAdapter. Med tem, ko je gradnik ComboBox še kar izbran, nastavimo njegovo lastnost DisplayMember na Company�ame in lastnost ValueMember na SupplierID. Ko bomo projekt zagnali, bomo v ComboBox-u dobili seznam vseh dobaviteljev, ob izbiri poljubnega dobavitelja iz seznama pa nam bo na voljo vrednost polja SupplierID. Ob izbranem gradniku ComboBox v oknu Properties kliknimo gumb Events in nato dvokliknimo v vrstico SelectedIndexChenged. V telo prikazane metode dopišimo stavek if (supplierList.SelectedValue != null) { //če izbrana vrednost ni prazna, se ID dobavitelja izpiše na labeli supplierID supplierID.Text = supplierList.SelectedValue.ToString(); }

Če sedaj aplikacijo poženemo, se ob izbiri poljubnega dobavitelja na labeli prikaže njegova identifikacijska številka.

Ažuriranje baze podatkov z uporabo objektov DataSet

Podatke iz podatkovne baze smo v prejšnjih primerih in vajah le prikazovali, v tem poglavju pa bomo pokazali, kako podatke v tabele dodajamo, jih ažuriramo ali brišemo. Pred tem pa moramo razmisliti o nekaterih potencialnih problemih, do katerih lahko pride pri takih opravilih in kako le-te odpravimo. Podatkovne baze so namenjene za hkratno delo več uporabnikom, pri čemer je to število v večini primerov omejeno le na določeno število hkratnih povezav. V aplikaciji, ki pridobiva oz. prikazuje podatke iz baze, nikoli ne vemo v naprej, koliko časa se bo nek uporabnik listal po podatkih v neki tabeli, zaradi česar ni dobro, da je povezava z podatkovno bazo odprta samo nek določen čas. Boljši pristop pri delu z bazami je ta, da se uporabnik na bazo priklopi, prenese potrebne podatek v objekt DataSet, nato pa povezavo z bazo prekine. Uporabnik se nato premika po pridobljenih podatkih in naredi poljubne spremembe. Ko so spremembe gotove, se ponovno priključi na bazo in predloži spremembe, ki naj se izvedejo tudi v bazi podatkov. Pri tem seveda lahko nastopijo

Page 249: PROŠNJA - uranic.tsckr.siuranic.tsckr.si/VISUAL C#/VISUAL C#.pdf · MICROSOFT VISUAL C# .NET Srečo Uranič . Visual C# . ... Za začetek novega projekta v okolju Visual C# Standard

Visual C# . NET

Algoritmi in programski jeziki – 4.letnik

249

težave, kot npr. kaj naj se zgodi v primeru, da dva uporabnika zahtevata spremembo istega podatka, nova vrednost pa je pri obeh različna. Kateri podatek bo tem primeru shranjen v bazo? O reševanju takih in podobnih težav bo napisano v nadaljevanju.

Upravljanje s povezavami

V vseh dosedanjih primerih smo videli, da ko definiramo objekt DataSet, lahko določimo tudi povezavo, ki jo uporabimo za povezavo z bazo podatkov. To informacijo je potem uporabil objekt TableAdapter za pridobitev ustreznih podatkov in napolnitev objekta DataSet. Ko želimo izvesti metode Fill in GetData, koda, ki jo je generiral Visual Studio najprej pregleda stanje povezave s podatkovno bazo. Če je potrebna povezava že vzpostavljena, jo uporabi za pridobivanje podatkov, in jo na koncu operacije pusti odprto. V kolikor pa je povezava z bazo zaprta, jo metodi Fill in GetData odpreta, pridobita podatke in povezavo zapreta. V tem primeru se objekt DataSet torej obnaša kot disconnected DataSet in ne vzdržuje še naprej aktivne povezave z bazo. Taki (disconnected DataSet) objekti se torej obnašajo kot t.i. data cache v aplikacijah. Podatke v objektu DataSet lahko poljubno spreminjamo, kasneje pa ponovno odpremo povezavo z bazo in ji pošljemo narejene spremembe. Povezavo z bazo lahko seveda odpremo tudi ročno (programsko) tako, da kreiramo objekt SqlConnection, določimo njegovo lastnost ConnectionString, nato pa pokličemo njegovo metodo Open. Z nastavitvijo lastnosti Connection lahko nato odprto povezavo povežemo z objektom TableAdapter. Naslednji primer prikazuje, kako se priklopimo na bazo in napolnimo objekt SuppliersDataTable. V tem primeru bo ostala povezava z bazo odprta tudi po zaključku metode Fill. SqlConnection dataConnection = new SqlConnection(); dataConnection.ConnectionString = "Integrated Security=true;Initial Catalog=Northwind; "+

"Data Source=ime_Računalnika\\SQLExpress"; dataConnection.Open(); suppliersTableAdapter.Connection = dataConnection; suppliersTableAdapter.Fill(northwindDataSet.Suppliers);

Razen če zato nimamo res dobrega razloga, moramo povezavo s podatkovno bazo prekiniti takoj, ko so podatki pridobljeni. Pustimo metodama Fill in GetData da skrbita za odpiranje in zapiranje povezav.

Obravnava istočasnega posodabljanja podatkov več uporabnikov (multi-user updates)

Pri hkratnem dostopanju, ažuriranju ali posodabljanju podatkov lahko pride do nepredvidenih težav. Za reševanje tega problema sta možna vsaj dva različna pristopa. Vsak od njiju ima svoje prednosti in svoje slabosti. Prvi pristop k reševanju problemov pri hkratnem ažuriranju je tehnika, ki zajema opcijo Use optimistic concurrency option v pogovornem oknu Advanced Options pri uporabi čarovnika TableAdapter Configuration.

Če ta opcija NI izbrana, bodo vrstice podatkov, ki smo jih pridobili v objekt DataSet zaklenjene, saj na ta način drugim uporabnikom preprečimo, da bi med tem te podatke spreminjali. Tak način povezovanja s podatkovno bazo oz. zaklepanja je poznan kot pessimistic concurrency, saj nam zagotavlja, da spremembe, ki jih bomo napravili v podatkih ne bodo prišle v kolizijo s spremembami, ki jih bodo nad istimi podatki napravili drugi

uporabniki. Slaba stran tega načina je seveda ta, da med našim ažuriranjem drugi uporabniki nimajo dostopa do teh podatkov. Če smo v našo aplikacijo pritegnili veliko količino podatkov, ažuriramo pa le njihov manjši del, smo ostalim uporabnikom preprečili možnost ažuriranja celotne količine pridobljenih podatkov, ne le tistih, ki jih potrebujemo. Pa še eno slabo točko ima tak način dela: zaklepanje podatkov zahteva, da povezava, ki smo uporabili za pridobivanje podatkov, ostane ves čas odprta. Če torej uporabimo način pessimistic concurancy, lahko tvegamo kar precejšnje zmanjšanje konkurenčnosti naših povezav. Vendar pa je tak način najbolj enostaven, saj nam ni potrebno pisati nobene kode, ki bi preverjala eventuelna posodabljanja ostalih uporabnikov pred ažuriranjem podatkov.

Page 250: PROŠNJA - uranic.tsckr.siuranic.tsckr.si/VISUAL C#/VISUAL C#.pdf · MICROSOFT VISUAL C# .NET Srečo Uranič . Visual C# . ... Za začetek novega projekta v okolju Visual C# Standard

Visual C# . NET

Algoritmi in programski jeziki – 4.letnik

250

Drug način povezovanja pa se imenuje optimistic concurrency – v oknu Advanced Options v tem primeru izberemo opcijo Use optimistic cuncurrency. Podatki v tem primeru niso zaklenjeni, povezavo z bazo pa lahko zapremo takoj ko pridobimo vse podatke. Slaba stran takega načina pa je, da moramo tem primeru napisati kodo, s katero se prepričamo, ali so mogoče spremembe, ki jih uporabnik naredi v podatkih, v koliziji s tistimi, ki jih je naredil nek drug uporabnik. Pisanje takšne kode pa je lahko precej težavno, težavno pa je tudi razhroščevanje. Objekt TableAdapter, ki ga zgenerira TableAdapter Configuration Wizard, sicer rešuje večji del teh težav, a vseeno moramo biti pripravljeni na obdelavo izjem, do katerih lahko pride pri takšnih konfliktih. Več o reševanju takih izjem bo zapisano na koncu tega poglavja.

Uporaba objekta DataSet v povezavi z gradnikom DataGridView

Med najpogostejšimi gradniki, v katerih prikazujemo vsebino neke tabele iz baze podatkov je gradnik DataGridView. V naslednjem primeru bo mo v tem gradniku prikazali vsebino tabele Employees iz podatkovne baze �orthwind. Kreirajmo nov projekt in ga poimenujmo Zaposleni. Lastnost Text obrazce spremenimo v Seznam zaposlenih v podjetju �orthwind. V projekt dodajmo podatkovni izvor: meni Project, Add �ew Item, izberimo DataSet in ga poimenujmo DS�orthwind, nato pa kliknimo gumb Add. Prikaže se okno DataSetDesigner. Z uporabo okna Toolbox dodajmo v DataSet nov TableAdapter. Odpre se okno TableAdapter Configuration Wizard. V oknu izberimo ustrezno povezavo - ime_računalnika\sqlexpress.�orthwind.dbo. Izbiro povezave potrdimo s klikom na gumb �ext. Prikaže se okno Save the connection string to the application configuration file – konfiguracijo shranimo pod privzetim imenom �orthwindConnectionString in kliknimo na gumb �ext. Odpre se okno Choose a Command Type, v katerem izberemo opcijo Use SQL statements in kliknimo �ext. V oknu Enter a Statement page kliknimo Query Builder. Odpreta se pogovorni okni Query Builder in Add Table, ki nam omogočata oblikovanje SELECT stavkov s pomočjo čarovnika. V oknu Add Table izberimo tabelo Empoyees, kliknimo Add in nato Close. Tabela Empoyees se doda v pogovorno okno Query Builder. Odkljukajmo vrstico All Columns in izbiro potrdimo s klikom na gumb OK. Kliknimo še na gumb Advanced Options in odkljukajmo vse tri možne opcije. S klikom na gumb OK okno zapremo, nato pa še s klikom na gumb Finish zaključimo nastavitve našega TableAdapter-ja. Podatkovni izvor je tako pripravljen, podatke moramo le še prikazati na gradniku DataGridView. Na obrazec zato postavilo gradnik DataGridView. Sedaj je potrebna le še povezava med našim podatkovnim izvorom in gradnikom DataGridView. Le-to pa lahko naredimo na dva načina: s pomočjo lastnosti DataSet gradnika DataGridView, ali pa čisto programsko. Pokažimo oba načina: Povezava gradnika DataGridView s podatkovnim izvorom preko lastnosti DataSet Kliknimo na trikotniček v desnem zgornjem kotu gradnika DataGridView. Razširimo OtherDataSource,

razširimo ProjectDataSources, razširimo DS�orthwind in zberimo Employees. Če sedaj projekt poženemo, bo vsebina gradnika TableGridView napolnjena s podatki o zaposlenih v podjetju �orthwind.

Povezava gradnika DataGridView s podatkovnim izvorom preko kode - programsko Preklopimo na pogled obrazca View Code in najprej deklariramo novo instanco našega podatkovnega izvora: private DSNorthwind dSNorthwind = new DSNorthwind();//Nov objekt za naš podatkovni izvor

Page 251: PROŠNJA - uranic.tsckr.siuranic.tsckr.si/VISUAL C#/VISUAL C#.pdf · MICROSOFT VISUAL C# .NET Srečo Uranič . Visual C# . ... Za začetek novega projekta v okolju Visual C# Standard

Visual C# . NET

Algoritmi in programski jeziki – 4.letnik

251

Povezavo s podatkovnim izvorom bomo zapisali v dogodek Load našega obrazca private void Form1_Load(object sender, EventArgs e) { //Napovemo novo instanco objekta DSNorthwindTableAdapters.EmployeesTa z imenom zaposleniTA DSNorthwindTableAdapters.EmployeesTableAdapter zaposleniTA; //Nov objekt tudi dejansko ustvarimo zaposleniTA = new DSNorthwindTableAdapters.EmployeesTableAdapter(); /*objekt TableAdapter z imenom zaposleniTA napolnimo s podatki iz podatkovne tabele. Ko se bo objekt EmployeesTableAdapter napolnil s podatki, bo povezava z bazo avtomatsko prekinjena, saj do tega trenutka ni bilo vzpostavljene nobene povezave!*/ zaposleniTA.Fill(this.dSNorthwind.Employees); //ustvarimo nov objekt tipa BindingSource za prenos podatkov iz TableAdapterja BindingSource employeesBS = new BindingSource(this.dSNorthwind, "Employees"); //povežemo lastnost DataSource gradnika DataGridView z objektom employeesBS dataGridView1.DataSource = employeesBS; }

Ko aplikacijo poženemo, se vsebina gradnika DataGridView napolni s podatki o zaposlenih v podjetju �orthWind. Podatke, ki so prikazani v posameznih stolpcih lahko urejamo po velikosti oz. po abecednem redu (klikamo v ustrezno polje v naslovni vrstici podatkov). Pri prvem kliku so podatki urejeni v naraščajočem vrstnem redu, pri drugem kliku pa v padajočem.

Če kliknemo v katerokoli celico gradnika DataGridView (razen v celico EmployeesID) lahko preko tipkovnice v polje vnesemo novo vsebino. V primeru, da v celico, ki sicer vsebuje numerične podatke, vnesemo nek alfanumeričen string, dobimo pri poskusu premika na drugo celico obvestilo o napaki. Okno z obvestilom zaprimo in popravimo vsebino celice (ali pa pritisnimo tipko Escape za preklic vnosa). Obvestilo o napaki je privzeto, lahko pa ga nadomestimo s poljubnim svojim obvestilom o napaki – to storimo tako, da napišemo svoj DataError dogodek, ki je sestavni del dogodkov gradnika DataGridView, npr.: private void dataGridView1_DataError(object sender, DataGridViewDataErrorEventArgs e) { MessageBox.Show("Napačen vnos podatkov!"); }

Po podatkih v tabeli se lahko premikamo s pomočjo horizontalnega in vertikalnega drsnika, ki se pojavita na robovih gradnika DataGridView. Če se premaknemo za nekaj polj bolj v desno, bomo v eni od celic opazili tudi sliko, ki predstavlja fotografijo zaposlenega. V posamezni celici gradnika DataGridView lahko napreč prikažemo prav vsak tip podatka. Premaknimo se na dno tabele, torej na konec podatkov. Pojavi se prazna vrstica, na njenem začetku pa se pojavi zvezdica. V prazno vrstico lahko dodamo nove podatke, pri čemer se nova številka zaposlenega (EmployeesID) generira avtomatično, saj gre za ključ v tabeli Empoyees. Če kliknemo v sivo polje na levi strani katerekoli vrstice, se obarva celotna vrstica. Ob pritisku na gumb Delete se celotna vrstica pobriše. Ko z delom v gradniku DataGridView končamo, obrazec zaprimo. Spremembe, ki smo jih v tabeli naredili se ne bodo shranile, saj za potrebe shranjevanja podatkov še nismo napisali ustrezne kode. Gradnik DataGridView vsebuje cel kup lastnosti, s pomočjo katerih oblikujemo vizuelni izgled gradnika in prikazanih podatkov. Med lastnostmi ima še poseben pomen zelo uporabna lastnost Columns, ki nam omogoča nastavljanje velikega števila podlastnosti tega gradnika: izgled, velikost celic, barve, izgled naslovne vrstice in celo nastavljanje izgleda posamezne celice (kot tekstovno polje, kot navaden gumb, kot ComboBox, …).

Page 252: PROŠNJA - uranic.tsckr.siuranic.tsckr.si/VISUAL C#/VISUAL C#.pdf · MICROSOFT VISUAL C# .NET Srečo Uranič . Visual C# . ... Za začetek novega projekta v okolju Visual C# Standard

Visual C# . NET

Algoritmi in programski jeziki – 4.letnik

252

V gradniku DataGridView nastavimo lastnosti tako, da bomo v zagnanem projektu lahko polje TitleOfCourtesy spremenili/ažurirali s pomočjo spustnega seznama, polje BirthDate pa z izbiro datuma na koledarčku. Spustni seznam za izbiro nadimka pripravimo takole:

• na obrazcu izberimo gradnik DataGridView (ime gradnika je dataGridView1)

• v oknu Properties kliknimo Columns • izberimo polje TitleOfCourtesy • Lastnost ColumnType nastavimo na

DataGridViewComboBoxColumn • Lastnost HeaderText nastavimo na �adimek

– spremenili smo napis na vrhu stolpca • Kliknimo lastnost Items in v okno vnesimo

vrstice, ki so na sliki

Ko sedaj projekt poženemo, lahko vsebino stolpca �adimek (ki dejansko pomeni vsebino polja TitleOfCourtesy) spremenimo le s pomočjo spustnega seznama, ki se prikaže pod to celico. Poglejmo še, kako lahko spremenimo vsebino datumskega polja s pomočjo koledarčka. V ta namen pripravimo nov obrazec in ga poimenujmo FKoledar. Na obrazec FKoledar nato postavimo gradnik MonthCalendar (ime gradnika naj ostane kar monthCalendar1). Ker želimo imeti do tega gradnika dostop iz prejšnjega obrazca, moramo ta gradnik narediti javno dostopen (v oknu Solution Explorer kliknimo na znak + pred obrazcem FKoledar in nato izberimo okno FKoledar.Designer.cs. Na dnu tega okna popravimo označevalec private za ta gradnik v public public System.Windows.Forms.MonthCalendar monthCalendar1;

Na obrazec FKoledar postavimo še gumb z napisom OK (lastnost DialogResult nastavimo na OK) in gumb z napisom Prekliči (DialogResult nastavimo na Cancel) in vse skupaj shranimo. Preklopimo ponovno na obrazec, na katerem imamo gradnik DataGridView in ta gradnik tudi izberimo. V oknu Properties preklopimo na seznam dogodkov, dvokliknimo v dogodek CellClick in v telo tega dogodka zapišimo naslednjo kodo: //če kliknemo v stolpec BirthDate, se prikaže koledarček if (dataGridView1.Columns[e.ColumnIndex].DataPropertyName == "BirthDate") { //nova instanca obrazca FKoledar, ki vsebuje gradnik MonthCalendar FKoledar koledar=new FKoledar(); //datum na obrazcu nastavimo glede na datum v izbrani celici koledar.monthCalendar1.SetDate((DateTime)dataGridView1[e.ColumnIndex, e.RowIndex].Value); //obrazec FKoledar odpremo modalno if (koledar.ShowDialog() == DialogResult.OK) //če uporabnik klikne gumb OK { //datum v celici popravimo glede na izbrani datum v koledarčku dataGridView1[e.ColumnIndex, e.RowIndex].Value = koledar.monthCalendar1.SelectionEnd; }

Page 253: PROŠNJA - uranic.tsckr.siuranic.tsckr.si/VISUAL C#/VISUAL C#.pdf · MICROSOFT VISUAL C# .NET Srečo Uranič . Visual C# . ... Za začetek novega projekta v okolju Visual C# Standard

Visual C# . NET

Algoritmi in programski jeziki – 4.letnik

253

}

Ob kliku v celico BirthDate se bo odprl obrazec s koledarčkom. Na koledarček bo prenesen datum iz celice, ob izbiri novega datuma na koledarčku in kliku na gumb OK, pa se bo v celico prenesel novi datum. Vaja: Vaja prikazuje, kako lahko podatke iz neke tabele v podatkovni bazi prikažemo v gradniku DataGridView izključno s pisanjem kode. Kreirajmo nov projekt in na obrazec Form1 postavimo dva gradnika DataGridView in ju poimenujmo DataGridViewArtikli in DataGridViewStranke. Preklopimo na Code View in nato kodo v Form1 dopolnimo takole: using System.Data.SqlClient; //Ker bo povezava programska, ne pozabimo na using stavek

//Deklarirajmo nov objekt SQLConnection za povezavo z SQL bazo SqlConnection dataConnection = new SqlConnection(); //Napoved adapterjev za posamezno tabelo iz baze SqlDataAdapter DAArtikli,DAStranke; //Napoved objekta dataset, ki bo vseboval podatke iz baze Northwind private DataSet dsNorthwind; public Form1() { InitializeComponent(); try { string izvor="Integrated Security=true;Initial Catalog=Northwind;Data Source = "+ "imeRacunalnika\\SQLExpress"; string SQL="SELECT * FROM Products"; dataConnection=new SqlConnection(izvor); // povezava z bazo DAArtikli = new SqlDataAdapter(SQL,dataConnection); DAStranke = new SqlDataAdapter(SQL, dataConnection); dsNorthwind = new DataSet(); //s pomočjo metode Fill adapterja DAArtikli potegnemo v dsArtikli prvo tebelo DAArtikli.Fill(dsNorthwind, "Products"); SQL = "SELECT * FROM Customers"; DAStranke = new SqlDataAdapter(SQL, dataConnection); //s pomočjo metode Fill adapterja DAArtikli potegnemo v dsArtikli še drugo tebelo DAStranke.Fill(dsNorthwind, "Customers"); //V gradniku tableGridViewArtikli prikažemo prvo tabelo iz našega dsArtikli –indeks 0! dataGridViewArtikli.DataSource = dsNorthwind.Tables[0]; //V gradniku tableGridViewStranke prikažemo drugo tabelo iz našega dsArtikli–indeks 1! dataGridViewStranke.DataSource = dsNorthwind.Tables[1]; } catch { MessageBox.Show("Napaka pri dostopu do baze podatkov!"); } }

Ko projekt poženemo, bomo v gradnikih DataGridView zagledali podatke iz tabele Products in iz tabele Customers. Na obrazec postavimo gumb. Ob kliku na gumb želimo obdelati tabelo Products tako, da ugotovimo in na koncu izpišemo skupno število izdelkov na zalogi. Ime polja, ki vsebuje podatke o številu posameznih izdelkov je UnitsInStock, zaporedna številka polja v tabeli pa je 6. private void button1_Click(object sender, EventArgs e) { try { string izvor = "Integrated Security=true;Initial Catalog=Northwind;Data Source ="+ "imeRacunalnika\\SQLExpress"; string SQL = "SELECT * FROM Products"; dataConnection.Open();

Page 254: PROŠNJA - uranic.tsckr.siuranic.tsckr.si/VISUAL C#/VISUAL C#.pdf · MICROSOFT VISUAL C# .NET Srečo Uranič . Visual C# . ... Za začetek novega projekta v okolju Visual C# Standard

Visual C# . NET

Algoritmi in programski jeziki – 4.letnik

254

SqlCommand dataCommand = new SqlCommand(); dataCommand.Connection = dataConnection; //kreiramo poizvedbo dataCommand.CommandText = "SELECT * FROM Products"; SqlDataReader dataReader = dataCommand.ExecuteReader(); int skupaj = 0; //ugotovimo skupno število artiklov na zalogi while (dataReader.Read())//dokler obstajajo podatki { try { //število komadov je v polju z indeksom 6. Metoda GetValue vrne tip Object object komadov = dataReader.GetValue(6); skupaj = skupaj + Convert.ToInt32(komadov); } catch { } } //rezultat prikažemo v sporoči oknu MessageBox.Show("Skupaj na zalogi: " + skupaj.ToString()); dataReader.Close();//zapremo podatkovni tok dataConnection.Close();//Obvezno zapremo povezavoz bazo } catch { MessageBox.Show("Napaka pri dostopu do baze podatkov!"); }

Kontrola uporabnikovih vnosov v gradnik DataGridView Pred shranjevanjem podatkov nazaj v bazo podatkov, se moramo prepričati, da so spremembe, ki jih je napravil uporabnik, veljavne oz. pravilne. V celice gradnika DataGridView lahko namreč uporabnik vtipka karkoli, zato moramo s programsko kodo poskrbeti, da bodo vnosi smiselni in se tako zavarovati pred napakami pri shranjevanju podatkov v bazo. Za prikaz nekaterih kontrol uporabnikovih vnosov bomo zopet uporabili bazo �orthwind. Kreirajmo nov projekt in ga imenujmo Produkti. V projekt dodajmo podatkovni izvor: meni Project, Add �ew Item, izberimo DataSet in ga poimenujmo DS�orthwind, nato pa kliknimo gumb Add. Prikaže se okno DataSetDesigner. Z uporabo okna Toolbox dodajmo v DataSet nov TableAdapter. Odpre se okno TableAdapter Configuration Wizard. V oknu izberimo ustrezno povezavo - ime_računalnika\sqlexpress.�orthwind.dbo. Izbiro povezave potrdimo s klikom na gumb �ext. Prikaže se okno Save the connection string to the application configuration file – konfiguracijo shranimo pod privzetim imenom �orthwindConnectionString in kliknimo na gumb �ext. Odpre se okno Choose a Command Type, v katerem izberemo opcijo Use SQL statements in kliknimo �ext. V oknu Enter a Statement page kliknimo Query Builder. Odpreta se pogovorni okni Query Builder in Add Table, ki nam omogočata oblikovanje SELECT stavkov s pomočjo čarovnika. V oknu Add Table izberimo tabelo Products, kliknimo Add in nato Close. Tabela Products se doda v pogovorno okno Query Builder. Odkljukajmo vrstico All Columns in izbiro potrdimo s klikom na gumb OK. Kliknimo še na gumb Advanced Options in odkljukajmo vse tri možne opcije. S klikom na gumb OK okno zapremo, nato pa še s klikom na gumb Finish zaključimo nastavitve našega TableAdapter-ja. Na enak način dodajmo v naš DataSet še tabelo Supplier. Podatkovni izvor je tako pripravljen, podatke moramo le še prikazati na gradniku DataGridView. Na obrazec zato postavilo gradnik DataGridView in ga poimenujmo productsGrid. Kliknimo na trikotniček v desnem zgornjem kotu gradnika DataGridView. Razširimo OtherDataSource, razširimo ProjectDataSources, razširimo DS�orthwind in zberimo Products. Če sedaj projekt poženemo, bo vsebina gradnika TableGridView napolnjena s podatki iz tabele Products. Vrnimo se v Design View našega obrazca in izberimo gradnik DataGridView. V oknu Properties kliknimo lastnost Columns in izberimo najprej polje ProductID. Prepričajmo se, da je lastnost ReadOnly v Bound Column Properties nastavljena na true. Vrednosti tega stolpca se namreč generira avtomatično, zato uporabniku ne smemo dopustiti, da ga spreminjal sam.

Page 255: PROŠNJA - uranic.tsckr.siuranic.tsckr.si/VISUAL C#/VISUAL C#.pdf · MICROSOFT VISUAL C# .NET Srečo Uranič . Visual C# . ... Za začetek novega projekta v okolju Visual C# Standard

Visual C# . NET

Algoritmi in programski jeziki – 4.letnik

255

Izberimo sedaj polje SupplierID in v Bound Column Properties nastavimo lastnosti tako, kot kaže slika. Najprej nastavimo lastnost ColumnType na supplierIDDataGridViewColumns, šele nato pa lastnost DisplayStyle (ta lastnost je na začetku nevidna). Nastavimo še DataSource na suppliersBindingSource, DisplayMember na Company�ame in ValueMember na SupplierID. Okno zarimo s klikom na OK in ponovno poženimo projekt. V stolpcu SupplierID so sedaj gradniki ComboBox. Če kateregakoli od njih odpremo dobimo spustni seznam vseh dobaviteljev – izberemo lahko kateregakoli in s tem zamenjamo prejšnjega. Na ta način smo dosegli, da se uporabnik pri vnosu dobavitelja nikakor ne more zmotiti in vnesti ID dobavitelja, ki ne obstaja.

Na istem primeru pokažimo še obdelavo nekaterih drugih dogodkov, s katerimi lahko kontroliramo uporabnikove vnose in se tem zavarujemo pred napačnimi vnosi podatkov. Ti dogodki so CellValidating, CellEndEdir in DataError. Izberimo gradnik DataGridView imenovan productsGrid in v oknu Properties kliknimo gumb Events. Dvokliknimo v dogodek CellValidating in Visual C# nam zgenerira ogrodje te odzivne metode. Zapišimo naslednjo kodo: int zacasna; productsGrid.Rows[e.RowIndex].ErrorText = ""; if ((productsGrid.Columns[e.ColumnIndex].DataPropertyName == "UnitsInStock") || (productsGrid.Columns[e.ColumnIndex].DataPropertyName == "UnitsOnOrder") || (productsGrid.Columns[e.ColumnIndex].DataPropertyName == "ReorderLevel")) { if (!int.TryParse(e.FormattedValue.ToString(), out zacasna) || zacasna < 0) { productsGrid.Rows[e.RowIndex].ErrorText = "Vrednost mora biti nenegativno število"; e.Cancel = true; } }

Namen te metode je zagotoviti, da bo uporabnik v polja UnitsInStock, UnitsOnOrder in ReorderLevel zagotovo vnesel nenegativna cela števila. Drugi parameter te metode, parameter e, je objekt tipa DataGridViewCellValidatingEventArgs. Vsebuje številne lastnosti, ki jih lahko uporabimo npr. za ugotavljanje, katera celica je bila ažurirana: e.ColumnIndex vsebuje številko stolpca, e.RowIndex pa številko vrstice (prva vrstica in prvi stolpec imata indeks enak 0). V prvem if stavku zgornje kode preverjamo, če smo v enem od treh celoštevilskih stolpcev/polj. Upoštevati moramo, da razred DataGridView vsebuje zbirko Columns, ki hrani informacije o vsakem prikazanem stolpcu. Vrednost e.ColumnIndex je uporabljena kot indeks elementa iz te zbirke, vrednost lastnosti DataProperty�ame pa smo uporabili za identifikacijo ustreznega stolpca. Metoda int.TryParse je zelo koristna v primerih ugotavljanja ali nek string vsebuje vrednost, ki se lahko konvertira v celo število. Metoda vrne true, če je pretvarjanje uspešno, obenem pa v svoj drugi parameter ( v

Page 256: PROŠNJA - uranic.tsckr.siuranic.tsckr.si/VISUAL C#/VISUAL C#.pdf · MICROSOFT VISUAL C# .NET Srečo Uranič . Visual C# . ... Za začetek novega projekta v okolju Visual C# Standard

Visual C# . NET

Algoritmi in programski jeziki – 4.letnik

256

našem primeru zacasna), ki je klican po referenci zapiše pretvorjeno vrednost. Drugi if stavek v zgornji kodi torej s pomočjo metode try.Parse preveri, ali je uporabnik v izbrano celico vnesel celoštevilsko vrednost. Če temu ni tako (uporabnik je vnesel npr. string, ki vsebuje črke, ali pa je vnesena vrednost negativna), se v lastnost ErrorText trenutne vrstice v tabeli zapiše ustrezno obvestilo o napaki. Na začetku te vrstice se v tem primeru prikaže ikona s klicajem in če na ikono postavimo kazalnik miške, se nam pod njo izpiše obvestilo o napaki ( v našem primeru tekst "Vrednost mora biti nenegativno število"). Z naslednjim stavkom (e.Cancel = true;) pa poskrbimo, da se uporabnik nikakor ne more premakniti v drugo celico vse dotlej, dokler v celico ne vnese pravilnega podatka, ali pa celoten vnos ne prekliče s tipko Esc. Za gradnik DataGridView zapišimo še dogodek CellEndEdit. Koda je naslednja: productsGrid.Rows[e.RowIndex].ErrorText = "";

Ta metoda se izvede, ko je uporabnikov vnos veljaven in se uporabnik premakne v drugo celico. S tem stavkom enostavno pobrišemo vsa obvestila o napaki zaradi nepravilnega vnosa podatkov v trenutno izbrano celico. Dvokliknimo še dogodek DataError gradnika DataGridView in v ogrodje prikazane metode zapišimo stavka: productsGrid.Rows[e.RowIndex].ErrorText="Napačen vnos. Poskusite ponovno!"; e.Cancel = true;

Dogodek DataError ujame prav vse napake, ki nastanejo pri preverjanju uporabnikovega vnosa v katerokoli celico (predvsem velja to za celice, katerih vneseno vsebino nismo preverjali preko imena celice, tako kot v zgornjem primeru). Vsebina e metode poskrbi za splošno obvestilo uporabniku, obenem pa prepreči, da bi se uporabnik kljub napačnemu vnosu premaknil iz trenutne celice. Poženimo sedaj naš projekt in poizkusimo v poljubne celice vnesti poljubno vsebino. Če vneseni tekst ne ustreza celici, katere vsebino vnašamo, bomo dobili obvestilo o napaki. Iz take celice se lahko premaknemo le če vnesemo pravilno vsebino, ali pa če vnos prekličemo s tipko Esc.

Ažuriranje uporabnikovih vnosov z uporabo objekta DataSet

Ko smo se prepričali, da so naši spremenjeni oz. dodani podatki res veljavni oz. pravilni, jih poskušamo shraniti nazaj v bazo. Spremembe podatkov, narejene z uporabo gradnika DataGridView , se avtomatično skopirajo nazaj v objekt DataSet, ki igra vlogo podatkovnega izvora. Shranjevanje spremenjenih oz. dodanih podatkov vključuje ponoven priklop na bazo, izvedbo ustreznih SQL INSERT, UPDATE in DELETE stavkov, in nato odklop od baze. Pri teh operacijah lahko pride do raznih napak, na obdelavo katerih pa moramo biti pripravljeni. Razred TableAdapter, ki smo ga ustvarili za naš DataSet vsebuje kar nekaj metod, ki nam pri tem zelo pomagajo. Tipične postopke, potrebne pri shranjevanju podatkov nazaj v bazo bomo prikazali kar na konkretnem primeru iz prejšnjega projekta Produkti. Na glavni obrazec postavimo gumb in mu spremenimo lastnosti Text v Shrani in lastnost �ame v shrani. Nato dvokliknimo na ta gumb in Visual C# nam zgenerira ogrodje metode shrani_Click. V telo te metode zapišimo varovalni blok z naslednjo kodo: try { NorthwindDataSet spremembe = (NorthwindDataSet)northwindDataSet.GetChanges(); if (spremembe == null) { return; } //Kontrola napak //Če napak ni, ažuriramo bazo, sicer pa obvestimo uporabnika o napakah } catch (Exception ex) {

Page 257: PROŠNJA - uranic.tsckr.siuranic.tsckr.si/VISUAL C#/VISUAL C#.pdf · MICROSOFT VISUAL C# .NET Srečo Uranič . Visual C# . ... Za začetek novega projekta v okolju Visual C# Standard

Visual C# . NET

Algoritmi in programski jeziki – 4.letnik

257

MessageBox.Show("Napaka: " + ex.Message, "Napake", MessageBoxButtons.OK, MessageBoxIcon.Error); northwindDataSet.RejectChanges(); }

Na začetku smo za ustvarjanje novega objekta razreda �orthwindDataSet (objektu smo dali ime spremembe) uporabili metodo GetChanges(). Ta metoda v objekt spremembe zapiše le tiste vrstice, ki so bile spremenjene. Eksplicitna konverzija (NorthwindDataSet)northwindDataSet.GetChanges() je nujno potrebna, ker metoda GetChanges vrača objekt tipa DataSet. Metode GetChanges nam sicer ne bilo potrebno uporabiti, je pa shranjevanje nazaj v bazo na ta način hitrejše, saj metodam za shranjevanje v tem primeru ni potrebno preverjati za vsako vrstico posebej, ali je bila le-ta spremenjena. Če ni bilo v gradniki DataGridView narejene nobene spremembe, potem ni nobene spremembe tudi v objektu spremembe – ta dobi vrednost null in metoda se zaključi. Če pa so spremembe bile, se izvede kontrola eventuelnih napak v podatkih, nakar se baza ažurira (ta del kode sledi v nadaljevanju). Če pride do napake med ažuriranjem baze, se bo uporabniku prikazalo sporočilno okno, vse dosedanje spremembe v bazi podatkov pa bodo zaradi uporabe metode RejectChanges preklicane. Dodajmo sedaj še kodo za kontrolo napak, s katero v zgornji kodi nadomestimo vrstico //Kontrola napak. DataTable dt = spremembe.Tables["Products"]; DataRow[] NapacneVrstice = dt.GetErrors();

V prvem stavku smo v objekt dt tipa DataTable zapisali vse spremembe, ki so bile shranjene v objektu spremembe. Metoda GetErrors vrne tabelo vseh vrstic iz te tabele, v katerih obstaja ena ali več napak v veljavnosti vnesenih podatkov. V primeru, da ni nobene napake, metoda GetErrors vrne prazno tabelo. Vrstico //Če napak ni, ažuriramo bazo, sicer pa obvestimo uporabnika o napakah sedaj nadomestimo s stavki: if (NapacneVrstice.Length==0) { //Ažuriranje baze } else { //Poiščemo napake in obvestimo uporabnika }

Obstaja kar nekaj strategij za obveščanje uporabnika o napakah. Ena izmed najbolj uporabnih je ta, da napišemo kodo, ki zgenerira seznam prav vseh napak in le-te izpiše uporabniku v nekem sporočilnem oknu. Nadomestimo kodo //Poiščemo napake in obvestimo uporabnika z naslednjimi stavki: string errorMsg=null; foreach (DataRow vrstica in NapacneVrstice) { foreach (DataColumn stolpec in vrstica.GetColumnsInError()) { errorMsg+=vrstica.GetColumnError(stolpec)+"\n"; } } MessageBox.Show("Napake v podatkih: "+errorMsg,"Prosim popravite podatke!",MessageBoxButtons.OK,MessageBoxIcon.Error);

Ta koda pregleda vse vrstice v tabeli �apacneVrstice. V vsaki od teh vrstic je zato po ena ali več napak. Metoda GetColumnsInError vrne zbirko, ki vsebuje vse stolpce z napačnimi podatki, metoda GetColumnError pa vrne obvestilo o napaki za posamezen stolpec. Vsako obvestilo o napaki je zatem dodano v string errorMsg. Ko so pregledane vse vrstice in preverjeni vsi stolpci, aplikacija v sporočilnem oknu izpiše skupno obvestilo o vseh napakah hkrati. Uporabnik naj bi s pomočjo te informacije popravil napačne vnose in nato poskusil s ponovnim shranjevanjem. Če pa so vsi vneseni podatki v redu, jih končno lahko pošljemo v bazo podatkov. Kodo //Ažuriranje baze nadomestimo s stavki: int steviloVrstic = productsTableAdapter.Update(spremembe); MessageBox.Show("Ažuriranih vrstic " + steviloVrstic, "Shranjevanje uspešno"); northwindDataSet.AcceptChanges();

Page 258: PROŠNJA - uranic.tsckr.siuranic.tsckr.si/VISUAL C#/VISUAL C#.pdf · MICROSOFT VISUAL C# .NET Srečo Uranič . Visual C# . ... Za začetek novega projekta v okolju Visual C# Standard

Visual C# . NET

Algoritmi in programski jeziki – 4.letnik

258

S pomočjo metode Update našega productTableAdapter-ja so spremenjeni podatki poslani v bazo podatkov. Ko so spremembe v bazi dejansko izvedene, smo v sporočilnem oknu uporabnika obvestili o številu izvedenih sprememb in metoda AcceptChanges te spremembe označi kot dejansko opravljene tudi v objektu DataSet.

� POZOR: V primeru, da je nek drug uporabnik spremenil vrstice, v katerih smo naredili spremembe tudi mi, bo metoda Update te spremembe zaznala, in dobili bomo ustrezno obvestilo o napaki. Razvijalec aplikacije se mora v takem primeru odločiti, kako bo obdelal to situacijo v svoji aplikaciji: uporabniku bo npr. ponudil možnost, da podatke kljub vsemu shrani in s tem prekrije spremembe, ki jih je napravil nek drug uporabnik, ali pa da prekliče svoje spremembe in osveži podatke v objektu DataSet z novim stanjem v bazi podatkov.

Naša aplikacija je sedaj pripravljena tudi za dodajanje novih podatkov v bazo, oz. za ažuriranje in brisanje le-teh. Pravila o integriteti v objektih DataSet V prejšnjem primeru, ki uporablja en sam objekt DataSet, je malo verjetno, da nam bo Metoda GetErrors pri shranjevanju vrnila kakršnokoli napako. Ko smo generirali objekt northwindDataSet, je ta že vseboval informacijo o primarnih ključih, tipih podatkov za posamezne stolpce, pravila integritete in vse ostale informacije pridobljene iz baze podatkov. Uporabnik ažurira podatke oz. dela spremembe običajno preko objektov, kot je npr. DataGridView, ti objekti pa imajo zopet svoje mehanizme za kontroliranje uporabnikovih vnosov – nekatere izmed njih smo spoznali v prejšnjem poglavju. Kljub temu, da je bila izvedena kontrola podatkov že pri vnosu v celice gradnika DataGridView, pa je ponovna kontrola pred dokončnim zapisovanjem v bazo prav tako nujno potrebna. Le tako se bomo izognili vsem možnim napakam. V primeru, da DataSet vsebuje večje število tabel, ki imajo primarne ključe oz. so med njimi relacijske povezave, bi bilo zelo moteče, če bi bil uporabnik prisiljen vnašati podatke v nekem posebnem zaporedju. Do take situacije pride npr., kadar metoda GetErrors nastopa samostojno. Metoda v tem primeru predvideva, da je uporabnik prenehal z vnašanjem podatkov in da lahko prične s kompleksnim navskrižnim preverjanjem med podatkovnimi tabelami in lovi vse možne napake. Razvijalec aplikacij ne more nikoli zagotovo vedeti, kdaj bodo uporabniki vnesli nek neobičajen podatek, ki ga razvijalec sploh ni predvidel. Načelo dobre prakse je, da naj bo napisana koda defenzivna, kar pomeni, da mora biti napisana tako, da bo ujela in obdelala vse napake, kjerkoli se že pojavijo. Vaja: Naslednja vaja Artikli prikazuje malo bolj kompleksen projekt z več obrazci, namenjenimi za delo s podatkovnimi tabelami. Glavni obrazec vsebuje meni s tremi opcijami: Šifranti, Artikli in Konec. Projekt bo namenjen delu s podatkovno bazo nabavaSQL (baza se nahaja na http://uranic.tsckr.si/VISUAL%20C%23/). Baza vsebuje tri tabele: Artikli, Skupine_Artiklov in Dobavitelji.

V projekt vključimo gradnik DataSet (Project -> Add �ew Item -> DataSet) in ga poimenujmo DSArtikli. V DSArtikli nato dojamo tri gradnike Table Adapter za vsako tabelo iz baze nabavaSQL posebej. Pri kreiranju adapterjev upoštevajmo dejstvo, da bomo vse tri tabele tudi ažurirali in dodajali nove zapise.

Page 259: PROŠNJA - uranic.tsckr.siuranic.tsckr.si/VISUAL C#/VISUAL C#.pdf · MICROSOFT VISUAL C# .NET Srečo Uranič . Visual C# . ... Za začetek novega projekta v okolju Visual C# Standard

Visual C# . NET

Algoritmi in programski jeziki – 4.letnik

259

Opcija Šifranti vsebuje podopcijo Skupine Artiklov. Njen namen je delo s podatkovno tabelo Skupine_Artiklov. V ta namen oblikujmo nov obrazec z imenom FSkupine. Obrazec odpremo iz glavnega menija s stavki FSkupine SkupineArtiklov = new FSkupine();//Nov objekt razreda (obrazca) FSkupine SkupineArtiklov.ShowDialog();//NNov obrazec odpremo z metod ShowDialog

Obrzec FSkupine moramo sedaj še kreirati. Odprimo prazen obrazec (Project -> Add Windows Form) in ga shranimo pod imenom FSkupine. Na nov obrazec nato dodajmo še gradnik DataGridView (ime gradnika pustimo kar dataGridView1) in mu nastavimo lastnost DataSource na DSArtikli -> Skupine_Artiklov. Za lepši izgled razširimo še lastnost ColumnHeadersDefaultCallStyle in nastavimo lastnost Font na Bold in po želji še

lastnost BackColor. Za boljši pregled vrstic nastavimo še različni prikaz sodih in lihih vrstic: razširimo lastnost AlternatingRowsDefaultCellStyle, nato pa izberimo ustrezno barvo za lastnost BackColor. Lastnost MultiSelect za naše potrebe nastavimo na False. Gradnik DataGridView z imenom dataGridView1 v fazi načrtovanja

Izgled obrazca v zagnanem projektu je takle:

Na obrazcu so še trije gumbi, s pomočjo katerih bomo izvajali manipulacije z bazo podatkov. Ker je podatek o šifri skupine ključni podatek (ključ v tabeli Skupina_Artiklov), tega podatka seveda ne moremo spremeniti. Podatek v stolpcu Skupina pa seveda lahko spremenimo. Z miško kliknemo v ustrezno celico in s pomočjo tipkovnice spremenimo vsebino vrstice. Če se premaknemo v naslednjo celico, so spremembe v celici shranjene, niso pa še ažurirane v bazi podatkov in to ne glede na to, v koliko vrsticah smo naredili spremembe. Kodo za to moramo napisati posebej, v našem primeru pa to kodo zapišimo v dogodek Click gumba Shrani in zapri okno. Koda je podobna tisti iz prejšnjega poglavja, kjer so posamezni stavki tudi razloženi:

private void bShrani_Click(object sender, EventArgs e) { try { DSArtikli spremembe = (DSArtikli)dSArtikli.GetChanges();

Page 260: PROŠNJA - uranic.tsckr.siuranic.tsckr.si/VISUAL C#/VISUAL C#.pdf · MICROSOFT VISUAL C# .NET Srečo Uranič . Visual C# . ... Za začetek novega projekta v okolju Visual C# Standard

Visual C# . NET

Algoritmi in programski jeziki – 4.letnik

260

if (spremembe == null) { Close(); return; } DataTable dt = spremembe.Tables["Skupine_artiklov"]; DataRow[] NapacneVrstice = dt.GetErrors(); if (NapacneVrstice.Length == 0) { int steviloVrstic = skupine_artiklovTableAdapter.Update(spremembe); dSArtikli.AcceptChanges(); Close();//Po uspešnem shranjevanju v bazo zapremo obrazec } else { string errorMsg=null; foreach (DataRow vrstica in NapacneVrstice) { foreach (DataColumn stolpec in vrstica.GetColumnsInError()) { errorMsg+=vrstica.GetColumnError(stolpec)+"\n"; } } MessageBox.Show("Napake v podatkih: "+errorMsg,"Preverite vnos in popravite Vnesene podatke!",MessageBoxButtons.OK,MessageBoxIcon.Error); } } catch (Exception ex) { MessageBox.Show("Napaka: " + ex.Message, "Napake", MessageBoxButtons.OK, MessageBoxIcon.Error); dSArtikli.RejectChanges(); } }

Spremembe lahko tudi prekličemo (seveda pred še pred klikom na gumb za dokončno shranjevanje v bazo). Preklicu je namenjen gumb Preklic sprememb, kateremu v dogodek Click zapišimo naslednjo kodo: private void bPreklic_Click(object sender, EventArgs e) { /*Preklic vseh sprememb – v gradniku dataGridView1 bo vzpostavljeno stanje po zadnjem shranjevanju*/ dSArtikli.RejectChanges(); }

Na obrazcu SkupineArtiklov je še gumb za brisanje vrstice. Koda za brisanje je sicer enostavna, a v našem primeru se moramo naprej prepričati, ali v tabeli Artikli mogoče obstaja artikel tiste skupine, ki jo želimo pobrisati. V tem primeru moramo seveda brisanje preprečiti. private void bBrisi_Click(object sender, EventArgs e) { try { //pred brisanjem preverimo, če v tabeli Artikli obstaja kak artikel iz te skupine SqlCommand dataCommand = new SqlCommand(); DSArtikliTableAdapters.ArtikliTableAdapter art; Art = new DSArtikliTableAdapters.ArtikliTableAdapter(); dataCommand.Connection = art.Connection; dataCommand.Connection.Open(); //kreiramo poizvedbo int stvrstice = dataGridView1.CurrentCell.RowIndex; //iz izbrane vrstice potegnemo vrednost skupine artiklov, ki jo želimo pobrisati int Skupina = (int)dataGridView1[0, stvrstice].Value;//0 pomeni indeks stolpca dataCommand.CommandText = "SELECT * From Artikli WHERE Skupina='" + Skupina + "'"; SqlDataReader dataReader = dataCommand.ExecuteReader(); //Če smo v tabeli Artikli našli artikel iz te skupine, potem skupine NE SMEMO brisati if (dataReader.Read() == true) { MessageBox.Show("Brisanje NI možno!\n\nV tabeli Artikli obstaja vsaj en artikel, ki pripada tej skupini!", "POZOR!", MessageBoxButtons.OK,MessageBoxIcon.Warning); } else

Page 261: PROŠNJA - uranic.tsckr.siuranic.tsckr.si/VISUAL C#/VISUAL C#.pdf · MICROSOFT VISUAL C# .NET Srečo Uranič . Visual C# . ... Za začetek novega projekta v okolju Visual C# Standard

Visual C# . NET

Algoritmi in programski jeziki – 4.letnik

261

{ if (MessageBox.Show("Brisanje vrstice", "BRISANJE", MessageBoxButtons.OKCancel, MessageBoxIcon.Question) == DialogResult.OK) { dataGridView1.Rows.RemoveAt(stvrstice); //Pobrišemo vrstico v dataGridView1 } } dataReader.Close();//zapremo podatkovni tok dataCommand.Connection.Close(); //Zapremo povezavo z bazo } catch { MessageBox.Show("Napaka pri dostopu do baze podatkov!"); } }

V zgornji kodi smo pobrisali vrstico le v gradniku dataGridView1. Potrebno je še ažurirati novo stanje v bazi podatkov. Spremembe zapišemo v bazo ali pa jih prekličemo s klikom na gumba Shrani in zapri okno oz. Preklic sprememb. Opcija Artikli vsebuje dve podopcij, Tabela Artiklov in Obdelava Artiklov. Namen prve je tabelarični prikaz in ažuriranje tabele Artikli, namen druge podopcije pa preprosta obdelava oz. poizvedba iz te tabele. Za potrebe podopcije Tabela Artiklov skreirajmo nov obrazec in ga poimenujmo fTabelaArtiklov. Nov objekt naj bo statičen, da bomo lahko do njegovih gradnikov oz. metod dostopali kar preko imena obrazca. Nov obrazec bomo odprli s klikom na podopcijo Tabele Artiklov na glavnem obrazcu, koda pa je naslednja: //Statičen objekt potreben za dostop do gradnika DataGridView na tem obrazcu static public fTabelaArtiklov TabelaArtiklov; private void tabelaArtiklovToolStripMenuItem_Click(object sender, EventArgs e) { //Nova istanca obrazca fTabelaArtiklov ima ime TabelaArtiklov TabelaArtiklov = new fTabelaArtiklov(); TabelaArtiklov.ShowDialog(); }

Obrazec fTabelaArtiklov oblikujmo tako, kot prikazuje slika:

DataGridView z

imenom dataGridView1

Page 262: PROŠNJA - uranic.tsckr.siuranic.tsckr.si/VISUAL C#/VISUAL C#.pdf · MICROSOFT VISUAL C# .NET Srečo Uranič . Visual C# . ... Za začetek novega projekta v okolju Visual C# Standard

Visual C# . NET

Algoritmi in programski jeziki – 4.letnik

262

Gradniku dataGridView1 določimo nastavimo lastnost DataSource na DSArtikli -> Artikli. . Za lepši izgled razširimo še lastnost ColumnHeadersDefaultCallStyle in nastavimo lastnost Font na Bold in po želji še lastnost BackColor. Za boljši pregled vrstic nastavimo še različni prikaz sodih in lihih vrstic: razširimo lastnost AlternatingRowsDefaultCellStyle, nato pa izberimo ustrezno barvo za lastnost BackColor. Kliknimo na tropičje pri lastnosti Columns in v oknu Edit Columns naredimo naslednje spremembe:

• Spremenimo tekst na vrhu stolpcev (lastnost Header Text)

• Stolpcema Dobavitelji in Skupine artiklov spremenimo lastnost ColumnType (dosedanja lastnost je DataGridViewTextBoxColumn) na DataGridViewComboBoxColumn. Ko smo to naredili, obema spremenimo lastnost DisplayStyle na ComboBox, nato pa še lastnosti razdelka Data. Stolpcu Dobavitelji nastavimo DisplayMember na ID_Dobavitelj, lastnost ValueMember pa na ID_Dobavitelj. Celice v tem stolpcu bodo imele obliko ComboBox-a, v katerega se bodo ob zagonu programa zapisali vsi dobavitelji, ob izbiri ustreznega dobavitelja pa se bo v celico zapisala vrednost polja ID_Dobavitelj.

Podobno storimo s poljem Skupina Artiklov – DisplayMember nastavimo na Skupina, ValueMember pa na ID_Skupina.

Objekt TabelaArtiklov v zagnanem projektu.

Če smo lastnosti gradniku dataGridView1 nastavili pravilno, nam je Visual C# sam zgeneriral kodo za dogodek Load tega obrazca: private void fTabelaArtiklov_Load(object sender, EventArgs e) { this.dobaviteljiTableAdapter.Fill(this.dSArtikli.Dobavitelji); this.skupine_artiklovTableAdapter.Fill(this.dSArtikli.Skupine_artiklov); this.artikliTableAdapter.Fill(this.dSArtikli.Artikli);

Page 263: PROŠNJA - uranic.tsckr.siuranic.tsckr.si/VISUAL C#/VISUAL C#.pdf · MICROSOFT VISUAL C# .NET Srečo Uranič . Visual C# . ... Za začetek novega projekta v okolju Visual C# Standard

Visual C# . NET

Algoritmi in programski jeziki – 4.letnik

263

}

Podatke v dataGridView1 bomo v zagnanem projektu lahko spreminjali (razen stolpca Šifra, ki je ključno polje v tabeli Artikli). Spremenimo lahko Dobavitelja in Skupino_Artiklov ( z ustrezno izbiro v ComboBox –ih), ime artikla (stolpec Ime) in pa podatek o količino (stolpec Kolicina). Zadnji stolpec je tudi edini, za katerega moramo še posebej preveriti pravilnost uporabnikovega vnosa. Gradniku dataGridView1 bomo zato zapisali kodo za dogodek CellValidating. Razlaga kode je zapisana v teoretičnem delu tega poglavja. private void dataGridView1_CellValidating(object sender,DataGridViewCellValidatingEventArgs e) { int zacasna; dataGridView1.Rows[e.RowIndex].ErrorText = ""; //Kontroliramo le celico Kolicina if (dataGridView1.Columns[e.ColumnIndex].DataPropertyName == "Kolicina") { if (!int.TryParse(e.FormattedValue.ToString(), out zacasna) || zacasna < 0) { dataGridView1.Rows[e.RowIndex].ErrorText = "Vnesi pozitivno celo število"; e.Cancel = true; } } }

Vse spremembe v dataGridView1 lahko shranimo s klikom na gumb Shrani Spremembe: private void bShrani_Click(object sender, EventArgs e) { try { DSArtikli spremembe = (DSArtikli)dSArtikli.GetChanges(); if (spremembe == null) { return; } DataTable dt = spremembe.Tables["Skupine_artiklov"]; DataRow[] NapacneVrstice = dt.GetErrors(); if (NapacneVrstice.Length == 0) { int steviloVrstic = artikliTableAdapter.Update(spremembe); dSArtikli.AcceptChanges(); } else { string errorMsg = null; foreach (DataRow vrstica in NapacneVrstice) { foreach (DataColumn stolpec in vrstica.GetColumnsInError()) { errorMsg += vrstica.GetColumnError(stolpec) + "\n"; } } MessageBox.Show("Napake v podatkih: " + errorMsg, "Prosim popravite podatke!", MessageBoxButtons.OK, MessageBoxIcon.Error); } } catch (Exception ex) { MessageBox.Show("Napaka: " + ex.Message, "Napake", MessageBoxButtons.OK, MessageBoxIcon.Error); dSArtikli.RejectChanges(); } }

Spremembe prekličemo s klikom na gumb Prekliči spremembe: private void bPreklici_Click(object sender, EventArgs e) { dSArtikli.RejectChanges(); }

Brisanje vrstice zapišemo v odzivni dogodek Click gumba Briši vrstico:

Page 264: PROŠNJA - uranic.tsckr.siuranic.tsckr.si/VISUAL C#/VISUAL C#.pdf · MICROSOFT VISUAL C# .NET Srečo Uranič . Visual C# . ... Za začetek novega projekta v okolju Visual C# Standard

Visual C# . NET

Algoritmi in programski jeziki – 4.letnik

264

private void bBrisi_Click(object sender, EventArgs e) { if (MessageBox.Show("Brisanje vrstice", "BRISANJE", MessageBoxButtons.OKCancel, MessageBoxIcon.Question) == DialogResult.OK) { //Ugotovimo številko izbrane vrstice int stvrstice = dataGridView1.CurrentCell.RowIndex; //pobrišemo izbrano vrstico dataGridView1.Rows.RemoveAt(stvrstice); } }

Pokažimo še, kako bi ažuriranje podatkov izbrane vrstice realizirali s pomočjo novega obrazca. V ta namen kreirajmo nov obrazec in ga poimenujmo FArtikel. Na obrazec postavimo tri TextBox-e, dva ComboBoxa in dva

gumba. Lastnost ReadOnly nastavimo na True, saj gre za ključno polje. Ker je polje Količina celoštevilsko, zapišimo za ta gradnik še dogodek KeyPress, ki porabniku omogoča le vnos cifer in decimalnega ločila:

private void tBKolicina_KeyPress(object sender, KeyPressEventArgs e) { if (((e.KeyChar < '0') || (e.KeyChar > '9')) && (e.KeyChar != ',') && (e.KeyChar != (char)(8))) e.Handled = true; }

Obrazec FArtikel bomo odprli z dvoklikom na gradnik dataGridView1 obrazca FTabelaArtiklov (dogodek DoubleClick gradnika DataGridView1). Pred prikazom tega obrazca pa moramo seveda poskrbeti za prenos podatkov izbrane vrstica gradnika dataGridView1 v ustrezne gradnike obrazca FArtikel, poleg tega pa moramo v ComboBox-a obrazca prenesti celotni vsebini tabele Dobavitelji in SkupineArtiklov. Samo kot primer bomo pri shranjevanju uporabnikovih podatkov napisali kodo za shranjevanje podatkov neposredno v bazo (in potem takoj ažurirali vsebino dataGridView1) in pa kodo za shranjevanje v gradnik dataGridView1 (s klikom na gumb Shrani Spremembe pa se podatki nato zapišejo še v bazo). private void dataGridView1_DoubleClick(object sender, EventArgs e) { /*Ažuriranje vrstice s pomočjo novega obrazca: SPREMENJENE PODATKE BOMO ZAPISALI NAzAJ V GRADNIK DataGridView IN ŠELE NATO AŽURIRALI BAZO*/ FArtikel IzbraniArtikel = new FArtikel(); string ID = Convert.ToString( dataGridView1[0, dataGridView1.CurrentCell.RowIndex].Value); //objekti tBID, tBIme, tBKolicina na obrazcu FArtikel morajo biti public!!!! IzbraniArtikel.tBID.Text = ID; string imeArtikla; imeArtikla = Convert.ToString( dataGridView1[2, dataGridView1.CurrentCell.RowIndex].Value); IzbraniArtikel.tBIme.Text = imeArtikla; string Kolicina; Kolicina = Convert.ToString( dataGridView1[4, dataGridView1.CurrentCell.RowIndex].Value); IzbraniArtikel.tBKolicina.Text = Kolicina; int ID_skupina; ID_Skupina = Convert.ToInt32(dataGridView1[3, dataGridView1.CurrentCell.RowIndex].Value); int ID_dobavit=0; try { ID_dobavit = Convert.ToInt32(dataGridView1[1, dataGridView1.CurrentCell.RowIndex].Value); } catch { } //ComboBox CBSkupina napolnimo z vrednostmi iz tabele Skupina_Artiklov string skupina="",dobavit=""; try { SqlCommand dataCommand = new SqlCommand(); dataCommand.Connection = skupine_artiklovTableAdapter.Connection;

Page 265: PROŠNJA - uranic.tsckr.siuranic.tsckr.si/VISUAL C#/VISUAL C#.pdf · MICROSOFT VISUAL C# .NET Srečo Uranič . Visual C# . ... Za začetek novega projekta v okolju Visual C# Standard

Visual C# . NET

Algoritmi in programski jeziki – 4.letnik

265

dataCommand.Connection.Open(); //kreiramo poizvedbo dataCommand.CommandText = "SELECT ID_Skupina,Skupina "; dataCommand.CommandText += "From Skupine_artiklov"; SqlDataReader dataReader = dataCommand.ExecuteReader(); //v ComboBox cBSkupina zapišemo vse stranke iz tabele Skupine_artiklov while (dataReader.Read())//dokler obstajajo podatki { /*v cBSkupina zapišemo dato ID_skupine, kot tudi ime skupine - med njima je LOČILNI znak PRESLEDEK*/ IzbraniArtikel.cBSkupina.Items.Add(dataReader.GetValue(0).ToString()+" "+dataReader.GetValue(1).ToString()); if ((int)dataReader.GetValue(0) == ID_skupina) skupina = dataReader.GetValue(0).ToString()+" "+dataReader.GetValue(1).ToString(); } dataReader.Close();//zapremo podatkovni tok dataCommand.Connection.Close(); //Zapremo povezavo z bazo //v cBDobavitelji zapišemo seznam vseh dobaviteljev dataCommand.Connection = dobaviteljiTableAdapter.Connection; dataCommand.Connection.Open(); //kreiramo poizvedbo dataCommand.CommandText = "SELECT ID_Dobavitelj,Ime "; dataCommand.CommandText += "From Dobavitelji"; dataReader = dataCommand.ExecuteReader(); //v ComboBox cBDobavitelji zapišemo vse stranke iz tabele Skupine_artiklov while (dataReader.Read())//dokler obstajajo podatki { /*v cBDobavitelji zapišemo dato ID_Dobavitelja, kot tudi ime - med njima je LOČILNI znak PRESLEDEK*/ IzbraniArtikel.cBDobavitelj.Items.Add(dataReader.GetValue(0).ToString() + " " + dataReader.GetValue(1).ToString()); if ((int)dataReader.GetValue(0) == ID_dobavit) dobavit = dataReader.GetValue(0).ToString()+" " + dataReader.GetValue(1).ToString(); } dataReader.Close();//zapremo podatkovni tok dataCommand.Connection.Close(); //Zapremo povezavo z bazo } catch { MessageBox.Show(" Napaka pri dostopu do baze podatkov!"); } IzbraniArtikel.cBSkupina.Text =skupina; IzbraniArtikel.cBDobavitelj.Text = dobavit; if (IzbraniArtikel.ShowDialog() == DialogResult.OK) { try { /*SAMO ZA VAJO: Prikaz dveh načinov shranjevanja podatkov vnešenih oz. izbranih na objektu IzbraniArtikel PONUDIMO dva načina: shranjevanje NEPOSREDNO v bazo (in takojšnja osvežitev vsebine dataGridView1) in shranjevanje v gradnik dataGridView1 in nato po želji še shranjevanje v bazo podatkov*/ if (MessageBox.Show("Shranjevanje neposredno v bazo?","Shranjevanje podatkov!", MessageBoxButtons.OKCancel, MessageBoxIcon.Question)==DialogResult.OK) { /*spremenjene podatke shranimo neposredno v bazo in nato osvežimo sebino gradnika dataGridView1*/ SqlCommand dataCommand1 = new SqlCommand(); dataCommand1.Connection = artikliTableAdapter.Connection; dataCommand1.CommandText = "UPDATE Artikli SET ID_dobavitelj = @ID_dobavitelj, Ime = @Ime, Skupina = @Skupina, Kolicina = @Kolicina "; dataCommand1.CommandText += "WHERE (ID_artikel = @Original_ID_artikel) AND (ID_dobavitelj IS NULL OR "; dataCommand1.CommandText += "ID_dobavitelj = @Original_ID_dobavitelj) AND (Ime IS NULL OR "; dataCommand1.CommandText += "Ime = @Original_Ime) AND (Skupina IS NULL OR Skupina = @Original_Skupina) "; dataCommand1.CommandText += "AND (Kolicina IS NULL OR Kolicina = @Original_Kolicina)"; dataCommand1.CommandType=CommandType.Text; dataCommand1.Parameters.AddWithValue("@Original_ID_Artikel", dataGridView1[0, dataGridView1.CurrentCell.RowIndex].Value);

Page 266: PROŠNJA - uranic.tsckr.siuranic.tsckr.si/VISUAL C#/VISUAL C#.pdf · MICROSOFT VISUAL C# .NET Srečo Uranič . Visual C# . ... Za začetek novega projekta v okolju Visual C# Standard

Visual C# . NET

Algoritmi in programski jeziki – 4.letnik

266

dataCommand1.Parameters.AddWithValue("@Original_ID_Dobavitelj", dataGridView1[1, dataGridView1.CurrentCell.RowIndex].Value); dataCommand1.Parameters.AddWithValue("@Original_Ime", dataGridView1[2, dataGridView1.CurrentCell.RowIndex].Value); dataCommand1.Parameters.AddWithValue("@Original_Skupina", dataGridView1[3, dataGridView1.CurrentCell.RowIndex].Value); dataCommand1.Parameters.AddWithValue("@Original_Kolicina", dataGridView1[4, dataGridView1.CurrentCell.RowIndex].Value); string[] tabDob = (IzbraniArtikel.cBDobavitelj.Text).Split(); int dobavitelj = Convert.ToInt32(tabDob[0]); //metoda AddWithValues v ustrezni parameter zapiše izbrane oz. vnešene vrednosti dataCommand1.Parameters.AddWithValue("@ID_Dobavitelj",dobavitelj); dataCommand1.Parameters.AddWithValue("@Ime",IzbraniArtikel.tBIme.Text); //s pomočjo metode Split iz izbrane vrstice izločimo le ID številko skupine string[] tab = (IzbraniArtikel.cBSkupina.Text).Split(); int skupinaArt = Convert.ToInt32(tab[0]); dataCommand1.Parameters.AddWithValue("@Skupina",skupinaArt); dataCommand1.Parameters.AddWithValue("@Kolicina",IzbraniArtikel.tBKolicina.Text); dataCommand1.Connection.Open(); //odpremo poezavo z bazo podatkov dataCommand1.ExecuteNonQuery(); //izvedemo SQL stavek dataCommand1.Connection.Close(); //OBVEZNO zapremo povezavo z bazo, sicer //spremembe ne bodo zavedene //ažuriramo daArtikli s tem pa tudi stanje v dataGridView1 this.artikliTableAdapter.Fill(this.dSArtikli.Artikli); } else { //spremenjene podatke shranimo nazaj dataGridView1, šele kasneje pa jih lahko //shranimo tudi v bazo string[] tabDob = (IzbraniArtikel.cBDobavitelj.Text).Split(); int dobavitelj = Convert.ToInt32(tabDob[0]); dataGridView1[1, dataGridView1.CurrentCell.RowIndex].Value = dobavitelj; dataGridView1[2,dataGridView1.CurrentCell.RowIndex].Value = IzbraniArtikel.tBIme.Text; string[] tab = (IzbraniArtikel.cBSkupina.Text).Split(); int skupinaArt = Convert.ToInt32(tab[0]); dataGridView1[3, dataGridView1.CurrentCell.RowIndex].Value = skupinaArt; dataGridView1[4, dataGridView1.CurrentCell.RowIndex].Value = IzbraniArtikel.tBKolicina.Text; dataGridView1.Refresh(); } } catch { MessageBox.Show("Napaka pri shranjevanju podatkov! "+e.ToString()); } } }

Na obrazcu FTabelaArtiklov realizirajmo še gumb Dodaj, za dodajanje nove vrstice podatkov v tabelo Artikli. Za dodajanje bomo uporabili kar obrazec FArtilkli, le koda, ki jo zapišemo v odzivni dogodek Click gumba Dodaj je nekoliko drugačna. Vneseni podatki se bodo v našem primeru shranili neposredno v bazo, zato je po shranjevanju potrebno ažurirati vsebino dataGridView1. private void bDodaj_Click(object sender, EventArgs e) { FArtikel IzbraniArtikel = new FArtikel(); SqlCommand dataCommand = new SqlCommand(); dataCommand.Connection = skupine_artiklovTableAdapter.Connection; dataCommand.Connection.Open(); //kreiramo poizvedbo dataCommand.CommandText = "SELECT ID_Skupina,Skupina "; dataCommand.CommandText += "From Skupine_artiklov"; SqlDataReader dataReader = dataCommand.ExecuteReader(); //v cBSkupina zapišemo vse podatke iz tabele Skupine_artiklov while (dataReader.Read())//dokler obstajajo podatki { //v cBSkupina zapišemo ID_skupine in tudi ime skupine-med njima je LOČILNI znak PRESLEDEK IzbraniArtikel.cBSkupina.Items.Add(dataReader.GetValue(0).ToString() + " " + dataReader.GetValue(1).ToString()); } dataReader.Close();//zapremo podatkovni tok dataCommand.Connection.Close(); //Zapremo povezavo z bazo

Page 267: PROŠNJA - uranic.tsckr.siuranic.tsckr.si/VISUAL C#/VISUAL C#.pdf · MICROSOFT VISUAL C# .NET Srečo Uranič . Visual C# . ... Za začetek novega projekta v okolju Visual C# Standard

Visual C# . NET

Algoritmi in programski jeziki – 4.letnik

267

//v cBDobavitelji zapišemo seznam vseh dobaviteljev dataCommand.Connection = dobaviteljiTableAdapter.Connection; dataCommand.Connection.Open(); //kreiramo poizvedbo dataCommand.CommandText = "SELECT ID_Dobavitelj,Ime "; dataCommand.CommandText += "From Dobavitelji"; dataReader = dataCommand.ExecuteReader(); //v cBSkupina zapišemo vse vrstice iz tabele Skupine_artiklov while (dataReader.Read())//dokler obstajajo podatki { //v cBDobavitelji zapišemo ID_Dobavitelja in ime - med njima je LOČILNI znak PRELEDEK IzbraniArtikel.cBDobavitelj.Items.Add(dataReader.GetValue(0).ToString() + " " + dataReader.GetValue(1).ToString()); } dataReader.Close();//zapremo podatkovni tok dataCommand.Connection.Close(); //Zapremo povezavo z bazo if (IzbraniArtikel.ShowDialog() == DialogResult.OK) { try { //NOVO vrstico shranimo neposredno v tabelo v bazi podatkov, nato pa osvežimo //dSArtikli in s tem tudi vsebino dataGridView1 SqlCommand dataCommand1 = new SqlCommand(); dataCommand1.Connection = artikliTableAdapter.Connection; //v SQL stavku določimo štiri parametre, preko katerih bomo v ustrezna polja tabele //Artikli posredovali nove vrednosti dataCommand1.CommandText = "INSERT INTO Artikli(ID_dobavitelj, Ime, Skupina, Kolicina) VALUES (@ID_Dobavitelj,@Ime,@Skupina,@Kolicina)"; dataCommand1.CommandType=CommandType.Text; //s pomočjo metode Split iz izbrane vrstice izločimo le ID številko dobavitelja string[] tabDob = (IzbraniArtikel.cBDobavitelj.Text).Split(); int dobavitelj = Convert.ToInt32(tabDob[0]); //metoda AddWithValues v ustrezni parameter zapiše izbrane oz. vnešene vrednosti dataCommand1.Parameters.AddWithValue("@ID_Dobavitelj",dobavitelj); dataCommand1.Parameters.AddWithValue("@Ime",IzbraniArtikel.tBIme.Text); //s pomočjo metode Split iz izbrane vrstice izločimo le številko skupine string[] tab = (IzbraniArtikel.cBSkupina.Text).Split(); int skupinaArt = Convert.ToInt32(tab[0]); dataCommand1.Parameters.AddWithValue("@Skupina",skupinaArt); dataCommand1.Parameters.AddWithValue("@Kolicina",IzbraniArtikel.tBKolicina.Text); dataCommand1.Connection.Open(); //odpremo pvezavo z bazo podatkov dataCommand1.ExecuteNonQuery(); //izvedemo SQL stavek dataCommand1.Connection.Close(); //OBVEZNO zapremo povezavo z bazo, sicer spremembe ne //bodo zavedene //ažuriramo daArtikli s tem pa tudi stanje v dataGridView1 this.artikliTableAdapter.Fill(this.dSArtikli.Artikli); } catch { MessageBox.Show("Napaka pri shranjevanju podatkov!"); } } }

Napišimo še kodo za podopcijo Obdelava Artiklov, ki je sestavni del glavnega menija na osnovnem obrazcu projekta. Ugotovili bomo in na koncu v sporočilnem oknu tudi izpisali, kolikšna je skupna količina vseh artiklov v tabeli Artikli. private void obdelavaArtiklovToolStripMenuItem_Click(object sender, EventArgs e) { DSArtikliTableAdapters.ArtikliTableAdapter aArtikli = new DSArtikliTableAdapters.ArtikliTableAdapter(); SqlCommand dataCommand = new SqlCommand(); dataCommand.Connection = aArtikli.Connection ; dataCommand.Connection.Open(); //kreiramo poizvedbo vseh vrednosti Kolicina iz tabele Artikli, ki so različna od null dataCommand.CommandText = "SELECT Kolicina From Artikli WHERE Kolicina IS NOT NULL"; SqlDataReader dataReader = dataCommand.ExecuteReader(); int skupaj=0; while (dataReader.Read())//dokler obstajajo podatki { //v cBSkupina zapišemo ID_skupine, in ime skupine - med njima je LOČILNI znak PRESLEDEK

Page 268: PROŠNJA - uranic.tsckr.siuranic.tsckr.si/VISUAL C#/VISUAL C#.pdf · MICROSOFT VISUAL C# .NET Srečo Uranič . Visual C# . ... Za začetek novega projekta v okolju Visual C# Standard

Visual C# . NET

Algoritmi in programski jeziki – 4.letnik

268

try { skupaj = skupaj + Convert.ToInt32(dataReader.GetValue(0)); } catch { MessageBox.Show("Napaka pri branju podatkov!"); } } dataReader.Close();//zapremo podatkovni tok dataCommand.Connection.Close(); //Zapremo povezavo z bazo MessageBox.Show("Skupna količina vseh artiklov v tabeli: "+skupaj.ToString()); }

Transakcije

Kadar je operacija nad podatki sestavljena iz več ukazov (primer v bazi nabavaSQL je npr. brisanje vrstice v tabeli Skupina_Artikov in hkratno brisanje vseh artiklov iz tabele Artikli, ki pripadajo pobrisani vrstici v tabeli Skupina_Artiklov), uporabimo transakcijo. Transakcija običajno (ni pa nujno) povzroči več sprememb v eni ali pa v več tabelah. Bistvena lastnost transakcij je, da se bodo izvedli vsi SQL ukazi znotraj transakcije, ali pa nobeden. Če se torej transakcija ne izvede v celoti, se vse spremembe zavržejo, vzpostavi se prvotno stanje (stanje pred transakcijo). Transakcije torej uporabljamo za zavarovanje podatkov v primeru, da med izvajanjem SQL stavkov pride do napake in je potrebno vzpostaviti prvotno stanje. Transakcijo pričnemo s kreiranjem novega objekta razreda SQLTransaction, končamo pa s stavkom COMMIT (ki dokončno potrdi spremembe v bazi), ali pa z ukazom ROLLBACK (ki spremembe zavrže). Primer: Kreirajmo nov projekt in v projekt dodajmo DataSet in ga povežimo z bazo nabavaSQL. V DataSet dodajmo dva TableAdapter-ja.

Na osnovni obrazec dodajmo dva gradnika DataGridView in jima določimo lastnost DataSource tako, da bomo imeli v zgornjem gradniku (dataGridView1) vsebino tabele Skupine_artiklov, v spodnjem gradniku dataGridView2 pa vsebino tabele Artikli. Na obrazec postavimo še dva gumba. Izgled obrazca ko poženemo projekt Odzivnemu dogodku Click gumba Dodaj Skupino bomo priredili transakcijo, ki bo v tabelo Skupina_artiklov dodala novo vrstico (skupini bomo dali ime SkupinaXX, pri čemer je XX trenutno število podatkov v tej tabeli).

Page 269: PROŠNJA - uranic.tsckr.siuranic.tsckr.si/VISUAL C#/VISUAL C#.pdf · MICROSOFT VISUAL C# .NET Srečo Uranič . Visual C# . ... Za začetek novega projekta v okolju Visual C# Standard

Visual C# . NET

Algoritmi in programski jeziki – 4.letnik

269

private void bDodaj_Click(object sender, EventArgs e) { SqlConnection dataConnection = new SqlConnection(); dataConnection.ConnectionString = "Integrated Security=true;" + "Initial Catalog=nabavaSQL;" + "Data Source=mojRacunalnik\\SQLExpress"; dataConnection.Open(); //Kreiramo nov objekt za transakcijo SqlTransaction myTrans = dataConnection.BeginTransaction(); //Kreiramo nov objekt za SQL ukaz SqlCommand dataCommand = new SqlCommand(); dataCommand.Connection = dataConnection; dataCommand.Transaction = myTrans; try { int skupina = dataGridView1.Rows.Count; string novaSkupina = "Skupina" + skupina.ToString(); dataCommand.CommandText = "Insert INTO Skupine_artiklov (Skupina) VALUES (@novaSk)"; dataCommand.Parameters.AddWithValue("@novaSk", novaSkupina); dataCommand.CommandType = CommandType.Text; dataCommand.ExecuteNonQuery(); //dokončno potrdimo spremembe v bazi myTrans.Commit(); } catch (Exception ep) { //Če je prišlo do napake, vzpostavimo prvorno stanje v bazi podatkov myTrans.Rollback(); MessageBox.Show("Napaka pri shranjevanju podatkov!"); } finally { dataConnection.Close(); //Ažuriramo vsebino gradnika dataGridView1 – tako da ažuriramo objekt dataSet1 this.skupine_artiklovTableAdapter.Fill(this.dataSet1.Skupine_artiklov); this.artikliTableAdapter.Fill(this.dataSet1.Artikli); } }

Odzivnemu dogodku Click gumba Briši Skupino pa bomo priredili transakcijo, ki bo v tabelo Skupina_artiklov izbrano vrstico pobrisala, nato pa v tabeli Artikli pobrisale še vse vrstice, v katerih nastopa podatek Skupina, ki smo pobrisali v tabeli Skupina_artiklov. V transakciji bomo imeli torej dva SQL ukaza in samo v primeru, da se bosta oba izvedla do konca, bo stanje v bazi podatkov spremenjeno, sicer pa ne. private void bBrisi_Click(object sender, EventArgs e) { SqlConnection dataConnection = new SqlConnection(); dataConnection.ConnectionString = "Integrated Security=true;" + "Initial Catalog=nabavaSQL;" + "Data Source=sreco-PC\\SQLExpress"; dataConnection.Open(); //Kreiramo nov objekt za SQL transakcijo v SQL bazi podatkov SqlTransaction myTrans = dataConnection.BeginTransaction(); //Kreiramo nov objekt za SQL stavek, ki ga bomo izveddli v SQL bazi podatkov SqlCommand dataCommand = new SqlCommand(); dataCommand.Connection = dataConnection; dataCommand.Transaction = myTrans; try { //ugotovimo vrednost v celici ID_skupina izbrane vrstice int skupinaID; skupinaID = Convert.ToInt32(dataGridView1[0,dataGridView1.CurrentCell.RowIndex].Value); //oblikujemo SQL ukaz za brisanje te vrstice dataCommand.CommandText = "delete Skupine_artiklov where ID_skupina=(@novaSk)"; dataCommand.Parameters.AddWithValue("@novaSk", skupinaID); //Poizkusimo izvesti dejansko brisanje izbrane skupine v bazi podatkov. dataCommand.ExecuteNonQuery(); /*oblikujemo še SQL ukaz za brisanje vseh artiklov iz tabele Artikli, ki pripadajo izbrisani skupini*/ dataCommand.CommandText = "delete Artikli where Skupina=(@novaSk)"; //Poizkusimo izvesti dejansko brisanje vseh artiklov iz te skupine v bazi podatkov. dataCommand.ExecuteNonQuery(); //dokončno shranimo narejene spremembe myTrans.Commit();

Page 270: PROŠNJA - uranic.tsckr.siuranic.tsckr.si/VISUAL C#/VISUAL C#.pdf · MICROSOFT VISUAL C# .NET Srečo Uranič . Visual C# . ... Za začetek novega projekta v okolju Visual C# Standard

Visual C# . NET

Algoritmi in programski jeziki – 4.letnik

270

} catch (Exception ep) { //Če transakcija ni uspela, vzpostavimo začetno stanje. myTrans.Rollback(); MessageBox.Show("Napaka pri brisanju podatkov!"); } finally { //na koncu zapremo našo transakcijo dataConnection.Close(); this.skupine_artiklovTableAdapter.Fill(this.dataSet1.Skupine_artiklov); } }

Page 271: PROŠNJA - uranic.tsckr.siuranic.tsckr.si/VISUAL C#/VISUAL C#.pdf · MICROSOFT VISUAL C# .NET Srečo Uranič . Visual C# . ... Za začetek novega projekta v okolju Visual C# Standard

Visual C# . NET

Algoritmi in programski jeziki – 4.letnik

271

Osnove razvoja oz. priprave aplikacije za potrebe testiranja in priprave namestitvenega (Setup) programa Na neki točki v procesu razvoja Windows aplikacije, je potrebno projekt pripraviti za testiranje na ciljnem računalniku in ga v končni fazi pripraviti tako, da ga bodo uporabniki lahko namestili na svoj računalnik in ga tudi uporabljali. Testiranje projekta pri uporabniku nam lahko v veliki meri pomaga pri iskanju boljših rešitev in pri odpravljanju težav, do katerih lahko pride pri uporabi aplikacije na drugem računalniku. V preteklosti je bilo večina projektov instaliranih s pomočjo raznih namestitvenih (Setup) programov, ki so bili shranjeni na CD-ju ali nekje v omrežni soseščini. V zadnjih letih pa lahko Windows projekte instaliramo neposredno preko Web strežnikov, kar je posebno ugodno pri nadgradnji projektov. Kot primer lahko vzamemo programe za protivirusno zaščito, ki jih je zaradi stalno novih in novih virusov potrebno nadgrajevati zelo pogosto. Visual Sudio 2008 nam za potrebe testiranja, instalacij in nadgradenj ponuja kateregakoli od opisanih načinov. Obstajajo trije načini za razmnoževanje oz. testiranje aplikacij.

• XCopy • ClickOnce • Kreiranje Setup projekta– a (namestitvenega programa)

Vsak od teh načinov ima svoje prednosti pa tudi slabosti.

XCopy

Najstarejši način za prenos aplikacije na drug sistem je s pomočjo metode xcopy operacijskega sistema. Mapo, v katero je shranjen naša izvršilna datoteka (.exe) na sistemu, na katerem smo ga razvili, enostavno prekopiramo na uporabnikov disk. Če so v tej mapi vse potrebne datoteke za našo aplikacijo, se bo projekt pravilno odprl in uporaba aplikacije oziroma njeno testiranje bo možno tudi na tem računalniku. Prednost tega načina je, da ni potrebna nobena dodatna dodatna konfiguracija ali pa registracija produkta. Postopek: Končna izvršilna verzija projekta, ki smo ga razvili je na našem sistemu shranjena v podmapi našega projekta in sicer v …bin\Release. Taka nastavitev je privzeta, spremenimo oz. preverimo po jo tako, da v Solution Explorerju izberemo ustrezen projekt, nato pa z desnim miškinim gumbom odpremo Pop Up Meni in izberemo opcijo Properties. Odpre se okno za nastavljanje lastnosti projekta. Izberimo opcijo Build in preverimo ter po želji (ni priporočljivo) spremenimo nastavitve.

Nastavitev mape za Release verzijo projekta

Page 272: PROŠNJA - uranic.tsckr.siuranic.tsckr.si/VISUAL C#/VISUAL C#.pdf · MICROSOFT VISUAL C# .NET Srečo Uranič . Visual C# . ... Za začetek novega projekta v okolju Visual C# Standard

Visual C# . NET

Algoritmi in programski jeziki – 4.letnik

272

S pomočjo ukaza za kopiranje datotek XCopy operacijskega sistema DOS (v oknu Programi->Pripomočki->Ukazni poziv, oz. Programs ->Accessoires->Command Prompt če imamo angleško verzijo operacijskega sistema) celotno mapo prekopiramo na nek medij (npr. CD, ključ, medij v omrežni soseščini, …) Primer ukaza XCopy C:>\ xcopy "C:\Moja aplikacija\bin\release\*" "E:\Moja aplikacija\" /s Seveda lahko kopiranje izvedemo tudi s pomočjo tehnike Copy – Paste v Windows Explorerju (Raziskovalcu). Ta način je seveda najenostavnejši, ima pa veliko pomanjkljivosti. Če namreč v mapi Release ni vseh potrebnih datotek, ali pa če uporabnik nima instaliranega okolja .&ET Framework, aplikacije na uporabnikovem računalniku ne bo možno zagnati.

ClickOnce

Ena izmed velikih prednosti Web aplikacij pred Windows aplikacijami je ta, da instalacija projekta oz. aplikacije ni potrebna. Zaradi tega je vsaka nadgradnja Web aplikacije takoj na voljo tudi uporabnikom Vsako novo verzijo programa moramo le prenesti na strežnik od koder je na voljo uporabnikom in ne na vsako posamično lokalno delovno postajo. Metoda ClickOnce je nova zmožnost v Visual Studio 2008 in omogoča zelo enostavno kreiranje in razmnoževanje novih projektov. Pri kreiranju nove verzije programa, ki bo na voljo uporabnikom preko spletnega strežnika, ali pa tudi preko klasične metode (npr. instalacijski CD ipd.) nam pomaga poseben čarovnik (Wizard), vsebovan v razvojnem okolju Visual C#. Izberimo projekt, za katerega bomo naredili nov namestitveni projekt in ga postavili na nek strežnik, kjer bo na voljo uporabnikom, kot alternativo pa še kreirali namestitveni program, s pomočjo katerega bo možno naš projekt instalirati pri uporabniku kar preko ključa oz. CD-ja. Izberimo npr. projekt Menjalnica, katerega izdelavo smo prikazali v enem od prejšnjih poglavij. Odprimo torej najprej projekt Menjalnica. Čarovnika poženemo iz menija Build -> Publish Menjalnica. V prvem koraku moramo navesti lokacijo, na kateri želimo objaviti (oz. kamor želimo postaviti) naše namestitvene datoteke.

Na voljo imamo več možnosti, saj lahko namestitveno datoteko (datoteke) kreiramo na strežniku, kjerkoli na disku, na ftp strežniku oz. na spletni strani.

POZOR! V primeru, da bomo namestitvene datoteke kreirali na nekem strežniku (tako kot v primeru, ki ga prikazujemo), bo na strežniku v izbrani mapi poleg namestitvenih datotek nastala tudi datoteka Publish.htm, ki omogoča instalacijo preko spleta (npr. s pomočjo Microsoft Internet Explorerja). Če pa bomo namestitvene

Page 273: PROŠNJA - uranic.tsckr.siuranic.tsckr.si/VISUAL C#/VISUAL C#.pdf · MICROSOFT VISUAL C# .NET Srečo Uranič . Visual C# . ... Za začetek novega projekta v okolju Visual C# Standard

Visual C# . NET

Algoritmi in programski jeziki – 4.letnik

273

datoteke kreirali na nekem disku, bomo na le-tem dobili le namestitvene datoteke (najpomembnejša med njimi je Setup.exe, ki požene namestitev). V drugem koraku določimo, kako bo uporabnik instaliral aplikacijo. Navadno pustimo opcijo, ki se prikaže v oknu nespremenjeno, tako tudi v našem primeru pustimo nastavitev, ki smo jo izbrali v prvem koraku.

V tretjem koraku določimo, ali bo naša aplikacija dostopna offline. Privzeta je vrednost Yes, kar pomeni, da bo

aplikacija instalirana na uporabnikov trdi disk, v Start meni bo dodana bližnjica za zagon aplikacije, možna pa bo tudi odstranitev te aplikacije preko opcije Dodaj ali odstrani programe v &adzorni plošči (Control Panel). Možno je seveda tudi kreiranje aplikacije, ki je dostopna le online. V tem primeru se aplikacija na uporabnikovem računalniku požene neposredno s strežnika. V tem primeru ni potrebno kreiranje bližnjice v Start meniju in obenem tudi ne zmožnost odstranitve aplikacije. Potrebna je le vsakokratna vzpostavitev povezave s strežnikom in download aplikacije preko tega strežnika pred njeno uporabo, kar pa lahko traja dalj časa.

V četrtem koraku imamo možnost potrditve vseh nastavitev. S klikom na gumb Finish bo čarovnik dokončno kreiral vse namestitvene datoteke, obenem pa še ustrezen certifikat, ki ga lahko uporabljamo za testiranje. Obenem se spremenijo/dodajo nekatere nastavitve (datoteke) v Solution Explorerju (glede prijavljanja, varnosti in razmnoževanja aplikacije). Za kasnejšen pregled in spreminjanje teh nastavitev lahko kadarkoli v Solution Explorerju izberemo projektno datoteko, kliknemo desni miškin gumb in izberemo Properties. V oknu ki se prikaže izberemo ustrezen zavihek (npr. zavihek o varnosti – Security) in opravimo želene spremembe.

Page 274: PROŠNJA - uranic.tsckr.siuranic.tsckr.si/VISUAL C#/VISUAL C#.pdf · MICROSOFT VISUAL C# .NET Srečo Uranič . Visual C# . ... Za začetek novega projekta v okolju Visual C# Standard

Visual C# . NET

Algoritmi in programski jeziki – 4.letnik

274

Namestitvene datoteke so sedaj pripravljene. Če smo v prvem koraku izbrali strežnik, ftp strežnik oz. spletno stran je ob kreiranju namestitvenih datoteka nastala tudi datoteka Publish.htm, ki omogoča instalacijo preko spleta (npr. s pomočjo Microsoft Internet Explorerja), torej Instalacijo aplikacije preko spletne strani. Bodočim uporabnikom naše aplikacije sedaj le pošljemo ustrezen link za dostop do namestitve. V našem primeru je spletna stran dostopna preko lokalnega intraneta.

Slika prikazuje obliko spletnega okna, ki se prikaže ob aktiviranju spletne strani. Instalacijo poženemo s klikom na gumb Install.

Na ekranu se prikaže pogovorno okno za namestitev nove aplikacije. V splošnem pogovorno okno vsebuje tudi varnostno opozorilo, ker pač naša testna aplikacija vsebuje le testni certifikat, ne pa tudi certifikat s

pooblastilom. Ne glede na to obvestilo bodo naši uporabniki vedeli, da aplikacija prihaja z mesta, ki mu lahko zaupajo, še poseben ker gre v našem primeru za namestitev preko lokalnega intraneta. O tem seveda lahko uporabnike prej tudi obvestimo s posebnim sporočilom preko elektronske pošte. Varnostnega opozorila pa se seveda lahko znebimo s pomočjo instalacije veljavnega certifikata.

Druga (popolnoma enakovredna, ali pa še celo primernejša) pot za zagon čarovnika je ta, da v Solution

Explorerju izberemo projekt (v našem primeru projekt Menjalnica), nato kliknemo desni miškin gumb, izberemo Properties in nato v oknu, ki se prikaže, izberemo zavihek Publish. Namestitvene datoteke lahko sedaj pripravimo preko opcije Publish Wizard (s potrjevanjem posameznih korakov, tako kot pri prvem načinu) ali pa kar s klikom na Publish &ow (upoštevajo se nastavitve, ki so trenutno na odprtem obrazcu).

Page 275: PROŠNJA - uranic.tsckr.siuranic.tsckr.si/VISUAL C#/VISUAL C#.pdf · MICROSOFT VISUAL C# .NET Srečo Uranič . Visual C# . ... Za začetek novega projekta v okolju Visual C# Standard

Visual C# . NET

Algoritmi in programski jeziki – 4.letnik

275

Za vsako ClickOnce namestitev je možno tudi avtomatsko posodabljanje oz. update, če je vzpostavljena povezava z ustreznim strežnikom, na katerem je projekt kreiran. Recimo, da na strežniku obstaja novejša verzija aplikacije Menjalnica. Ko uporabnik starta svojo verzijo aplikacije, le ta najprej preveri, ali na strežniku obstaja novejša različica. Če obstaja novejša verzija, se odpre pogovorno okno s sporočilom, da obstaja novejša različica ter nam ponudi download te nove verzije. S klikom na gumb OK pogovornega okna bo nova različica instalirana in se bo odslej naprej tudi izvajala na uporabnikovem računalniku (vse do naslednje nove verzije). Kdaj in v katerem primeru se bo prikazalo okno z obvestilom in nastavitvami za nadgradnje naše aplikacije, pa lahko določimo v pogovornem oknu Application Updates. Okno odpremo tako, da v Solution Explorerju

izberemo projekt (v našem primeru projekt Menjalnica), nato kliknemo desni miškin gumb, izberemo Properties in nato v oknu, ki se prikaže, izberemo zavihek Publish in nato kliknemo na gumb Updates.

Odpre se pogovorno okno za določanje / spreminjanje privzetih lastnosti za posodobitve. Če na primer želimo,

da se naša aplikacija zažene preden so naložene posodobitve, kliknemo na prvi radijski gumb. Tako se bo najnovejša verzija aplikacije zagnala šele, ko jo bomo odprli naslednjič. V tem primeru imamo možnost še dodatne nastavitve, saj lahko določimo tudi interval, kako pogosto naj naša aplikacija preverja, ali obstajajo posodobitve. Na vrhu tega sporočilnega okna obstaja tudi Potrditveno gumb (CheckBox) s pomočjo katerega lahko izključimo preverjanje obstoja novih verzij oziroma posodobitev. Na dnu okna je še gumb za določanje minimalne zahtevane verzije programa, ki jo je še možno posodobiti, ter možnost izbire druge lokacije od koder se bo izvedla posodobitev.

Setup (namestitveni) program Če nam prva dva načina za kreiranje novih verzij programov ne zadoščata, je na voljo še tretja, najbolj kompleksna. Žal pa ta verzija ni na voljo v Visual C# Express Edition, ampak le v višjih različicah razvojnega orodja Visual C# ( npr. Standard Edition). Kreiranje namestitvenega programa z metodo Setup je lahko zelo kompleksen projekt. V nadaljevanju bo prikazano le nekaj osnovnih idej in postopkov, kako tako kreiranje poteka. V Visual C# Standard Edition ali

Page 276: PROŠNJA - uranic.tsckr.siuranic.tsckr.si/VISUAL C#/VISUAL C#.pdf · MICROSOFT VISUAL C# .NET Srečo Uranič . Visual C# . ... Za začetek novega projekta v okolju Visual C# Standard

Visual C# . NET

Algoritmi in programski jeziki – 4.letnik

276

višjih različicah najprej odpremo projekt, za katerega želimo izdelati Setup program – v našem primeru bo to zopet projekt Menjalnica. Naredili bomo projekt z imenom Menjalnica_Setup - to bo posebna vrsta Windows aplikacije, namenjena za namestitev (instalacijo) druge Windows aplikacije z imenom Menjalnica. Ko je projekt, za katerega želimo narediti Setup aplikacijo, odprt, izberimo File -> Add -> &ew Project. Odpre se pogovorno okno Add &ew Project. V levem delu tega okna razširimo opcijo Other Project Types (s nklikom na znak +), izberimo opcijo Setup in Deployment. V desnem oknu nato izberemo opcijo Setup Project, ter vnesemo ime našega namestitvenega projekta. Po potrditvi nastavitev, s klikom na gumb OK, je naš nov projekt dodan k obstoječemu projektu, kar se vidi tudi v oknu Solution Explorer – v tem oknu e pojavi nov projekt z imenom Menjalnica_Setup. Naš namestitveni projekt sedaj nadaljujemo tako, da v Solution Explorerju izberemo Vrstico Menjalnica_Setup in pod vrhom okna se kot rezultat prikažejo gumbi za dostop do ustreznih urejevalniških oken (Editorjev) za nadaljevanje gradnje projekta.

Gumbi za dostop do urejevalniških oken &amestitveni projekt (Setup project) Okno z lastnostmi namestitvenega projekta

Če npr. v Solution Exlorerju kliknemo gumb File System Editor, se tako kot na zgornji sliki odpre okno File System Editor. Editor sedaj lahko uporabimo za dodajanje potrebnih datotek v naš Setup projekt. To storimo tako, da izberemo ustrezno mapo, kliknemo desni miškin gumb in izberemo ustrezno opcijo podmenija Add. Za dodajanje temeljne output datoteke Menjalnica.exe v namestitveno mapo, izberemo mapo Application Folder, nato desni miškin gumb in še Add -> Project Output. Odpre se pogovorno okno Add Project Output Group (tako kot na zgornji sliki). Izberemo npr. opcijo Primary Output, poleg tega pa lahko izberemo še druge datoteke, kot npr. datoteko ReadMe ( Add -> File, nato pa izberemo ustrezno datoteko).

Page 277: PROŠNJA - uranic.tsckr.siuranic.tsckr.si/VISUAL C#/VISUAL C#.pdf · MICROSOFT VISUAL C# .NET Srečo Uranič . Visual C# . ... Za začetek novega projekta v okolju Visual C# Standard

Visual C# . NET

Algoritmi in programski jeziki – 4.letnik

277

Podobno tudi ostali urejevalniki ponujajo dodajanje funkcij in dodatkov naš namestitveni program. Če je potrebno, lahko npr. uporabimo Registry Editor ( s klikom na gumb Registry Editor v Solution Explorerju) ter določimo ključe in vrednosti, ki naj se ob uporabi našega namestitvenega programa dodajo v register ciljnega računalnika. Uporabimo lahko npr. tudi File Types Editor, za vzpostavitev povezave med datotečno ekstenzijo in samo aplikacijo na ciljnem računalniku. Urejevalnik User Interface Editor omogoča izdelavo uporabnikom prijaznih pogovornih oken, ki se pojavijo na ekranu ciljnega računalnika ob uporabi našega Setup programa. Spremenimo lahko npr. lastnost BannerBitMap, kateregakoli pogovornega okna za prikaz našega logotipa v tem oknu. Ta urejevalnik omogoča tudi spremembo teksta, ki je prikazan v namestitvenih pogovornih oknih in omogoča dodajanje naših lastnih pogovornih oken k že obstoječim v namestitvenem projektu. Urejevalnik Custom Actions Editor omogoča dodajanje novih procesov v namestitveni program. Dodamo lahko npr. proces za dostop do neke SQL Server podatkovne datoteke, ki se bo izvedel med samo instalacijo. Urejevalnik Launch Condition Editor omogoča nastavljanje pogojev, ki morajo biti izpolnjeni, preden se namestitev na ciljnem računalniku lahko sploh prične. Uporabimo lahko npr. pogoj Launch Condition za preverjanje operacijskega sistema oz. za preverjanje, ali neka datoteka na ciljnem računalniku že obstaja. Če nastavljeni pogoji niso izpolnjeni, bo namestitveni program prekinil namestitev. Poleg uporabe namestitvenih urejevalnikov, pa lahko nastavljamo tudi lastnosti namestitvenega projekta. Med temi lastnostmi sta najpogostejši lastnosti Manufacturer in Product&ame, ker sta uporabljeni za kreiranje poti pri privzeti namestitvi na ciljnem računalniku. Vnesimo za lastnost Manufacturer npr. TŠC Kranj (to pomeni, da se bo naša aplikacija na uporabnikovem računalniku namestila na disk C, v mapo Program Files -> TŠC Kranj) , lastnost Author opremimo s svojim imenom, lastnost Product&ame pa je tako ali tako že nastavljena tako, kot smo na začetku poimenovali naš namestitveni projekt. Poleg tega bo ob namestitvi na uporabnikovem računalniku v Start meni dodana bližnjica za zagon aplikacije, možna pa bo tudi odstranitev te aplikacije preko opcije Dodaj ali odstrani programe v &adzorni plošči (Control Panel). Za dokončno kreiranje namestitvene datoteke Setup.exe v Solution Explorerju izberimo naš namestitveni projekt (Menjalnica_Setup) in izberimo opcijo Build->Build Menjalnica_Setup. Pred tem lahko seveda še določimo verzijo našega novega projekta. Namestitveni projekt je tako pripravljen – nahaja se v mapi Release, ki jo sedaj lahko skopiramo na nek strežnih ali pa npr. zapečemo na nek CD oz. DVD. Uporabnik bo lahko naš projekt namestil tako, da bo na svojem računalniku pognal datoteko Setup.exe, ki se nahaja v tej mapi.

Uporaba namestitvenega (Setup) programa za namestitev aplikacije pri uporabniku

Ko uporabnik na svojem računalniku zažene namestitveni program ( s klikom na Setup.exe), se odpre standardno namestitveno pogovorno okno. V oknu je prikazano privzeto pozdravno besedilo, ki pa bi ga ob kreiranju namestitvene datoteke s pomočjo User Interface Editorja lahko oblikovali po svoje.

Page 278: PROŠNJA - uranic.tsckr.siuranic.tsckr.si/VISUAL C#/VISUAL C#.pdf · MICROSOFT VISUAL C# .NET Srečo Uranič . Visual C# . ... Za začetek novega projekta v okolju Visual C# Standard

Visual C# . NET

Algoritmi in programski jeziki – 4.letnik

278

Drugi korak namestitve prikaže privzeto namestitveno mapo, kamor se bo naša aplikacija namestila. Seveda lahko uporabnik določi svojo mapo, kamor želi namestiti našo aplikacijo.

V naslednjem koraku še potrdimo nastavitve oz se po želji lahko vračamo na prejšnje korake. Sledita pa še dve

okni z obvestilom, da je namestitev končana in da lahko nameščeno aplikacijo pričnemo uporabljati. Bližnjica do nje je umeščena tudi v meniju Start -> Programi.

Prikazali smo le najosnovnejšo izdelavo namestitvenega projekta. Seveda pa, v kolikor gre za aplikacijo, ki dela za bazami podatkov, je potrebno še preveriti, če ima uporabnik dostop do te baze.

Page 279: PROŠNJA - uranic.tsckr.siuranic.tsckr.si/VISUAL C#/VISUAL C#.pdf · MICROSOFT VISUAL C# .NET Srečo Uranič . Visual C# . ... Za začetek novega projekta v okolju Visual C# Standard

Visual C# . NET

Algoritmi in programski jeziki – 4.letnik

279

Dokumentiranje

Dokumentiranje razredov Kot že vemo, lahko splošne komentarje v našo kodo dodajamo tako, da stavke, s komentarjem začnemo s parom znakov //, npr.: //Tole je komentar

Pri pisanju novih razredov in njihovih članov pa lahko uporabljamo tudi XML dokumentacijo. XML dokumentacija bo naše razrede naredila razumljivejše za ostale razvijalce, ki bodo mogoče nadaljevali z našim projektom, ali pa samo uporabljali razred, ki smo ga napisali. Tudi za nas same je takšno pisanje dokumentacije koristno, saj če imamo veliko lastnih razredov, bomo s časoma pozabili njihov namen in možnosti uporabe, s pomočjo XML komentarjev pa bomo njihovo uporabo in namen hitro obnovili. Informacija ki jih bomo napisali kot XML dokumentacijo, se bo tako ob uporabi našega razreda in ustrezne kombinacije tipk (glej poglavje Pisanje programske kode z uporabo IntelliSense tehnologije) prikazala na ekranu in sicer v okvirčku pod imenom razreda ali metode. Čeprav je XML dokumentacija bazirana na XML sintaksi, nam ni potrebno podrobno poznavanje XML-a da bi ga lahko uporabljali. Vedeti moramo le, da se vrstica z XML dokumentacijo prične s tremi znaki /// (tremi slash-i), nahajati pa se morajo takoj za razredom oz. njegovim članom, ki ga želimo dokumentirati. Dokumentacija za razred ali člana razreda lahko vsebuje enega ali več elementov dokumentiranja. Vsak element se prične z oznako za začetek, npr. <summary> in se konča z oznaka za konec elementa, npr. </summary>. Vsebina tega elementa dokumentacije se nahaja med začetno in končno oznako. Tabela najpogostejših elementov XML dokumentacije

XML Razlaga

<summary> Zagotavljanje temeljne razlage o nekem razredu, lastnosti, metodi ali drugem elementu.

<value> Uporablja se za opis oz. razlago vrednosti neke lastnosti.

<returns> Uporablja se za opis oz. razlago vrnjene vrednosti neke metode.

<param name>="name" Uporablja se za opis oz. razlago parametrov metode.

Najlažji način za kreiranje XML dokumentacije je, da v prvi vrstici, ki sledi najavi novega razreda vtipkamo trojno poševnico oz. slash (///). Visual Studio nam nato avtomatsko naredi ogrodje XML dokumentacije, vključno s tem, ali je oznaka primerna za člana razreda, ki ga dokumentiramo. Če npr. vtipkamo /// v prazni vrstici pred članom razreda, ki ima parametre in vrača nek rezultat, bo Visual Studio tudi avtomatično generiral oznako <summary> za to metodo, oznako <param name =…> za posamezne parametre te metode in oznako <return> za vrednost, ki jo metoda vrača. Nato lahko med oznakami za začetek in konec nekega elementa XML dokumentacije pričnemo s tipkanjem opisa posamezne sekcije. Vaja: Vaja XML Dokumentacija demonstrira primer XML dokumentacije razredov. Na obrazcu sta le dve vnosni

polji: v prvo naj bi uporabnik vnesel poljuben tekst, ki pa ne sme biti prazen, v drugo polje pa naj bi vnesel neko decimalno število. Po kliku na gumb Preveri pravilnost vnosa bomo s pomočjo dveh statičnih metod razreda Validator preverili pravilnost uporabnikovega vnosa. Razred Validator smo napisali sami in ga opremili z XML dokumentacijo.

Page 280: PROŠNJA - uranic.tsckr.siuranic.tsckr.si/VISUAL C#/VISUAL C#.pdf · MICROSOFT VISUAL C# .NET Srečo Uranič . Visual C# . ... Za začetek novega projekta v okolju Visual C# Standard

Visual C# . NET

Algoritmi in programski jeziki – 4.letnik

280

/// <summary> /// Vsebuje statične metode za kontrolo pravilnosti uporabnikovega vnosa /// </summary> public class Validator { public Validator()//konstruktor { } /// <summary> /// Besedilo, ki se bo izpisalo NA pogovornem oknu pri obvestilu o napačnem vnosu /// </summary> public static string Naslov="Napaka pri vnosu!"; //IsPresent je statična metoda, zato jo lahko kličeno kar s pomočjo razreda /// <summary> /// Preverja, če je uporabnik vnesel podatke v tekstovno polje /// </summary> /// <param name="textBox">Tekstovno polje, ki se preverja</param> /// <returns>True (v redu), če je uporabnik vnesel podatke</returns> public static bool IsPresent(TextBox textBox) { if (textBox.Text=="") { //Predpostavimo, da je pomen vnosnega polja zapisan v lastnosti Tag MessageBox.Show("Polje "+textBox.Tag+" ne sme biti prazno!",Naslov); textBox.Focus(); return false; } return true; } . . .

Ko smo XML dokumentacijo dodali v naš razred, jo bo Visual Studio uporabil npr. tedaj, ko bo uporabnik zapisal ime razreda, ime metode, ipd, ali pa se le z miško zapeljal prek kode. V prvem primeru, ko vtipkamo ime razreda Validator in za njim še znak pika, IntelliSense prikaže seznam

članov razreda Validator. Med njimi je tudi metoda IsPresent, ki smo jo napisali, ob njej pa se v okvirčku pojavi okno z opisano dokumentacijo za ta razred (ki smo jo napisali s pomočjo XML-a).

Ko uporabnik izbere metodo IsPresent in za njo zapiše oklepaj, Visual Studio v okvirčku pod metodo izpiše razlago za parameter te metode, tako kot smo jo napisali s pomočjo XML dokumentacije.

Informacijo o samem razredu pa lahko uporabnik dobi, če se s kazalnikom miške postavi kamorkoli na besedico Validator (ime razreda) v kodi.

Page 281: PROŠNJA - uranic.tsckr.siuranic.tsckr.si/VISUAL C#/VISUAL C#.pdf · MICROSOFT VISUAL C# .NET Srečo Uranič . Visual C# . ... Za začetek novega projekta v okolju Visual C# Standard

Visual C# . NET

Algoritmi in programski jeziki – 4.letnik

281

Kreiranje in uporaba knjižnice razreda (class library) Doslej so bili vsi razredi, ki smo jih napisali samo sestavni del našega okenskega projekta. Če pa hočemo razred, ki smo ga napisali, uporabiti v dveh ali pa v več projektih, ga bomo morali shraniti v knjižnico vseh razredov (class library). Poenostavljeno rečeno, class library vsebuje zbirko sorodnih razredov. Z uporabo razrednih knjižnic, lahko kateregakoli od razredov iz te knjižnice uporabimo v našem projektu, ne da bi ga bilo potrebno kopirati v projekt.

Kako delujejo knjižnice razredov

Razliko med uporabo razredov kreiranih znotraj Windows aplikacije in razredi kreiranimi v knjižnici razredov prikazujejo naslednje slike: Dva različna projekta uporabljata razred Validator, ki smo ga napisali v prejšnjem projektu:

Projekt1 Projekt2

Form1 (Form class)

Validator (Validation class)

Form2 (Form class)

Validator (Validation class)

Dva različna projekta uporabljata razred Validator preko knjižnice razredov

Projekt1 Knižnica Projekt2

Form1

(Form class)

Validator

(Validation class)

Form1

(Form class)

Kot vidimo s slike, moramo razrede, ki so kreirani v okenskem projektu vstaviti v vsak projekt, ki te razrede uporablja. Razredi, ki so kreirani v knjižnici razredov, pa obstajajo izven vseh projektov, ki te rezrede uporabljajo. Zaradi tega so dostopni vsem tistim projektom, ki imajo dostop do te knjižnice. Ena od prednosti uporabe knjižnic je tudi ta, da z uporabo razrednih knjižnic naši projekti postanejo krajši, saj v tem primeru naš projekt vsebuje le referenco na knjižnico razredov, ne pa tudi kodo, ki je v splošnem lahko zelo obsežna. Ker pa so razredi v knjižnici razredov že prevedeni, jih Visual Studio ne prevaja vsakič, ko prevajamo našo aplikacijo, s tem pa je čas prevajanja seveda krajši. Druga prednost uporabe knjižnic pa je seveda njihova preprosta uporaba in nadgradnja. Če je potrebno narediti spremembo v nekem razredu v knjižnici, moramo narediti spremembo le v knjižnici – ko je le ta narejena, je tako modificirana knjižnica takoj dostopna vsem projektom, ki to knjižnico uporabljajo. Največja prednost uporabe razrednih knjižnic pa je verjetno ta, ki smo jo omenili na začetku. Ko je razred enkrat napisan in shranjen v knjižnico, ga lahko uporabimo v naših novih projektih, ki potrebujejo podobne funkcije. V

Page 282: PROŠNJA - uranic.tsckr.siuranic.tsckr.si/VISUAL C#/VISUAL C#.pdf · MICROSOFT VISUAL C# .NET Srečo Uranič . Visual C# . ... Za začetek novega projekta v okolju Visual C# Standard

Visual C# . NET

Algoritmi in programski jeziki – 4.letnik

282

večini primerov bomo razrede uporabljali neposredno, bolj zahtevni pa jih bodo v svojih projektih nadgradili z novimi lastnostmi in metodami.

Kako kreiramo knjižnico razredov

Novo knjižnico razredov kreiramo tako, da namesto kreiranja novega projekta izberemo kreiranje nove knjižnice razredov (File -> &ew Project nato pa izberemo opcijo Class Library). Visual Studio kreira projekt za novo knjižnico, ki pa na začetku vsebuje en sam razred imenovan Class1. V tem projektu potem lahko zapišemo kodo za ta razred. Če imamo kodo razreda že nekje zapisano, lahko razred Class1 pobrišemo in na njegovo mesto skopiramo obstoječega ( Project -> Add Existing Item). Pri kreiranju nove knjižnice razredov je pomembno tudi to, kako bomo poimenovali imenski prostor za naš razred. Ko postavljamo razrede v imenski prostor, je priporočljivo, da imenski prostor poimenujemo z imenom, ki ponazarja pomen razredov, ki jih vsebuje. V primeru, da razredi, ki jih vključujemo v knjižnico, potrebujejo dostop do drugih imenskih prostorov kot je osnovni imenski prostor System, je take imenske prostore potrebno navesti na začetku knjižnice ( using …). Ko smo s kreiranjem knjižnice razredov končali (napisali in kompletirali smo vse razrede znotraj te knjižnice), moramo projekt prevesti da dobimo njegovo sestavljenko (assembly). Rezultat prevajanja je dinamična knjižnica tipa DLL (Dynamic Link Library), to je datoteka s končnico dll, ki vsebuje izvršilno kodo za knjižnico razredov. Datoteka je shranjena v mapi Bin\Debug, znotraj mape z imenom razrednega projekta. V svoje nove projekte lahko sedaj to knjižnico dodamo tako, da v novem projektu izberemo opcijo Project -> AddReference… , nato pa v pogovornem oknu kliknemo na jeziček Browse in poiščemo knjižnico razredov, ki jo potrebujemo. Ko jo najdemo, jo izberemo in kliknemo gumb OK. Razredi iz te knjižnice so nam sedaj na voljo v našem novem projektu, v katerega moramo le še dodati ustrezen using stavek, ali pa se na razred iz knjižnice sklicujemo z imenom knjižnice, ki mu sledi znak pika in nato še ime razreda.

Page 283: PROŠNJA - uranic.tsckr.siuranic.tsckr.si/VISUAL C#/VISUAL C#.pdf · MICROSOFT VISUAL C# .NET Srečo Uranič . Visual C# . ... Za začetek novega projekta v okolju Visual C# Standard

Visual C# . NET

Algoritmi in programski jeziki – 4.letnik

283

Pomoč Spoznali smo nekaj osnov pri delu z Visual C#. Pri nadaljnjem delu bomo seveda rabili še veliko pomoči, ki nam jo okolje Visual C# ponuja v meniju Help, ali pa pomoč poiščemo preprosto tako, da z miško kliknemo na besedo, za katero rabimo pomoč in nato na tipkovnici pritisnemo tipko F1. Če pa nam pomoč v meniju Help ali pa F1 še ne zadoščata, imamo na voljo številne msdn forume!

Dodatek 1: Kode tipk (KeyCode) na tipkovnici

Ikona označuje tipke, ki jih podpira .NET Compact Framework. Zaradi enostavnosti razlaga posamezne tipke ni prevedena!

Oznaka tipke Razlaga

A The A key.

Add The add key.

Alt The ALT modifier key.

Apps The application key (Microsoft &atural Keyboard).

Attn The ATT& key.

B The B key.

Back The BACKSPACE key.

BrowserBack The browser back key (Windows 2000 or later).

Page 284: PROŠNJA - uranic.tsckr.siuranic.tsckr.si/VISUAL C#/VISUAL C#.pdf · MICROSOFT VISUAL C# .NET Srečo Uranič . Visual C# . ... Za začetek novega projekta v okolju Visual C# Standard

Visual C# . NET

Algoritmi in programski jeziki – 4.letnik

284

BrowserFavorites The browser favorites key (Windows 2000 or later).

BrowserForward The browser forward key (Windows 2000 or later).

BrowserHome The browser home key (Windows 2000 or later).

BrowserRefresh The browser refresh key (Windows 2000 or later).

BrowserSearch The browser search key (Windows 2000 or later).

BrowserStop The browser stop key (Windows 2000 or later).

C The C key.

Cancel The CA&CEL key.

Capital The CAPS LOCK key.

CapsLock The CAPS LOCK key.

Clear The CLEAR key.

Control The CTRL modifier key.

ControlKey The CTRL key.

Crsel The CRSEL key.

D The D key.

D0 The 0 key.

D1 The 1 key.

D2 The 2 key.

D3 The 3 key.

D4 The 4 key.

D5 The 5 key.

D6 The 6 key.

D7 The 7 key.

D8 The 8 key.

D9 The 9 key.

Page 285: PROŠNJA - uranic.tsckr.siuranic.tsckr.si/VISUAL C#/VISUAL C#.pdf · MICROSOFT VISUAL C# .NET Srečo Uranič . Visual C# . ... Za začetek novega projekta v okolju Visual C# Standard

Visual C# . NET

Algoritmi in programski jeziki – 4.letnik

285

Decimal The decimal key.

Delete The DEL key.

Divide The divide key.

Down The DOW& ARROW key.

E The E key.

End The E&D key.

Enter The E&TER key.

EraseEof The ERASE EOF key.

Escape The ESC key.

Execute The EXECUTE key.

Exsel The EXSEL key.

F The F key.

F1 The F1 key.

F10 The F10 key.

F11 The F11 key.

F12 The F12 key.

F13 The F13 key.

F14 The F14 key.

F15 The F15 key.

F16 The F16 key.

F17 The F17 key.

F18 The F18 key.

F19 The F19 key.

F2 The F2 key.

F20 The F20 key.

Page 286: PROŠNJA - uranic.tsckr.siuranic.tsckr.si/VISUAL C#/VISUAL C#.pdf · MICROSOFT VISUAL C# .NET Srečo Uranič . Visual C# . ... Za začetek novega projekta v okolju Visual C# Standard

Visual C# . NET

Algoritmi in programski jeziki – 4.letnik

286

F21 The F21 key.

F22 The F22 key.

F23 The F23 key.

F24 The F24 key.

F3 The F3 key.

F4 The F4 key.

F5 The F5 key.

F6 The F6 key.

F7 The F7 key.

F8 The F8 key.

F9 The F9 key.

FinalMode The IME final mode key.

G The G key.

H The H key.

HanguelMode The IME Hanguel mode key. (maintained for compatibility; use HangulMode)

HangulMode The IME Hangul mode key.

HanjaMode The IME Hanja mode key.

Help The HELP key.

Home The HOME key.

I The I key.

IMEAccept The IME accept key, replaces IMEAceept.

IMEAceept The IME accept key. Obsolete, use IMEAccept instead.

IMEConvert The IME convert key.

IMEModeChange The IME mode change key.

IME&onconvert The IME nonconvert key.

Page 287: PROŠNJA - uranic.tsckr.siuranic.tsckr.si/VISUAL C#/VISUAL C#.pdf · MICROSOFT VISUAL C# .NET Srečo Uranič . Visual C# . ... Za začetek novega projekta v okolju Visual C# Standard

Visual C# . NET

Algoritmi in programski jeziki – 4.letnik

287

Insert The I&S key.

J The J key.

JunjaMode The IME Junja mode key.

K The K key.

KanaMode The IME Kana mode key.

KanjiMode The IME Kanji mode key.

KeyCode The bitmask to extract a key code from a key value.

L The L key.

LaunchApplication1 The start application one key (Windows 2000 or later).

LaunchApplication2 The start application two key (Windows 2000 or later).

LaunchMail The launch mail key (Windows 2000 or later).

LButton The left mouse button.

LControlKey The left CTRL key.

Left The LEFT ARROW key.

LineFeed The LI&EFEED key.

LMenu The left ALT key.

LShiftKey The left SHIFT key.

LWin The left Windows logo key (Microsoft &atural Keyboard).

M The M key.

MButton The middle mouse button (three-button mouse).

Media&extTrack The media next track key (Windows 2000 or later).

MediaPlayPause The media play pause key (Windows 2000 or later).

MediaPreviousTrack The media previous track key (Windows 2000 or later).

MediaStop The media Stop key (Windows 2000 or later).

Menu The ALT key.

Page 288: PROŠNJA - uranic.tsckr.siuranic.tsckr.si/VISUAL C#/VISUAL C#.pdf · MICROSOFT VISUAL C# .NET Srečo Uranič . Visual C# . ... Za začetek novega projekta v okolju Visual C# Standard

Visual C# . NET

Algoritmi in programski jeziki – 4.letnik

288

Modifiers The bitmask to extract modifiers from a key value.

Multiply The multiply key.

& The & key.

&ext The PAGE DOW& key.

&o&ame A constant reserved for future use.

&one &o key pressed.

&umLock The &UM LOCK key.

&umPad0 The 0 key on the numeric keypad.

&umPad1 The 1 key on the numeric keypad.

&umPad2 The 2 key on the numeric keypad.

&umPad3 The 3 key on the numeric keypad.

&umPad4 The 4 key on the numeric keypad.

&umPad5 The 5 key on the numeric keypad.

&umPad6 The 6 key on the numeric keypad.

&umPad7 The 7 key on the numeric keypad.

&umPad8 The 8 key on the numeric keypad.

&umPad9 The 9 key on the numeric keypad.

O The O key.

Oem1 The OEM 1 key.

Oem102 The OEM 102 key.

Oem2 The OEM 2 key.

Oem3 The OEM 3 key.

Oem4 The OEM 4 key.

Oem5 The OEM 5 key.

Oem6 The OEM 6 key.

Page 289: PROŠNJA - uranic.tsckr.siuranic.tsckr.si/VISUAL C#/VISUAL C#.pdf · MICROSOFT VISUAL C# .NET Srečo Uranič . Visual C# . ... Za začetek novega projekta v okolju Visual C# Standard

Visual C# . NET

Algoritmi in programski jeziki – 4.letnik

289

Oem7 The OEM 7 key.

Oem8 The OEM 8 key.

OemBackslash The OEM angle bracket or backslash key on the RT 102 key keyboard (Windows 2000 or later).

OemClear The CLEAR key.

OemCloseBrackets The OEM close bracket key on a US standard keyboard (Windows 2000 or later).

Oemcomma The OEM comma key on any country/region keyboard (Windows 2000 or later).

OemMinus The OEM minus key on any country/region keyboard (Windows 2000 or later).

OemOpenBrackets The OEM open bracket key on a US standard keyboard (Windows 2000 or later).

OemPeriod The OEM period key on any country/region keyboard (Windows 2000 or later).

OemPipe The OEM pipe key on a US standard keyboard (Windows 2000 or later).

Oemplus The OEM plus key on any country/region keyboard (Windows 2000 or later).

OemQuestion The OEM question mark key on a US standard keyboard (Windows 2000 or later).

OemQuotes The OEM singled/double quote key on a US standard keyboard (Windows 2000 or later).

OemSemicolon The OEM Semicolon key on a US standard keyboard (Windows 2000 or later).

Oemtilde The OEM tilde key on a US standard keyboard (Windows 2000 or later).

P The P key.

Pa1 The PA1 key.

Packet

Used to pass Unicode characters as if they were keystrokes. The Packet key value is the low word of a 32-bit virtual-key value used for non-keyboard input methods.

PageDown The PAGE DOW& key.

PageUp The PAGE UP key.

Pause The PAUSE key.

Play The PLAY key.

Page 290: PROŠNJA - uranic.tsckr.siuranic.tsckr.si/VISUAL C#/VISUAL C#.pdf · MICROSOFT VISUAL C# .NET Srečo Uranič . Visual C# . ... Za začetek novega projekta v okolju Visual C# Standard

Visual C# . NET

Algoritmi in programski jeziki – 4.letnik

290

Print The PRI&T key.

PrintScreen The PRI&T SCREE& key.

Prior The PAGE UP key.

ProcessKey The PROCESS KEY key.

Q The Q key.

R The R key.

RButton The right mouse button.

RControlKey The right CTRL key.

Return The RETUR& key.

Right The RIGHT ARROW key.

RMenu The right ALT key.

RShiftKey The right SHIFT key.

RWin The right Windows logo key (Microsoft &atural Keyboard).

S The S key.

Scroll The SCROLL LOCK key.

Select The SELECT key.

SelectMedia The select media key (Windows 2000 or later).

Separator The separator key.

Shift The SHIFT modifier key.

ShiftKey The SHIFT key.

Sleep The computer sleep key.

Snapshot The PRI&T SCREE& key.

Space The SPACEBAR key.

Subtract The subtract key.

T The T key.

Page 291: PROŠNJA - uranic.tsckr.siuranic.tsckr.si/VISUAL C#/VISUAL C#.pdf · MICROSOFT VISUAL C# .NET Srečo Uranič . Visual C# . ... Za začetek novega projekta v okolju Visual C# Standard

Visual C# . NET

Algoritmi in programski jeziki – 4.letnik

291

Tab The TAB key.

U The U key.

Up The UP ARROW key.

V The V key.

VolumeDown The volume down key (Windows 2000 or later).

VolumeMute The volume mute key (Windows 2000 or later).

VolumeUp The volume up key (Windows 2000 or later).

W The W key.

X The X key.

XButton1 The first x mouse button (five-button mouse).

XButton2 The second x mouse button (five-button mouse).

Y The Y key.

Z The Z key.

Zoom The ZOOM key.

Literatura: Microsoft Visual C# 2008 Step by Step, John Sharp, izdano leta 2008 Microsoft Visual C# 2005 Step by Step, John Sharp, izdano leta 2005 Microsoft Visual C# 2003 Step by Step, John Sharp, izdano leta 2003 Murach's C# 2005 Training & reference, Joel Murach, Mike Murach & Associates Inc. 2006 Microsoft Visual C# .NET, Mickey Williams, izdano leta 2002

Page 292: PROŠNJA - uranic.tsckr.siuranic.tsckr.si/VISUAL C#/VISUAL C#.pdf · MICROSOFT VISUAL C# .NET Srečo Uranič . Visual C# . ... Za začetek novega projekta v okolju Visual C# Standard

Visual C# . NET

Algoritmi in programski jeziki – 4.letnik

292

Kazalo vsebine

Kaj je C# in .&ET Framework............................................................................................................................. 2

Izdelava konzolnih aplikacij v VISUAL C# . &ET............................................................................................. 3

Začenjanje novega projekta v različici Express Edition..................................................................................... 3

Začenjanje novega projekta v različici Standard Edition................................................................................... 3

Pisanje programske kode z uporabo IntelliSense tehnologije ............................................................................ 4

Prevajanje in zagon projekta.............................................................................................................................. 6

�apake pri prevajanju (Compile Time Error) in napake pri izvajanju (Run Time Error) ................................. 8

Imenski prostori ................................................................................................................................................ 10

Komentarji ........................................................................................................................................................ 11

MICROSOFT Visual C# 2008 - OS&OV&I POJMI: Rešitve (Solutions) in projekti (projects) ................. 13

Okenske aplikacije (Windows Forms) v Visual C# . &ET 2008 Express Edition ......................................... 15

Kreiranje osnovne okenske aplikacije v okolju Visual C# ................................................................................ 15

Preimenovanje obrazca (forme) ....................................................................................................................... 20

Preimenovanje imen odzivnih dogodkov .......................................................................................................... 21

Glavne datoteke, ki sestavljajo okenski projekt v Visual C#............................................................................. 21

Identifikatorji, spremenljivke, operatorji in izrazi v Visual C# . &ET........................................................... 23

Osnovni podatkovni tipi v C# ........................................................................................................................... 23

Operatorji ......................................................................................................................................................... 28

Pretvarjanje osnovnih podatkovnih tipov ......................................................................................................... 28

Standardne oznake/kode za formatiranje števil ................................................................................................ 28

Običajne (custom) oznake/kode za formatiranje števil ..................................................................................... 29

Formatiranje objektov tipa DateTime .............................................................................................................. 33

Zanka foreach................................................................................................................................................... 34

Rezervirana beseda params.............................................................................................................................. 38

Delo z zbirkami – Collections (ponovitev) ......................................................................................................... 39

�etipizirane zbirke............................................................................................................................................ 39

Tipizirane zbirke ............................................................................................................................................... 40

Varovalni bloki – obravnava napak in izjem (Exceptions) .............................................................................. 43

Blok za obravnavo prekinitev: try … catch ….................................................................................................. 43

Večkratni varovalni blok za obravnavo prekinitev try … catch … catch …..................................................... 44

Hierarhija izjem................................................................................................................................................ 46

Brezpogojni varovalni blok try … finally …..................................................................................................... 46

Uporaba rezerviranih besed Checked in Unchecked........................................................................................ 47

Pisanje lastnih izjem – uporaba rezervirane besede throw .............................................................................. 47

Page 293: PROŠNJA - uranic.tsckr.siuranic.tsckr.si/VISUAL C#/VISUAL C#.pdf · MICROSOFT VISUAL C# .NET Srečo Uranič . Visual C# . ... Za začetek novega projekta v okolju Visual C# Standard

Visual C# . NET

Algoritmi in programski jeziki – 4.letnik

293

Pisanje lastnih metod v Visual C#...................................................................................................................... 51

Deklaracija in definicija metode....................................................................................................................... 51

Klic metode ....................................................................................................................................................... 52

Bloki (ponovitev/nadgradnja iz osnov jezika C# oz. C++)............................................................................... 54

Bloki znotraj razreda ........................................................................................................................................ 55

Razredi in objekti ................................................................................................................................................ 58

Razred - class.................................................................................................................................................... 58 Enkapsulacija ............................................................................................................................................... 58 Konstruktor .................................................................................................................................................. 62 Preobloženi (overloaded) konstruktorji........................................................................................................ 64 Kopirni (copy) konstruktorji ........................................................................................................................ 65 Statični (static) konstruktorji........................................................................................................................ 65 Polja tipa readonly ....................................................................................................................................... 66 Destruktorji .................................................................................................................................................. 66 Dogovor o poimenovanju ............................................................................................................................ 67 Lastnost (Property) ...................................................................................................................................... 71 Statične metode............................................................................................................................................ 75 Statična polja................................................................................................................................................ 76 Indekserji - Indexers .................................................................................................................................... 77

Dedovanje (Inheritance) – izpeljani razredi..................................................................................................... 80 Kaj je to dedovanje ...................................................................................................................................... 80 Bazični razredi in izpeljani razredi .............................................................................................................. 81 Klic konstruktorja bazičnega razreda........................................................................................................... 81 Določanje oz. prirejanje razredov ................................................................................................................ 82 Nove metode – operator new ....................................................................................................................... 82 Virtualne metode.......................................................................................................................................... 84 Prekrivne (Override) metode ...................................................................................................................... 85

Osnova vseh razredov - Object......................................................................................................................... 88

Boxing in Unboxing .......................................................................................................................................... 88

Polimorfizem - mnogoličnost ............................................................................................................................ 90 Uporaba označevalca protected ................................................................................................................... 91

Vmesnik – Interface .......................................................................................................................................... 91 Sintaksa in pomen vmesnikov...................................................................................................................... 91 Implementacija vmesnikov .......................................................................................................................... 92 Delo z več vmesniki..................................................................................................................................... 92

Abstraktni razredi in abstraktne metode........................................................................................................... 94

Zapečateni razredi (Sealed classes) ................................................................................................................. 96

Gnezdeni razredi............................................................................................................................................... 96

Operatorja is in as............................................................................................................................................ 97

Prekrivanje operatorjev - uporaba rezervirane besede operator ..................................................................... 98

Delegati ............................................................................................................................................................ 99 Uporaba delegatov ....................................................................................................................................... 99 Deklaracija delegata................................................................................................................................... 100 Anonimne metode in delegati .................................................................................................................... 104

Dogodki (events)............................................................................................................................................. 104 Deklaracija dogodka .................................................................................................................................. 105 Naročanje (subscribing) na nek dogodek................................................................................................... 105 Klic oz. zagon nekega dogodka ................................................................................................................. 105 Odjavljanje (unsubscribing) dogodka ........................................................................................................ 107

Page 294: PROŠNJA - uranic.tsckr.siuranic.tsckr.si/VISUAL C#/VISUAL C#.pdf · MICROSOFT VISUAL C# .NET Srečo Uranič . Visual C# . ... Za začetek novega projekta v okolju Visual C# Standard

Visual C# . NET

Algoritmi in programski jeziki – 4.letnik

294

Razumevanje dogodkov grafičnega uporabniškega vmesnika ( GUI - Graphical User Interface) ............ 107

Knjižnice razredov in datoteka z razredi ........................................................................................................ 110 Ustvarjanje nove knjižnice razredov.......................................................................................................... 110 Uporaba knjižnjice razredov ...................................................................................................................... 111 Datoteka z razredom (ali več razredi) ........................................................................................................ 112 Povzetek..................................................................................................................................................... 115

Dogodki .............................................................................................................................................................. 116

Sistemski dogodki ........................................................................................................................................... 116

Dogodki ki jih sproži tipkovnica ..................................................................................................................... 117

Dogodki ki jih sproži miška ............................................................................................................................ 120 Pozicija miškinega kazalca ........................................................................................................................ 120 Dogodki miške........................................................................................................................................... 120

Parameter Sender ........................................................................................................................................... 121

Dogodki ki jih sproži ura (gradnik Timer) in metode za delo s časom ........................................................... 126 Delo z datumi in časom.............................................................................................................................. 126

Projekt z več obrazci – metodi Show in ShowDialog...................................................................................... 136

Vključevanje nemodalnih obrazcev v projekt ................................................................................................. 136

Vključevanje modalnih obrazcev v projekt ..................................................................................................... 137

Vključevanje že obstoječih obrazcev in knjižnic v rešitev............................................................................... 140 Vključevanje projekta, obrazca ali knjižnice v rešitev............................................................................... 140 Odstranjevanje projekta, obrazca ali knjižnice iz rešitve ........................................................................... 140 Sporočilno okno AboutBox ....................................................................................................................... 141

Dostop do polj, metod in gradnikov drugega obrazca.................................................................................... 143 Dostop do polj in metod generiranih v razredu drugega obrazca............................................................... 143 Dostop do gradnikov na drugem obrazcu .................................................................................................. 144

Delo z enostavnimi in sestavljenimi meniji (nadaljevanje), orodjarna ......................................................... 151

Glavni meni – MenuStrip (ponovitev + nadgradnja)...................................................................................... 151

Lebdeči (Pop Up) meni - ContextMenuStrip .................................................................................................. 152

Orodjarna - ToolStrip..................................................................................................................................... 153

Sporočilna okna – okno MessageBox............................................................................................................... 157

Delo z datotekami in podatkovnimi tokovi...................................................................................................... 163

Imenski prostor (knjižnjica) System.IO........................................................................................................... 163

Razredi imenskega prostora System.IO za delo z imeniki, datotekami in potmi do datotek ........................... 163 Najpomembnejše metode razreda Directory.............................................................................................. 163 Imena datotek............................................................................................................................................. 163 Najpomembnejše metode razreda File ....................................................................................................... 164

Delo s podatkovnimi tokovi ............................................................................................................................ 165

Enostavno delo s podatkovnimi tokovi............................................................................................................ 167 Ustvarjanje nove datoteke.......................................................................................................................... 167 Pisanje v tekstovno datoteko...................................................................................................................... 167 Dodajanje podatkov v tekstovno datoteko ................................................................................................. 168 Branje tekstovnih datotek .......................................................................................................................... 168

�apredno delo s podatkovnimi tokovi ............................................................................................................ 169 Uporaba razreda FileStream....................................................................................................................... 169

Page 295: PROŠNJA - uranic.tsckr.siuranic.tsckr.si/VISUAL C#/VISUAL C#.pdf · MICROSOFT VISUAL C# .NET Srečo Uranič . Visual C# . ... Za začetek novega projekta v okolju Visual C# Standard

Visual C# . NET

Algoritmi in programski jeziki – 4.letnik

295

Delo s tekstovnimi datotekami ........................................................................................................................ 171 Pisanje podatkov v tekstovno datoteko ...................................................................................................... 171 Branje podatkov iz tekstovne datoteke ...................................................................................................... 172

Delo z binarnimi datotekami .......................................................................................................................... 174 Pisanje podatkov v binarno datoteko ......................................................................................................... 174 Branje podatkov iz binarne datoteke.......................................................................................................... 175

Garbage Collector ............................................................................................................................................. 182

Uporaba Garbage Collectorja in upravljanje s pomnilnikom........................................................................ 182 Življenjska doba objektov.......................................................................................................................... 182 Destruktorji in Garbage Collector.............................................................................................................. 182 Kako deluje Garbage Collector.................................................................................................................. 183

Upravljanje s pomnilnikom............................................................................................................................. 183 Dispose metode.......................................................................................................................................... 183 Using stavek............................................................................................................................................... 184 Klic metode dispose iz destruktorja ........................................................................................................... 185

Dialogi – okenska pogovorna okna .................................................................................................................. 186

ColorDialog – pogovorno okno za izbiro barve. ............................................................................................ 186

FolderBrowserDialog – pogovorno okno za raziskovanje map. .................................................................... 187

FontDialog – pogovorno okno za izbiro pisave.............................................................................................. 188

OpenFileDialog - pogovorno okno za odpiranje datotek ............................................................................... 189

SaveFileDialog - pogovorno okno za shranjevanje datotek ........................................................................... 190

Tiskalnik............................................................................................................................................................. 198

MDI – Multi Document Interface (vmesnik z več dokumenti) ...................................................................... 204

Razhroščevalnik / Debugger – izvajanje in iskanje napak............................................................................. 210

Koračno izvajanje programa.......................................................................................................................... 210

Prekinitvene točke - breakpoints..................................................................................................................... 210

Razhroščevanje - debugging........................................................................................................................... 211

Osnove grafike: GDI+ (Graphical Device Interface)...................................................................................... 213

Razumevanje geometrije okenskih obrazcev................................................................................................... 213 Določanje pozicije s pomočjo strukture Point ........................................................................................... 213 Določanje velikosti vidnih elementov........................................................................................................ 214 Struktura Rectangle (pravokotnik)............................................................................................................. 215

Uporaba razreda Graphics............................................................................................................................. 215 Dogodek Paint............................................................................................................................................ 216 Metoda DrawString - risanje teksta s pomočjo GDI+................................................................................ 216 Metodi DrawLine in DrawLines- risanje ravnih črt – daljic (Lines).......................................................... 217 Metodi DrawRectangle in DrawRectangles - risanje in barvanje pravokotnikov ...................................... 218 Metodi DrawEllipse in FillElipse - risanje krivulj ..................................................................................... 219 Metodi DrawPie in FillPie - risanje tortic .................................................................................................. 220 Metodi DrawImage in DrawIcon – postavljanje slike in ikone na gradnik................................................ 221

Podatkovna skladišča (baze) in upravljanje s podatki ................................................................................... 223

Uporaba baze podatkov.................................................................................................................................. 223 Uporaba ADO.NET podatkovnih baz ........................................................................................................ 223 Kreiranje nove baze podatkov.................................................................................................................... 223 Izdelava novega projekta, ki dela nad obstoječo bazo podatkov................................................................ 227

Page 296: PROŠNJA - uranic.tsckr.siuranic.tsckr.si/VISUAL C#/VISUAL C#.pdf · MICROSOFT VISUAL C# .NET Srečo Uranič . Visual C# . ... Za začetek novega projekta v okolju Visual C# Standard

Visual C# . NET

Algoritmi in programski jeziki – 4.letnik

296

Instalacija in uporaba že obstoječih baz podatkov ..................................................................................... 229 Dostop do baze podatkov........................................................................................................................... 230 Razumevanje pojmov DataSet, DataTable in TableAdapter...................................................................... 232 Prikaz podatkov iz neke podatkovne tabele v naši aplikaciji .................................................................... 233

Programska uporaba ADO.�ET (brez čarovnika) ........................................................................................ 235 Splošna oblika kreiranja poizvedbe iz baze podatkov ............................................................................... 236 Splošna oblika SQL transakcije nad bazo podatkov .................................................................................. 237 Razred SqlDataReader ............................................................................................................................... 242 Zapiranje povezav z bazo podatkov........................................................................................................... 242

Delo s povezavami podatkov (Data Binding) in objekti razreda DataSet ...................................................... 243 Povezava okenskih gradnikov s podatkovnim izvorom............................................................................. 243 Objekt DataSet in enostavno povezovanje s podatkovnim izvorom.......................................................... 243 Kompleksno povezovanje s podatkovnim izvorom ................................................................................... 247 Ažuriranje baze podatkov z uporabo objektov DataSet ............................................................................. 248 Upravljanje s povezavami.......................................................................................................................... 249 Obravnava istočasnega posodabljanja podatkov več uporabnikov (multi-user updates) ........................... 249 Uporaba objekta DataSet v povezavi z gradnikom DataGridView............................................................ 250 Ažuriranje uporabnikovih vnosov z uporabo objekta DataSet................................................................... 256 Transakcije................................................................................................................................................. 268

Osnove razvoja oz. priprave aplikacije za potrebe testiranja in priprave namestitvenega (Setup) programa............................................................................................................................................................................. 271

XCopy ............................................................................................................................................................. 271

ClickOnce ....................................................................................................................................................... 272

Setup (namestitveni) program......................................................................................................................... 275 Uporaba namestitvenega (Setup) programa za namestitev aplikacije pri uporabniku ............................... 277

Dokumentiranje................................................................................................................................................. 279

Dokumentiranje razredov ............................................................................................................................... 279

Kreiranje in uporaba knjižnice razreda (class library) .................................................................................. 281 Kako delujejo knjižnice razredov .............................................................................................................. 281 Kako kreiramo knjižnico razredov............................................................................................................. 282

Pomoč ................................................................................................................................................................. 283

Dodatek 1: Kode tipk (KeyCode) na tipkovnici.............................................................................................. 283

Kazalo vsebine ................................................................................................................................................... 292