Visual Basic från kommandoprompten

Clas Örjan Spång

Det här är en textbaserad snabbkurs i programspråket Visual Basic. Kursen kan läsas fristående eller som ett delmoment i gymnasiekurserna Programmering 1 eller Programmering 2. För att följa kursen bör du ha grundläggande kunskaper om datoranvändning och programmering. Kursen får användas fritt för undervisningsändamål.

VB är ett programspråk med bästa tänkbara utvecklingsmöjligheter i en Windows-miljö. Språket bygger vidare på syntax och nyckelord från programspråket BASIC, men VB erbjuder dessutom avancerad objektorientering och ett välutvecklat grafiskt användargränssnitt i en händelsestyrd programexekvering.

Källkodsraderna till ett enkelt VB-program är grupperat i moduler och klasser. En källkod kan antingen vara uppbyggd så att den innehåller:

Moduler och klasser skrivs in sist i källkoden. Överst kan källkodsfilen innehålla en Option-sats (som exempelvis Option Explicit) och därefter, eventuellt, en eller flera imports-satser (mer om imports-satserna i avsnittet om namnrymder).

Du kan börja med ett VB-program som innehåller en enda modul. För att modulen ska exekveras måste proceduren Main() finnas med. I ett program som innehåller proceduren Main() börjar exekveringen automatiskt med första satsen. Den första satsen i Main() är då programmets ingångspunkt. I sin enklaste variant är proceduren Main() en subrutin, den inleds då med raden Sub Main() och den avslutas med raden End Sub.

Källkoden till ett VB-program som visar texten "Hej världen!" i en särskild meddelanderuta på bildskärmen kan se ut så här:

' Källkod i VB
Module minModul
' Här börjar modulen minModul
Sub Main()
' är börjar proceduren Main()
MsgBox("Hej världen!")
' Proceduren MsgBox() visar en meddelanderuta
End Sub
' Här slutar proceduren Main()
End Module
' Här slutar modulen minModul

Här innehåller proceduren Main() bara en programsats – ett anrop av proceduren MsgBox() - proceduren >MsgBox() visar meddelanderutor.

Tecknet ' längst till vänster på en rad markerar att raden är en kommentar till källkoden som inte ska kompileras.

Källkoden sparas med ett filnamn som slutar med suffixet .vb

Till sist ska källkodsfilen kompileras. För att kompilera en felfri källkodsfil med namnet nr1.vb med hjälp av VB-kompilatorn vbc räcker det att skriva följande kommando i ett Visual Studios kommandofönster. Välj Tools -> Command Line -> Developer Command Prompt:

vbc.exe   nr1.vb

I kommandofönstret kan det se ut så här:



OBS: De senaste versionerna av Visual Studio saknar åtkomst till kompilatorfilen vbc.exe, i stället får man kompilera källkoden mha det grafiska användargränssnittet.

Alternativt kan du tweaka datorn för att få tillgång till kompilatorfilen. Det finns tips om hur du kan göra på stackoverflow och fileinspect.

Du kan hitta ännu fler tips genom att googla efter "visual basic command line compiler".

Resultatet av kompileringen blir en ny fil, nr1.exe, ett vanligt program som kan köras genom att man dubbelklickar på det - men ett problem är att ett oönskat kommandofönster visas under programkörningen.

För att undvika kommandofönstret under programkörningen – det behövs inte om du bara vill visa en enkel meddelanderuta - kan du ange kompileringsdirektivet /target:winexe när du kompilerar filen.

För att skapa en windowsapplikation utan visning av kommandofönster skriver du alltså (i äldre versioner av Visual Studio) vbc.exe /target:winexe nr1.vb på kommandoraden.

Vill du arbeta mer koncentrerat med enbart ett kommandofönster utan meddelanderutor och annat grafiskt användargränssnitt kan du i stället låta dina program mata ut meddelanden som text efter kommandoprompten. Med hjälp av proceduren Console.WriteLine() skriver det här programmet en meddelanderad med texten
"Hello world!" direkt i kommandofönstret:


' Källkod i VB
Module enTillModul
' Här börjar modulen enTillModul
Sub Main()
' Här börjar proceduren Main()
Console.WriteLine("Hello world!")
' Proceduren Console.WriteLine skriver en textrad
End Sub
' Här slutar proceduren Main()
End Module
' Här slutar modulen enTillModul


Med ett kompileringsdirektiv kan du också förse programmet med en egen ikon.
Kompileringsdirektivet är /win32icon:ikon.ico, där ikon.ico är filnamnet på en ikon i samma mapp.

Flera kompileringsdirektiv kan kombineras. Följande kommandorad ger programmet nr1.exe en ikon med filnamnet Hand.ico – och det körs utan att ett kommandofönster visas:

vbc.exe   /target:winexe   /win32icon:Hand.ico   nr1.vb

Det finns ytterligare några användbara kompileringsdirektiv. Skriv vbc /? efter kommandoprompten för att ta fram en lista på de tillgängliga kompileringsdirektiven.

Inmatning av text

En grafisk inmatningsruta – proceduren InputBox() - kan ta emot text som input till programmet. Proceduren ska också visa en ledtext som talar om vad användaren förväntas skriva:

' Källkod i VB
Module inputexempel1
' Här börjar modulen inputexempel1
Sub Main()
' är börjar proceduren Main()
MsgBox("Du skrev " & InputBox("Skriv en text:"))
End Sub
' Här slutar proceduren Main()
End Module
' Här slutar modulen inputexempel1


Programmet tar emot en text i en inmatningsruta, sedan återger ger det texten i en meddelanderuta, efter den inledande texten ”Du skrev ” (operatorn & sammanfogar två textsträngar).

Om du i stället vill göra programmets textinmatning i ett kommandofönster, kan du använda proceduren Console.ReadLine(). Källkoden kan skrivas så här:


' Källkod i VB
Module inputexempel2

' Här börjar modulen inputexempel2
Sub Main()
' Här börjar proceduren Main()
Console.WriteLine("Skriv en text:")
Console.WriteLine("Du skrev " & Console.ReadLine())
End Sub
' Här slutar proceduren Main()
End Module
' Här slutar modulen inputexempel2


Proceduren Console.ReadLine() kan inte visa en särskild ledtext för att uppmana användaren att skriva in en text, så proceduren anropas utan argument. I stället för att visa en ledtext som i en meddelanderuta får man helt enkelt låta Console.WriteLine() visa en text på raden ovanför med innebörden att nu förväntas användaren mata in text.


Procedurer

Källkoden i en modul delas upp mellan olika procedurer som du själv definierar. Procedurerna kan vara antingen subrutiner (som inte returnerar ett värde) eller funktioner (som returnerar ett värde). Procedurerna i sin tur läggs in i olika moduler. Det finns inga bestämda regler för hur den uppdelningen ska göras, huvudsaken är att källkoden blir strukturerad på ett sådant sätt att den blir tydlig och överskådlig.

I nedanstående program finns det två moduler, Modul1 och Modul2. Main-proceduren finns i Modul1, och därför kommer programexekveringen att starta där. Från Main-proceduren anropas sedan proceduren Fortsättning(), som också finns i Modul1. Från proceduren Fortsättning() anropas proceduren Avslutning(), som finns i en annan modul, Modul2 – proceduranropet måstge därför skrivas Modul2.Avslutning().

I den sistnämda proceduren visas först en inmatningsruta – med hjälp av proceduren InputBox() - som uppmanar användaren att skriva in sitt namn. Den textsträng som returneras från anropet InputBox("Skriv ditt namn") sammanfogas sedan med ”Hej då” . Den sammanfogade textsträngen visas i en meddelanderuta:


' Källkod i VB
Module Modul1
' Här börjar modulen Modul1
Sub Main()
' Här börjar Main()
MsgBox("Ingångspunkten passerad!")
' Proceduren MsgBox() visar en meddelanderuta
Fortsättning()
' Anrop av Fortsättning()
End Sub
' Här slutar Main()
Sub Fortsättning()
MsgBox("Proceduren Fortsättning anropad!")
Modul2.Avslutning()
End Sub
End Module
' Här slutar modulen Modul1
Module Modul2
' Här börjar modulen Modul2
Sub Avslutning()
MsgBox("Hej då " & InputBox("Skriv ditt namn"))
End Sub
End Module
' Här slutar modulen Modul2

Namnrymder

Programsatser i VB samlas ihop i procedurer och dessa kan sedan i sin tur samlas ihop i moduler och klasser. På nivån ovanför kan också moduler och klasser samlas ihop i namnrymder. Namnrymder kan i sin tur omges av andra namnrymder.

Stora program kan av misstag få namnkollisioner då variabler eller moduler har samma namn. För att undvika detta finns möjligheten att dela upp källkoden i namnrymder. Då gör det inget att proceduren Fortsättning() i modulen Adam och namnrymden Alpha har samma namn som proceduren Fortsättning() i modulen Adam och namnrymden Beta. Det går inte att förväxla dem.

Början på en namnrymd markeras med nyckelordet Namespace och ett unikt namn. Slutet på namnrymden markeras med End Namespace. Namn i VB får inte innehålla mellanslag och de måste börja med en bokstav. Det här VB-programmet innehåller två namnrymder, Alpha och Beta:


' Källkod i VB
Namespace Alpha

Sub Main()
MsgBox("Hej " & InputBox("Skriv ditt namn") & "!")
Fortsättning()
End Sub
Sub Fortsättning()
MsgBox("Fortsättning i Adam ...")
Bertil.Fortsättning()
End Sub
End Module
Module Bertil
Sub Fortsättning()
MsgBox("Fortsätter till namnrymden Beta ...")

' Anropar en procedur i namnrymden Beta

Beta.Adam.Avslutning()
End Sub
End Module
End Namespace
Namespace Beta>
Module Adam
Sub Avslutning()
MsgBox("Programmet avslutas!")
End Sub
End Module
End Namespace


I programmet får ett proceduranrop i namnrymden Alpha åtkomst till en procedur i namnrymden Beta med hjälp av punktnotation - det vill säga att Beta. skrivs framför anropet.

Det finns ett stort antal standardbibliotek i VB med källkod innehållande procedurer och variabler som kan användas direkt vid kompileringen av ett VB-program. Deras funktionalitet kommer sedan att ingå i programmet. Standardbibliotekens källkod är uppdelad i färdiga namnrymder. Också här har ett program direkt åtkomst till sådana namnrymder med hjälp av punktnotation.

Här nedan har vi ett program som läser av storleken på datorns RAM-minne och det operativsystem som är installerat i datorn. I namnrymden My, närmare bestämt i >My.Computer.InfooTotalPhysicalMemory (storleken på RAM-minnet) och OSFullName (namnet på operativsystemet). Tecknet _ längst till höger på en rad markerar att en programsats inte avslutas med radbrytningen – den fortsätter på nästa rad i källkoden:


' Källkod i VB
Module Datorkoll
Sub Main()
MsgBox("Ledigt utrymme i "&vbNewLine & _
My.Computer.Info.TotalPhysicalMemory, vbOKOnly, "RAM")
MsgBox("Datorns operativsystem:" & vbNewLine & _
My.Computer.Info.OSFullName, vbOKOnly, "RAM")
End Sub
End Module



     



Variabeldeklarationer i Visual Basic

Variabler i VB ska alltid deklareras. I variabeldeklarationen finns mer eller mindre information om variabeln – i enklaste fall bara en upplysning om att detta är en variabel, ibland mer precist också information om vilken slags data variablen innehåller och var i källkoden den kan användas. Kraven på variabeldeklaration är mer eller mindre omfattande beroende på vilket alternativ för Option som man väljer på översta raden i källkoden. Det alternativ som ställer de hårdaste kraven på variabeldeklarationerna är Option Strict On, därefter kommer det mer tillåtande Option Explicit On. Man kan också utelämna Option-satsen helt – det ger mindre automatisk kontroll men det ger också större risk för att fel släpps igenom.

Utelämnar man Option-satsen räcker det att variabeldeklarera med enkla Dim-satser.

Här är ett program som räknar ut volymen av en kub. Programmet innehåller fyra variabler, text1, text2, kubensSida och kubensVolym, deklarerade i var sin Dim-sats:


Module Kubvolym1
Sub Main()
Dim kubensSida = 31
Dim kubensVolym = kubensSida * kubensSida * kubensSida
Dim text1 = "Kubens volym är "
Dim text2 = " volymenheter"
MsgBox(text1 & kubensVolym & text2)
End Sub
End Module


Källkoden är skriven på det enkla sättet, utan Option-alternativ. Tecknet = är här en tilldelningsoperator, det vill säga att det anger vilket värde en variabel ska få. Tecknet ska inte förväxlas med ett matematiskt likhetstecken, som också skrivs som =


Datatyper

Om man inte påbörjar VB-källkoden med satsen Option Strict On kommer variablerna att få samma datatyper som de värden variablerna först tilldelas: En variabel som tilldelas ett heltal får datatypen Integer, en variabel som tilldelas ett decimaltal får datatypen Double, en variabel som tilldelas något av de boolska värdena sant eller falskt - True eller False - får datatypen Boolean, en variabel som tilldelas en textsträng omgiven med " får datatypen String och en variabel som tilldelas ett datum omgivet med # (till exempel #12/24/2013#) får datatypen Date.


OBS det amerikanska datumformatet för julafton 2013 i det sistnämnda exemplet!


I Dim-satserna i programexemplet här ovan har variablerna kubensSida och kubensVolym fått sina initialvärden i form av heltal och därigenom har de fått datatypen Integer. Det vill säga att RAM-minnet tar till ett utrymme för en variabel som kan innehålla heltalsvärden mellan -2 147 483 648 och +2 147 483 647. För att lagra heltal av den storleksordningen krävs det som mest 4 bytes (detsamma som 32 bitar) i datorns RAM-minne.

Det finns en uppenbar nackdel med att undvika att deklarera datatyp som i exemplet: För den aktuella kalkylen är minnesutrymmet onödigt stort. Det här enkla sättet att deklarera variablerna ger inte optimal minnesanvändning.

För att få bästa minnesanvändning bör man ange vilka slags variabler man deklarerar - heltal, decimaltal, boolska värden, enstaka skrivbara tecken eller textsträngar. Kompilatorn använder den informationen om datatypen när den ska reservera ett utrymme i minnet för variabelns värden: Hur stort utrymmet ska vara och vilken slags information det ska innehålla. Med en mer detaljerad variabeldeklaration får variablerna statisk typning - med statisk typning kan datatypen från variabeldeklarationen inte ändras under programkörningen. Det ger en mer effektiv användning av minnet. Kompilatorn reserverar då inte ett onödigt stort utrymme för variabelns värden.

Datatypen kan man alltså bestämma redan i variabeldeklarationen: Första gången man skriver en variabels namn kan man ge en detaljerad information om hur variabeln ska användas i programmet. Deklarationen ska inte göras mer än en gång. En fördel med att göra variabeldeklarationer med tydligt angiven datatyp är alltså att datorns RAM-minne används mer effektivt.

En variabeldeklaration med angiven datatyp skrivs med nyckelordet Dim först och därefter variabelnamnet, nyckelordet As och datatypen.

Den här mer detaljerade variabeldeklarationen motsvarar de högre krav som ställs av Option-alternativet Strict. Det går alltså att skriva satsen Option Strict On överst i den här källkoden utan att det uppstår kompileringsfel:


Option Strict On
Module Kubvolym2
Sub Main()
Dim kubensSida
As Short
kubensSida = 31
Dim kubensVolym
As Short
kubensVolym = kubensSida * kubensSida * kubensSida
Dim text1
As String
text1 = "Kubens volym är "
Dim text2
As String
text2 = " volymenheter"
MsgBox(text1 & kubensVolym & text2)
End Sub
End Module


Datatypen Short tillåter att kubensSida och kubensVolym får heltalsvärden någonstans mellan -32768 och +32767. För att lagra heltal av den storleksordningen krävs det som mest bara 2 bytes (detsamma som 16 bitar) i minnet.

För att arbeta med större heltal krävs variabeldeklarationer som ger variabeln mer utrymme i minnet och för att arbeta med mindre heltal kan det räcka med variabeldeklarationer som kräver mindre utrymme. De vanliga datatyperna Sbyte, Short, Integer och Long och deras lagringskapacitet för heltal visas i tabellen. Kolumnerna anger datatyp, lagringsutrymme räknat i byte, minsta tillåtna värde och största tillåtna värde:


 Datatyp:    

Storlek:

Minsta värde:

Största värde:

Sbyte

1

-128

127

Short

2

-32 768

32 767

Integer

4

-2 147 483 648

2 147 483 647

Long

8

-9,2 * 1018

9,2 * 1018



Variabler för enbart positiva heltal kan ha särskilda datatyper. Den minst minneskrävande datatypen är Byte, som bara kan ta värden mellan 0 och 255, medan datatypen ULong, med minneskrav på 8 byte, kan ta värden mellan 0 och 18 446 744 073 709 551 615.

Också variabler med decimaltal kan ha olika datatyper med varierande utrymmeskrav. Beroende på hur stor nogrannhet man önskar kan man deklarera en variabel som Single (kräver 4 bytes minne) eller Double (kräver 8 bytes minne).

Andra viktiga datatyper är datum, som har datatypen Date, boolska värden med datatypen Boolean, textsträngar med datatypen String och enstaka tecken med datatypen Char.

 

Jämförelser

Variabler måste ha samma datatyp för att kunna jämföras.

Jämförelser i en selektion görs enklast med en If-sats. En If-sats börjar med nyckelordet If, sedan kommer ett villkor, sedan nyckelordet Then, därefter kommer de satser som utförs om vilkoret är sant, och till sist avslutas If-satsen med nyckelorden End If . Programsatserna mellan If och End If utförs bara om villkoret är sant.

Om man dessutom vill ha programsatser för fallet att villkoret inte är sant ska If-satsen ha den här syntaxen:


If villkor Then
' Satser som utförs om villkoret är sant
Else
'Satser som utförs om villkoret är falskt
End If

I programmet här nedan får användaren välja mellan två olika fortsättningar, beroende på om användaren svarar med rätt lösenord eller inte …


Module Inloggning
  Sub Main()
  Dim svar, lösen
  lösen = "hemligt"
  svar = InputBox("Skriv det hemliga lösenordet","Inloggning")
 
If > svar = lösen Then
     MsgBox("Ja, du kan det hemliga lösenordet!",,"Inloggning")
 
End If
 
End Sub
End Module


... om användaren svarar med fel lösenord händer ingenting, men om användaren svarar rätt kommer texten Ja, du kan det hemliga lösenordet! att visas i en meddelanderuta. Villkoret för att If-satsen ska utföras är att variabeln svar har värdet "hemligt" i början av If-satsen och det värdet får den om användaren skriver hemligt i inmatningsrutan. Jämförelsen är möjlig eftersom både svar och lösen har datatypen String. Tecknet = i villkoret är inte en tilldelningsoperator, här kan det läsas som ”är lika med”.

Det finns flera sätt att skriva villkor. Du kan använda "är inte lika med" <> "är mindre än" < och "är större än" > och många andra jämförelser. Vi ska pröva jämförelserna i dataspelet HILO, ett spel som går ut på att gissa ett slumpmässigt tal.

Datorn kan skapa slumptal med hjälp av funktionen Rnd. Tack vare Rnd-funktionen, som skapar slumptal i mellan 0 och 1 i decimalform, kan vi simulera tärningskast, lottdragningar och andra slumpmässiga händelser. Visserligen är det inte riktiga slumptal som funktionen Rnd tar fram, det är bara så kallade pseudoslumptal som genereras med en komplicerad formel, men om programsatsen Randomize utförs innan Rnd anropas går det att simulera en någotsånär slumpmässig talföljd.

Om vi till exempel multiplicerar Rnd med 4 får vi ett decimaltal någonstans mellan 0 och 3,999 … - om vi sedan tar bort decimalerna med funktionen Int() får vi ett heltal mellan 0 och 3. Addera en etta och vi får ett heltal som är lägst 1 och högst 4. Uttrycket Int(Rnd*4)+1 simulerar alltså ett slumpmässigt framtaget heltal mellan 1 och 4.

Ett problem är att det tal som genereras slumpmässigt i programmet får datatypen Integer, medan det som en användare skriver i en inmatningsruta alltid får datatypen String. De värdena kan alltså inte jämföras, eftersom båda leden i en jämförelse måste ha samma datatyp. Proceduren Cint() löser problemet med textsträngen, den konverterar en textsträng (som till exempel "2") till heltalet 2.


' Dataspelet HILO
Module HILO
     Public dittSvar, datornsSvar, max, talet
     Sub Main()
            start
            spel
            spel
            spel
            slut
     End Sub
     
Sub start()
            Randomize
            max=4
            talet=Int(Rnd*max)+1
            dittSvar = InputBox("Gissa ett tal mellan 1 och " & max,"HILO")
            dittSvar = CInt(dittSvar)
           
' Nödvändig typkonvertering till Integer
     
End Sub
     
Sub spel()
            If dittSvar < talet Then
             datornsSvar = "För litet"
            End If
            If dittSvar > talet Then
             datornsSvar = "För mycket"
            End If
            If dittSvar <> talet Then
              dittSvar = InputBox(datornsSvar & ", gissa en gång till")
              dittSvar = CInt(dittSvar)
            End If
     
End Sub
        Sub slut()
            If dittSvar=talet Then
               MsgBox("Ja, det är rätt! Grattis!")
            Else
               MsgBox("Nej, talet var " & talet)
            End If
     
End Sub
End Module


Dataspelet tar först fram ett slumptal mellan ett och fyra, sen får spelaren tre chanser att gissa talet. Efter varje felaktig gissning lämnas en ledtråd: Spelaren har antingen gissat för litet eller för mycket.

 

If-satser med många alternativ

Med nyckelorden If Then .. ElseIf .. Then .. Else .. End If kan man skapa en uppräkning av alternativ som utesluter varandra. Det går att ha ett valfritt antal alternativ. Fyra alternativ kan ställas upp så här:

If villkor Then
sats
ElseIf villkor Then
sats
ElseIf villkor Then
sats
Else
sats
End If

Antingen utförs en av de tre första satserna eller också utförs den sista Else-satsen.

I den här frågesporten går programmet med på flera alternativa svar:


Module ExempelMedElse
Sub Main()
Dim text, svar
text = "Hur kan nyckelordet If översättas till svenska?"
svar = InputBox(text)
If svar = "om" Then
      MsgBox "Rätt!"
ElseIf svar = "utifall att" Then
      MsgBox "Rätt!"
ElseIf svar = "ifall" Then
      MsgBox "Rätt!"
Else
      MsgBox "Fel svar!"
EndIf
End Sub
End Module



Deklarationer av fält

Blir variablerna många är det svårt för programmeraren att hålla reda på deras namn och datatyp och källkoden blir det otydlig och svåröverskådlig. En lösning är att gruppera liknande variabler i en datastruktur. Den enklaste och ofta den mest praktiska datastrukturen är en lista, eller som det också kallas, ett fält eller en vektor.
I stället för att ha till exempel variablerna ole, dole, doff har vi en lista, x, med variablerna x(0), x(1) och x(2). Variablerna i listan kallas element. De får samma datatyp.

En lista med en bestämd längd deklareras så att deklarationen visar det högsta indexnumret i listan. En lista, x, för tre element deklareras x(2) och en sådan deklaration numrerar elementen 0, 1 och 2 …


Dim x(2) As String
x(0)="Jag trivs bäst"
x(1)="i öppna"
x(2)="landskap ..."

... fältet x tilldelas tre av sångtextens rader, en i taget.

Deklaration och tilldelning av värden till ett fält kan förkortas så här:

Dim x() As String = {"Jag trivs bäst", "i öppna", "landskap ..."}

Anpassning av datatyperna

Inte bara variablerna utan också de värden som de tilldelas har typer i sig själva. Värdena måste vara adekvat anpassade till variablerna. I normalfallet behandlas decimaltal som värden av typen Double. Heltal behandlas som värden av typen Integer, om de inte är för stora (se tabellen), då får de typen Long i stället. Till exempel talet 3 har alltså i sig självt typen Integer, medan talet 2 147 483 648 har typen Long.

Om man vill ge ett tal eller tecken en annan värdetyp är normalfallets, kan det enkelt ordnas med ett särskilt värdetyptecken. Char markeras med c, Single markeras med F, den vanliga decimaltypen Double markeras med R och Decimal markeras med D. Också heltal kan förses med särskilda värdetypstecken. De vanligaste heltalstypernas värdetyptecken framgår av tabellen:


 Datatyp:

Värdetyptecken:

Sbyte

Tecken saknas

Short

S

Integer

I

Long

L


Värdetyptecknen kan användas då tilldelade värden ska anpassas till variabelns datatyp. I följande exempel kommer både Fel 1 och Fel 2 att förorsaka kompileringsfel:

Option Strict On
Module Typteckentest1
Sub Main()
Dim tecken As Char
' Fel 1:
tecken = "!"
' Tecknet ! är omgivet med " - alltså textsträng
Dim talet As Single
' Fel 2:
talet = 12.5
' Talet 12.5 får automatiskt datatypen Double
MsgBox("Svar: " & talet & tecken)
End Sub
End Module

Värdetyptecken löser problemet:

Option Strict On
Module Typteckentest2
Sub Main()
Dim
' Rättelse 1:
tecken = "!"
c
Dim talet As Single
' Rättelse 2:
talet = 12.5
F
MsgBox("Svar: " & talet & tecken)
End Sub
End Module


Allt deklarerande av variablerna kan ibland verka onödigt komplicerat. För att förenkla programmeringen kan det ofta vara praktiskt att enbart hålla sig till två datatyper för variabler med numeriska värden: Integer för heltal och Double för decimaltal. Det kan vara den optimala lösningen så länge som RAM-minnet inte blir fullt, eftersom det färdiga programmet då i regel kan köras snabbare.


Typomvandlingar

Alla datatyper kan specificeras i variabeldeklarationerna. Om en variabel inte har en deklarerad datatyp får den, som vi sett tidigare, i stället automatiskt en datatyp första gången den tilldelas ett värde.

Om ett värde som tilldelas en variabel inte överensstämmer med variabelns typ måste det värdet typomvandlas så att det passar variabeln. En sådan typomvandling kan vara antingen utvidgande eller inskränkande.

I en utvidgande typomvandling ändras värdets datatyp till en datatyp som tillåter mer utryme än tidigare. Det är då ingen risk att en del av värdet ska försvinna för att det inte får plats i den nya datatypen. En sådan typomvandling utförs automatiskt.

I en inskränkande typomvandling finns det en risk för att en del av värdet försvinner för att det inte får plats i den nya datatypen. I sådana fall kan det krävas mer kontroll i källkoden: Under Option-alternativet Option Strict On gäller att en explicit typomvandling måste utföras med hjälp av en särskild typomvandlingsfunktion.

Det är ofta praktiskt att undvika explicita typomvandlingar när det går och i stället skriva variabeldeklarationer och tilldelningssatser så att programmet får automatisk typomvandling. Den här tabellen visar vilka tilldelningar som kan ge automatisk typomvandling för de vanliga datatyperna när vi inte har samma datatyp till vänster och till höger om tilldelningsoperatorn =


Automatisk till:

Från:

Sbyte

-

Short

SByte

Integer

SByte eller Short

Long

Integer, Short eller SByte

Single

Long, Integer, Short eller SByte

Double

Single, Long, Integer, Short eller SByte


I exemplet här nedan beräknas en cirkels area. Värdet för variabeln radie kommer automatiskt att typkonverteras från Integer till Double. Källkoden uppfyller kraven för kompileringsalternativet Option Strict On:

Option Strict On
Module cirkel
Sub Main()
Dim radie As Integer
' är ett heltal av typen Integer
Dim pi As Double
' pi är ett decimaltal av typen Double
radie = 5
pi = 3.14
MsgBox("Arean är " & radie*radie*pi & " ytenheter")
End Sub
End Module



Den variabeldeklaration som kan ge automatisk typomvandling i flest fall är alltså Double, därefter kommer Single, Long, Integer, Short och Sbyte. Därför är det också av det skälet enklast för den som är nybörjare i VB att låta alla variabler för numeriska värden ha antingen datatypen Double eller – i vissa fall - Integer.

Senare kan man – om det är nödvändigt - finslipa sina variabeldeklarationer genom att deklarera andra datatyper. I moderna datorer brukar inte minnesanvändningen vara något problem, förutsatt att man inte använder stora fält av små heltal eller att man arbetar med extremt stora heltal.

Med typomvandlingsfunktionen CType kan man utföra en explicit typomvandling av ett värde från en datatyp till en annan.

CType tar två argument, dels variabelnamnet eller värdet, dels den önskade datatypen.

Ett exempel på typomvandlingar i ett program som läser av hur mycket fritt RAM-minne det finns i en dator när programmet körs:


Option Strict On
Module Typomvandlingar
Sub Main()
Dim svar As String
svar = InputBox("Hur många byte behöver du?","RAM","0")
Dim behov As Long
behov =
CType(svar, Long)
Dim finns As ULong
finns = My.Computer.Info.AvailablePhysicalMemory
' Avläser ett heltal av datatypen ULong från systemet
Dim diff As Long
diff =
CType(finns, Long) - behov
' Tillgängligt utrymme minus behovet av utrymme
Dim testsvar As String
If diff < 0 Then
  testsvar = "Det finns inget utrymme." & _
  vbNewLine & "Det fattas " &
CType(-diff, String)
Else
  testsvar = "Minnet räcker, det finns " & _
  CType(diff, String)
End If
MsgBox(testsvar, vbOKOnly, "")
End Sub
End Module


   


Variablers synlighet

En variabels synlighet definieras i dess variabeldeklaration. Variabeldeklarationens plats i källkoden påverkar synligheten, beroende på om platsen för deklarationen finns på namnrymdsnivå, modulnivå, procedurnivå eller på ännu lägre nivå. Men nyckelorden Static, Private och Public kan ändra detta och ge variabeln större eller mindre synlighet. Det finns tre faktorer förutom variabeldeklarationens plats i källkoden som inverkar på synligheten - tillgänglighet, livslängd och scope:

Beroende på vilken synlighet man vill ge variabeln kan man alltså inleda deklarationen med något av nyckelorden Dim, Static, Private, Public eller ReadOnly. Deklarationen Dim är identisk med Private, men för tydlighetens skull är det bättre att använda Private för att visa när variabeln är deklarerad på modulnivå (utanför procedurer) medan deklarationen Dim visar att variabeln är deklarerad på procedurnivå . Inuti procedurer är man hänvisad till enbart två alternativ - antingen Dim eller Static.


Variabeldeklarationen Static

En variabel som deklareras som Static inuti en procedur behåller sitt värde även då proceduren inte exekveras.

I det här exemplet är en variabel, räknare, deklarerad inuti subrutinen kanRäkna. Variabeln ökar med ett varje gång subrutinen anropas:


Module rkn
Sub Main()
kanRäkna()
kanRäkna()
kanRäkna()
kanRäkna()
End Sub
Sub kanRäkna()
Static räknare

' Deklarationen Static skapar en enda variabel räknare
' som behåller sitt värde till nästa gång
räknare = räknare + 1
MsgBox("Anrop nr: " & räknare)
End Sub
End Module


For-Next-slingor

En For-Next-slinga upprepar en eller flera programsatser ett bestämt antal gånger.

Här är ett program som hejar tre gånger på ditt fotbollslag. Variabeln antalHejarop har scope enbart inom For-Next-slingan:


Option Strict On
Module Fotboll
Private text As String = "Fotbollsfråga"

Sub Main()
Dim svar As String
svar = InputBox(text, "Skriv namnet på ditt fotbollslag!")
' En For-slinga upprepar en programsats 3 gånger
For antalHejarop As Integer = 1 To 3
' Variabeln antalHejarop är bara synlig inom For-slinga
    MsgBox("Heja " & svar & "!")
Next antalHejarop
End Sub
End Module


Man behöver inte bestämma i förväg hur många gånger en For-Next-slinga ska upprepas. Nedanstående program går igenom tecknen i ett ord som du har skrivit i en inmatningsruta, och visar vilket UNICODE-nummer tecknen har. En funktion, Len(), räknar först efter antalet bokstäver i ordet, antalet tilldelas variabeln max, sedan upprepas For-Next-slingan en gång för varje bokstav i ordet, från 1 till max


Module UNICODEexempel
Sub Main()
' UNICODE-koden för bokstäverna i ett ord
Dim svar,
max, text, tkn
svar = InputBox("Skriv några ord!")
max = Len(svar)
text = "UNICODE:" & vbNewLine
For i As Integer = 1 To
max
    tkn = Mid(svar, i, 1)
    text = text & tkn & " -> " & AscW(tkn) & vbNewLine
Next
MsgBox(text ,, "Från tecken till UNICODE-nummer")
End Sub
End Module


Kryptering

Med hjälp av kryptering kan du - till exempel - göra en minnesanteckning om lösenord som du använder på internet på ett sådant sätt att utomstående inte kan läsa dom. En enkel krypteringsmetod (känd som Caesar-krypto) går till så att man byter ut varje bokstav i ett ord mot den bokstav som kommer ett bestämt antal platser senare i alfabetet. Om du till exempel använder lösenordet ”ahemligt” och du vill kryptera ordet så att varje bokstav i ordet ersätts med nästa bokstav i alfabetet får du det krypterade ordet ”bifnmjhu”. För att dekryptera – det vill säga för att ta fram det riktiga lösenordet från det krypterade ordet – behöver du bara ersätta varje bokstav i det krypterade ordet med den bokstav som kommer på platsen innan i alfabetet. Plus ett ger alltså en bokstavs krypterade platsnummer i alfabetet, minus ett ger det okrypterade platsnumret. Talet ett är din krypteringsnyckel. För att kryptera använder vi då nyckeln +1, för att dekryptera använder vi nyckeln -1.

Med hjälp av UNICODE-tabellens platsnummer i stället för det svenska alfabetets kan vi skriva det här enkla krypteringsprogrammet:


Module Caesar
Sub Main()
' Caesar-krypto
Dim ordet, nyckel, max, text, i, tkn, nyttNr, nyttTkn
ordet = InputBox("Ordet du vill kryptera eller dekryptera?")
nyckel = InputBox("Skriv nyckel!")
max = Len(ordet)
text = ""
For i = 1 To max
  tkn = Mid(ordet, i, 1)
  nyttNr = AscW(tkn)+nyckel
  nyttTkn = ChrW(nyttNr)
  text = text & tkn & " " & nyttTkn & vbNewLine
Next
MsgBox(text,, "Caesar-krypto")
End Sub
End Module


Om, till exempel, ditt lösenord är ”lingon” och din krypteringsnyckel är +5 kommer du att ta fram de krypterade tecknen ”qnslts”, som senare kan dekrypteras med nyckeln -5.

Använd helst inte stora tal som krypteringsnyckel i detta enkla Caesar-krypto, då kommer du att hamna utanför gränsen för de tecken som kan återges av ett svenskt tangentbord.


Objekt

VB är ett objektorienterat språk. Objekt grupperar egenskaper och metoder på ett lättillgängligt sätt. Objekten binds samman i en objektmodell, där kompilatorn, direkt eller indirekt, har åtkomst till allt från toppen av objektmodellen.

Objektmodellen skiljer mellan exponerade och icke exponerade objekt. Egenskaper och metoder hos de exponerade objekten kallas ibland klassegenskaper och klassmetoder. De kan användas direkt i en källkod utan några förberedelser.

Med punktnotation har man direkt åtkomst till de exponerade objektens egenskaper och metoder.

Metoder som ReadLine() eller WriteLine() kan man som vi sett anropa genom att skriva Console.ReadLine(). eller Console.WriteLine()

Egenskaper - variabler och konstantvärden - hos objekten når man på ett liknande sätt med hjälp av punktnotation. Exempelvis objektet My.Computer.Info kan ge upplysningar om datorns minneskapacitet och operativsystem.

 

Namnrymden My


Namnrymden My samlar ett antal metoder, egenskaper och objekt som ofta används. Där finns bland annat information om det aktuella programmet, filsystemet, operativsystemet, datorn, användargränssnitt, resurser som bilder och ljudfiler, inställningar, användaren, webbtjänster mm. Namnrymden My är uppdelad i namnrymder som i sin tur kan vara uppdelade i ytterligare namnrymder med olika inriktningar. I det här exemplet används metoden ReadAllText() som finns i objektet Filesystem under namnrymden Computer under namnrymden My. Metoden ReadAllText() läser in hela innehållet i en textfil. I programmet finns textfilen i mappen C:\Temp\ och den har filnamnet minfil.txt. När innehållet har blivit inläst visas det i en meddelanderuta:


Module textfilExempel
Sub Main()
Dim innehåll, textfil As String
textfil = "C:\Temp\minfil.txt"
' Metoden ReadAllText() finns i My.Computer.Filesystem
innehåll = My.Computer.Filesystem.ReadAllText(textfil)
MsgBox(innehåll)
End Sub
End Module

Metoder för att spela upp musik och ljudeffekter erbjuds också i namnrymden My. I exemplet används metoden Play() som spelar upp ljudfilen drumroll.wav. Filen är här placerad i mappen C:\temp\:

Module ljudfilExempel
Sub Main()
Dim ljudet As String = "C:\Temp\drumroll.wav"
' Metoden Play() finns i My.Computer.Audio
My.Computer.Audio.Play(ljudet)
MsgBox("Spelar " & ljudet)
End Sub
End Module

Märk att metoden Play() bara kan vara aktiv så länge som bildskärmen visar en grafisk komponent (i det här fallet en meddelanderuta).

Färglägg kommandofönstret

I ett kommandofönster går det att färglägga text och bakgrund. Texten färglägger man genom att tilldela en färgegenskap till Console.ForegroundColor, på samma sätt kan man färglägga bakgrunden genom att tilldela en färgegenskap till Console.BackgroundColor. Kommandofönstrets vanliga färger kan återställas med programsatsen Console.ResetColor().
Färgerna heter DarkBlue, DarkGreen, DarkCyan, DarkRed, DarkMagenta, DarkYellow, Blue, Green, Cyan, Red, Magenta, Yellow, White och Black. De är tillgängliga som egenskaper i objektet ConsoleColor.
Den här tilldelningssatsen kommer alltså att färglägga texten mörkgrön:
Console.ForegroundColor = ConsoleColor.DarkGreen

Och den här tilldelningssatsen färglägger den röd …
Console.ForegroundColor = ConsoleColor.Red

I det här multiplikationstestet får man tio frågor. Rätt svar på en fråga ger mörkgrön text och fel svar ger röd text:


Module Multiplikationstest
Sub Main()
' Ett multiplikationstest med facit och poängberäkning
Randomize
Dim R, textFraga, i, tal1, tal2, textFaktorer, textNr, svar, max
max = 10 : R = 0 : textFraga = "Nu börjar vi: "
For i=1 To max
' Formulerar frågan:
   tal1 = CInt(16*Rnd)
   tal2 = CInt(16*Rnd)
   textFaktorer = tal1 & " * " & tal2
   textFraga = "Vad är " & textFaktorer
   textNr = "Fråga " & i & " av " & max & ": "
   Console.ForegroundColor = ConsoleColor.DarkYellow
   'Först mörkt gul text
   Console.Writeline(textNr & textFraga & " ? ")
   svar = CInt(Console.ReadLine())
   ' Vid rätt svar:
   If svar = tal1*tal2 Then
      R = R + 1
      Console.ForegroundColor = ConsoleColor.DarkGreen
   ' Vid fel svar:
   Else
      Console.ForegroundColor = ConsoleColor.Red
   End If
   textFraga = "Rätt svar var " & tal1*tal2 & ". Du har "
   textFraga = textFraga & CInt(100*R/i) & " % rätt " & vbNewLine
   Console.WriteLine(textFraga)
Next
' Slutresultatet:
Console.WriteLine("Ditt resultat: Du fick " & R & " rätt av " & max)
End Sub
End Modulee


Ett kommandofönster med färglagd text passar bra som gränssnitt för den som behöver koll på det som skrivits tidigare – det är bara att se efter på raderna ovanför i kommandofönstret.




Dialogrutor

Med funktionen MsgBox() har du tillgång till ett stort antal dialogrutor med svarsknappar. Du kan sätta in knappar med etiketter som Ja, Nej eller Avbryt i dialogrutan, och när programmet körs kan programtolken känna av vilken av knapparna användaren har tryckt på.


Fördefinierade konstanter för knappar

vbAbortRetryIgnore
vbMsgBoxHelpButton
vbOKCancel
vbOKOnly
vbRetryCancel
vbYesNo
vbYesNoCancel


Konstanternas namn ger en fingervisning om vilka knappar de tar fram. I det här programmet använder vi konstanten vbYesNo, som ger dialogrutor med Ja och Nej knappar. En tryckning på Ja-knappen ger sifferkoden 6, och en tryckning på Nej-knappen ger sifferkoden 7. Sifferkoderna är lika med konstanterna vbYes och vbNo. Sifferkoderna kan behandlas som vanliga heltal. I programmet räknas 6:an eller 7:an om till en beställning av antingen 1 stycken eller 0 stycken:


Module KaffeOchBulle
Sub Main()
' En robotkypare
Dim svar, enKaffe, enKaffeTill, enBulle, enBulleTill, summa
Dim textA = "Välkommen till datacafét!"
Dim textB = "En kaffe kostar 10 kr, påtår 5 kr och en bulle 20 kr"
MsgBox(textB,, textA)
svar = MsgBox("Vill du ha en kaffe?", vbYesNo, textA)
enKaffe = 7 – svar
' Ja ger 1 kaffe, Nej ger 0 kaffe i beställningen
If svar = vbYes Then
    svar = MsgBox("Vill du ha påtår?", vbYesNo, textA)
    enKaffeTill = 1
Else
    enKaffeTill = 0
End If
svar = MsgBox("Vill du ha en bulle?", vbYesNo, textA)
enBulle = 7 – svar
If svar = vbYes Then
    svar = MsgBox("Vill du ha en bulle till?", vbYesNo, textA)
    enBulleTill = 1
Else
    enBulleTill = 0
End If
summa = 10*enKaffe+5*enKaffeTill+20*enBulle+20*enBulleTill
MsgBox( "Det blir " & summa & " kronor")
End Sub
End Module


Subrutiner


Subrutinerna är den enklaste typen av procedurer. Subrutiner fungerar som kapitel i en bok. Varje kapitel svarar för en del av boken - varje subrutin svarar för en del av modulen. På första raden i en subrutin står nyckelordet Sub , sedan ett lämpligt namn på subrutinen, och sist på raden, inom parenteser, skriver man parameterlistan - namnen på de variabler som tar emot argumenten till subrutinen. En subrutin kan också skrivas utan parameterlista, men då ska namnet följas av ett par tomma parenteser.

På de följande raderna kommer de programsatser som subrutinen ska utföra. På sista raden avslutas subrutinen End Sub. Du anropar en subrutin genom att skriva dess namn. I det här exemplet anropas först subrutinen adam och sedan subrutinen bertil ...


Module ab
Sub Main()
adam
bertil
End Sub
Sub adam()
   Dim texten
   texten = "Nu har adam anropats!"
   MsgBox(texten)
End Sub
Sub bertil()
   Dim texten
   texten = "Nu har bertil anropats!"
   MsgBox(texten)
End Sub
End Module


Programmet kommer att visa, först en meddelanderuta med texten ”Nu har adam anropats” och sedan en meddelanderuta med texten ”Nu har bertil anropats”.

De variabler som deklareras i en Dim-sats i en procedur kan som sagt bara användas inuti proceduren - deras synlighet och livslängd är begränsad till arbetsflödet i proceduren. Sådana variabler har enbart synlighet på procedurnivå. I exemplet här ovan fanns det egentligen två variabler texten, men det gick att ge dem samma namn eftersom deras synlighet var begränsad till var sin subrutin - om variabeln texten i subrutinen adam får ett nytt värde, påverkar det inte variabeln texten i subrutinen bertil. Variabler med synlighet på procedurnivå kallas ibland lokala variabler.

Om du vill att samma variabel ska kunna användas överallt i modulen måste du deklarera den i en Private-sats i början av programmet, utanför procedurerna. Vi säger då att variabeln har fått synlighet på modulnivå. Så här kan vi använda en enda variabel - texten2 - och ge den en synlighet på modulnivå:


Module ab2
Private texten2
Sub Main()
adam
bertil
End Sub
Sub adam()
   texten2
= "Nu har adam anropats!"
    MsgBox(texten2)
End Sub
Sub bertil()
   texten2 = "Nu har bertil anropats!"
   MsgBox(texten2)
End Sub
End Module


Om subrutiner kan användas flera gånger i ett program spar det in på överflödiga upprepningar i källkoden.

Det här dataspelet är uppdelat i subrutinerna Main, start, spel och slut. Subrutinerna anropas en eller flera gånger från Main. I subrutinen start skapas ett slumptal mellan 1 och 5, i subrutinen spel jämförs slumptalet med spelarens gissning och i subrutinen slut får spelaren veta hur det har gått:


' Dataspelet HILO med subrutiner
Module HILO
Private dittSvar, datornsSvar, max, talet
Sub Main()
start
For spelomgångNummer = 1 To 4
    ' Spelaren får 4 försök att gissa talet
    spel
Next
slut
End Sub
Sub
Randomize
' Randomize förbereder pseudoslumptal
max=10
talet=Int(Rnd*max)+1
' Rnd ger slumptal i decimalform mellan 0 och 1
' Int(Rnd*5)+1 ger slumptal i heltalsform mellan 1 och 5
dittSvar = InputBox("Gissa ett tal mellan 1 och " & max)
dittSvar = CInt(dittSvar)
' Nödvändig typkonvertering till Int
End Sub
Sub spel()
' En If-sats prövar om dittSvar är mindre än talet
If dittSvar < talet Then
    datornsSvar = "För litet"
End If
' övar om dittSvar är större än talet
If dittSvar > talet Then
    datornsSvar = "För mycket"
End If
' övar om dittSvar inte är lika med talet
If dittSvar <> talet Then
    dittSvar = InputBox(datornsSvar & ", gissa en gång till")
    dittSvar = CInt(dittSvar)
End If
End Sub
Sub slut()
If dittSvar=talet Then
    MsgBox("Ja, det är rätt! Grattis!")
Else
    MsgBox("Nej, talet var " & talet)
End If
End Sub
End Module


En källkod blir i regel lättare att kopiera till andra program om man så långt som möjligt bara ger variabler synlighet på procedurnivå.

Alternativt, i stället för att använda variabler med synlighet på modulnivå kan man skicka data till en subrutin när den anropas. Vi kan deklarera en eller flera parametrar till subrutinen som tar emot de data som vi skickar till den. Därigenom blir varje subrutin en självständig byggsten som lätt kan läggas in i olika program. Gränssnittet till andra byggstenar blir okomplicerat.

Här är ett exempel på en subrutin som anropas med ett värde som argument. Variabeln texten3 är deklarerad som parameter:


Module portabel
Sub Main()
calle("Anropar subrutinen calle")
calle("Anropar subrutinen calle en gång till")
End Sub
Sub calle(texten3)
   MsgBox(texten3)
End Sub
End Module


En subrutin kan också få ett värde indirekt från en variabel i ett värdeanrop:


Module portabel2
Sub Main()
Dim enText = "Första anropet av Calle"
calle(enText)
enText = "Sista anropet av Calle"
calle(enText)
End Sub
Sub calle(texten4)
MsgBox(texten4)
End Sub
End Module

 

Referensanrop

En subrutin kan också anropas i ett referensanrop med en variabel som argument, som i detta något enkla karaoke-program, där subrutinen rad3 anropas med variabeln sjungAnnat som argument. För att ange att det är ett referensanrop används nyckelordet ByRef:

Module grodorna
Sub Main()
Dim sjung
sjung="Små grodorna"
MsgBox(sjung)
MsgBox(sjung)
rad3(sjung)
' Här anropas subrutinen
MsgBox(sjung)
' Nu har variabeln sjung ett nytt värde
End Sub
Sub rad3(ByRef sjungAnnat)
' Här börjar subrutinen
  sjungAnnat = "är lustiga att se"
End Sub
' Här slutar subrutinen
End Module


När en subrutin tar emot referensanrop kan variabeln lätt ges ett nytt värde med hjälp av subrutinen. I ovanstående program har variabeln sjung först värdet "Små grodorna". Efter att subrutinen rad3 har anropats med variablen sjung kommer variabeln sjungAnnat - som bara har synlighet inom subrutinen - att använda samma minnesutrymme i datorn. Därför kommer subrutinen att tilldela värdet "är lustiga att se" både till sjung och till sjungAnnat. Efter att subrutinen är avslutad finns variabeln sjungAnnat inte kvar, men sjung behåller det nya värdet, och det värdet visas sedan i programmets sista meddelanderuta.

Källkoden i subrutinen rad3 är portabel - den gör samma sak som förut om den kopieras och klistras in i något annat program tack vare anropet med en variabel som kan få nya värden från subrutinen.

Tack vare möjligheten att göra referensanrop går det att konstruera subrutiner som skapar en hel lista med värden.

I det här exemplet används subrutinen Heltalsdivision till att ta fram både antalet timmar och antalet minuter från 127 minuter i ett enda referensanrop:


Module klockan
Sub Main()
Dim tim = 0, antal = 60, min = 127
MsgBox("Före anropet: " & tim & " timmar och " & min & " minuter")
Heltalsdivision(tim, antal, min)
MsgBox("Efter: " & tim & " timmar och " & min & " minuter")
End Sub
Sub Heltalsdivision(ByRef T, ByRef N, ByRef R)
' Operatorn \ beräknar heltalsdelen av en division
        T = R \ N
' Operatorn Mod beräknar resten av en heltalsdivision
        R = R Mod N
End Sub
End Module


Det går att blanda värdeanrop och referensanrop i en subrutins parameterlista.

Den här subrutinen, plusTio, kommer att ta referensanrop från de första två variablerna i anropet, medan den sista variabeln tas som värdeanrop:


Module blandat
Sub Main()
Dim a, b, c
a = 1
b = 2
c = 3
MsgBox("a, b och c före anropet: " & a & " " & b & " " & c)
plusTio(a, b, c)
MsgBox("a, b och c efter anropet: " & a & " " & b & " " & c)
End Sub
Sub plusTio(ByRef x, ByRef y, z)
x = x+10
y = y+10
z = z+10
End Sub
End Module


Meddelanderutan kommer att visa att a har blivit 11, b har blivit 12, men c är fortfarande 3.


Funktioner


Funktioner kan användas på samma sätt som subrutiner, men de kan dessutom returnera ett värde av valfri datatyp. När man skriver en funktion i VB med alla tillhörande komponenter börjar man på första raden med nyckelordet Function, sedan ett lämpligt namn, och sist på raden, inom parenteser, skriver man parameterlistan - namnen på de variabler som tar emot argumenten till funktionen. En funktion kan också skrivas utan parameterlista, men då ska namnet följas av ett par tomma parenteser. Därefter kommer de programsatser som funktionen ska utföra. På sista raden i funktionen skriver man End Function.

De variabler som namngivits i parameterlistan kan sedan användas i funktionen och man kan också deklarera nya variabler som är verksamma i funktionen på procedurnivå. Även variabler med synlighet på högre nivå kan användas.

Om du vill skicka tillbaka ett värde från funktionen skriver du en eller flera programsatser som anger returvärden. En funktion kan alltid ge ett returvärde. En funktion får ett returvärde i en vanlig tilldelningssats, där funktionsnamnet behandlas som en vanlig variabel som tilldelas ett värde.

En funktion som räknar ut det dubbla kan alltså skrivas så här. Funktionen anropas med talet 32:


Module fördubbla
Sub Main()
Dim talet
talet = 32
MsgBox("Fördubblar du " & talet & " blir det " & dubbelt(talet))
End Sub
Function dubbelt(taEmot)
Dim produkt
produkt = 2*taEmot
MsgBox("Nu arbetar funktionen! Resultat: " & produkt)
dubbelt = produkt
End Function
End Module


Om en funktion saknar parameterlista, behövs inte några parenteser i funktionsanropet - som i det här exemplet, där en funktion hämtar ett användarnamn och samtidigt kontrollerar att det är 8 tecken långt …


Module inloggning
Sub Main()
Dim namn
namn = hämtaAnvändarNamn
End Sub
Function hämtaAnvändarNamn()
Dim svar
svar = InputBox("Skriv ditt användarnamn","Inloggning")
' Funktionen Len() räknar antalet tecken i en textsträng
If Len(svar) <> 8 Then
    svar = InputBox("Ditt användarnamn?","Andra försöket")
End If
hämtaAnvändarNamn = svar
End Function
End Module


I dataspelet här nedan – Kvitt eller dubbelt – får du en slumpmässig insats mellan 1 och 100 kronor i början av spelet. Funktionen spela låter dig behålla din insats om du trycker på Nej-knappen i en dialogruta, men om du trycker på Ja-knappen får du satsa den i spelet. Då kan du antingen förlora den, behålla den oförändrad eller fördubbla den. Det finns en enda variabel – duHar – som är synlig på modulnivå, medan de övriga variablerna är synliga på procedurnivå:


Module spel
Private duHar
Sub Main()
Randomize
duHar = start
duHar = spela(duHar)
duHar = spela(duHar)
duHar = spela(duHar)
avslutning(duHar)
End Sub
Function start()
Dim slump, introtext
slump=1+Int(Rnd*100)
introtext = "I kvitt eller dubbelt får du välja tre gånger " & _
vbNewLine & " mellan att - å ena sidan - behålla din insats " & _
vbNewLine & " eller - å andra sidan - satsa den i ett spel " & _
vbNewLine & " där du kan behålla den, fördubbla den eller & _
vbNewLine & " förlora allt. Du har " & slump & " kronor"
MsgBox(introtext,, "Kvitt eller dubbelt?")
start = slump
End Function
Function spela(insats)
Dim erbjudande, svar, slumptal, faktor
erbjudande = "Vill du spela? Du kan satsa " & insats & " kr!"
' Argumentet vbYesNo ger en dialogruta med Ja- och Nej-knapp
svar = MsgBox(erbjudande, vbYesNo, "Spelomgången kan börja!")
' Vid Ja blir svar 1, vid Nej blir svar 0
slumptal = Int(Rnd*3)-1
If svar = vbYes Then
' Om spelaren har tryckt på Ja-knappen …
    svar = 1
Else
' Annars …
    svar = 0
End If
faktor = svar*slumptal + 1
' faktor blir 1 vid ett Nej, men 0, 1 eller 2 vid ett Ja
spela = insats*faktor
End Function
Sub avslutning(kvar)
MsgBox("Efter tre spelomgångar har du " & kvar & " kr!")
End Sub
End Module


Procedurer kan avslutas i förtid med nyckelordet Exit. Programsatsen Exit Sub avslutar en subrutin och Exit Function avslutar en funktion. Som exempel kan vi kika på ett program med en funktion som gör divisioner. Märk att om användaren försöker det omöjliga att dividera med 0 eller att dividera 0 med 0, avslutas funktionen med Exit Function innan divisionen har utförts:


Module dividera
Sub Main()
Dim täljare, nämnare
täljare = InputBox("Skriv täljare!")
nämnare = InputBox("Skriv nämnare!")
MsgBox("Svaret är " & dividera(täljare, nämnare))
End Sub
Function dividera(T, N)
If N=0 And T=0 Then
' Om både N och T är 0 …
  dividera = "odefinierat"
    Exit Function
ElseIf N=0 Then
' I annat fall, om N är 0 …
  dividera = "oändligt"
    Exit Function
End If
' Här utförs divisionen:
    dividera = T/N
End Function
End Module


I källkoden är Exit-satserna nödvändiga för att undvika programstopp.


Vilken Main-procedur?

Det finns fyra olika sätt att utforma proceduren Main med kombinationerna att …

Om den inte kan returnera ett värde skrivs den som subrutin, om den kan returnera ett värde skrivs den som funktion. Den enklaste varianten av en applikation med en Main-procedur, skriven som subrutin, som varken tar emot eller lämnar ifrån sig argument är den som vi har använt hittills:


Module enkeltMainexempel
Sub Main()
MsgBox("Main-proceduren startar!")
End Sub
End Module


Om man vill att Main ska returnera en sifferkod används syntaxen för en funktion. Returvärdet får datatypen Integer, eftersom Main-funktionens datatyp deklareras som Integer:


Module funktionsExempel
Function Main()
As Integer
MsgBox("Main-proceduren startar!")
Return 0
End Function
End Module


I standardfallet kan man använda felkoden 0, som kan läsas som ”noll fel”.

Båda varianterna av Main kan dessutom skrivas så att de tar emot ett fält av textsträngar som argument. Om programmet test.exe startas med kommandot test ole dole doff från kommandoprompten kommer de textsträngar som då skapas, " ole", "dole" och "doff", att bli tre element i ett fält som programmet har tillgång till. I Main-subrutinens parameterlista deklareras ett fält med namnet argumenten. Uttrycket UBound(argumenten, 1) ger indexnumret för det sista elementet i fältet:


Module argumentMottagning
Sub Main(argumenten() As String)
Dim mottaget As String = "Mottagna argument:"
If argumenten.Length > 0 Then
' Om det finns något alls i fältet argumenten …
    For argNr As Integer = 0 To UBound(argumenten, 1)
    ' Går igenom samtliga element i argumenten
    ' från element nr 0 till övre gränsen av fältet

        mottaget = mottaget & " " &
    Next argNr
End If
MsgBox(mottaget)
End Sub
End Module


Programmet kommer att visa de mottagna argumenten i en meddelanderuta. Märk att fältet argumenten inte kan ta emot de svenska tecknen å, ä och ö. En lösning på problemet är använda samma bokstavskombinationer som i personnamnen på flygbiljetter, aa i stället för å, ae i stället för ä och oe i stället för ö.

En kombinerad Main-funktion som både tar emot argument och lämnar en sifferkod, kan se ut så här. Det är ett program som tar emot ett namn på en person och sedan ger personen ett slumpmässigt id-nummer. Namn och id-nummer visas sedan i en meddelanderuta:


Module enKombination
Function Main(argumenten() As String) As Integer
Dim namn As String = ""
If argumenten.Length > 0 Then
    For argNr As Integer = 0 To UBound(argumenten, 1)
        namn = namn & " " & argumenten(argNr)
    Next argNr
End If
Randomize()
Dim slump As Double = Rnd * 1000
Dim idNr As Integer = CInt(slump)
MsgBox(namn & vbNewLine & "har id-nummer " & idNr)
Return 0
End Function
End Module



Grafiskt användargränssnitt med formulär

Med hjälp av formulär kan man utveckla det grafiska användargränssnittet i Visual Basic till en professionell nivå. Formulären kan ha rubriker, texter, inmatningsfält för texter, knappar, checkboxar med mera. Formulär skapas från klassen Form som finns i den redan färdigställda namnrymden System.Windows.Forms.

I samma namnrymd finns också klassen Label som kan ge ett etikettliknande grafiskt objekt med en text som kan infogas i formuläret. Kompilatorn får enklast tillgång till klasserna Form och Label genom att importera namnrymden System.Windows.Forms med satsen Imports System.Windows.Forms


Imports System.Windows.Forms
Module formulärexempel1
Sub
' Skapar ruta1, ett Form-objekt
Dim ruta1 As New Form()
' Beskrivande text för rubriken
ruta1.Text = "En enkel rubrik"
' Skapar etikettText,
Dim etikettText As New Label()
' Ger Label-objektet texten "En kort text ..."
etikettText.Text = "En kort text ..."
' Infogar Label-objektet i formuläret
ruta1.Controls.Add(etikettText)
' Visar det färdiga formuläret
ruta1.ShowDialog()
End Sub
End Module


Formuläret har rubriken "En enkel rubrik" och en etikett-text: "En kort text ..." mot grå bakgrund. När det färdiga programmet körs kan användaren ge formuläret en ny bredd och höjd med drag och släpp-metoden.


Om man inte vill inkludera hela namnrymden System.Windows.Forms i programmets källkod kan man i stället ange sökvägarna till klasserna Form och Label var för sig:

Module formulärexempel2
Sub Main()
Dim ruta1 As New
System.Windows.Forms.Form()
ruta1.Text = "En enkel rubrik"
Dim etikettText As New
System.Windows.Forms.Label()
etikettText.Text = "En kort text ..."
ruta1.Controls.Add(etikettText)
ruta1.ShowDialog()
End Sub
End Module


För att färglägga formulär och etikett med egna färger behöver kompilatorn tillgång till namnrymden System.Drawing. Där finns metoden Color.FromName() som skapar ett färgobjekt med hjälp av ett färgnamn från System.Drawing (i exemplet nedan används VB-färgnamnen "LightSkyBlue" och "White").

Färgobjekten kan sedan tilldelas formulärets och etikettens egenskaper för bakgrundsfärg, BackColor: Hela formuläret kan dessutom göras svagt genomskinligt genom att egenskapen för grafisk synlighet på skrivbordet, Opacity, får värdet 0.8 (motsvarar 80 % grafisk synlighet):


Imports System.Windows.Forms
Imports System.Drawing
Module formulärexempel3
Sub Main()
Dim ruta1 As New Form()
ruta1.Text = "Ett enkelt blått formulär"
' Färgen ljusblå
Dim ljusblå As Color = Color.FromName("LightSkyBlue")
' Formuläret får bakgrundsfärgen ljusblå
ruta1.BackColor = ljusblå
' Formuläret blir svagt genomskinligt
ruta1.Opacity = 0.8
Dim eText As New Label()
eText.Text = "En vit etikett"
' Definierar färgen vitt
Dim vitt As Color = Color.FromName("White")
' Etiketten får bakgrundsfärgen vitt
eText.BackColor = vitt
ruta1.Controls.Add(eText)
ruta1.ShowDialog()
End Sub
End Module




I följande avsnitt ska vi se närmare på hur formulärets design kan utformas och ändras och hur olika grafiska objekt kan knytas till händelser.


Instanser av objekt

Vill man använda egenskaper och metoder hos de icke exponerade objekten måste man först skapa en instans - en förekomst - av objektet. Av den anledningen brukar objektets egenskaper och metoder ibland kallas instansegenskaper och instansmetoder. Nyckelordet New kan skapa ett nytt objekt från en klass som kompilatorn har tillgång till.



Avancerad OOP i Visual Basic

OOP handlar om att använda klasser och objekt på ett sätt som stödjer programmeringslogik, överskådlighet och organisation. Nu ska vi kika närmare på de grafiska objekten i VB och vi ska ta upp två programmeringsmetoder som har fått en omfattande användning i avancerad OOP: klassarv och konstruktorer.

Grafiska objekt

Form-objekten skapas, som vi sett tidigare, från klassen Form som finns i namnrymden System.Windows.Forms. I samma namnrymd finns också klassen Button. Med hjälp av den klassen skapas ett användbart grafiskt objekt - en knapp, knappen kan sedan förses med en förklarande text och den kan infogas i formuläret :


Imports System.Windows.Forms : Imports System.Drawing
Module formulärexempel4
Sub Main()
Randomize()
Dim ruta1 As New Form()
ruta1.Text = "Formulär med olika färger"
Dim gult As Color = Color.FromName("Yellow")
Dim grönt As Color = Color.FromName("Green")
Dim rött As Color = Color.FromName("Red")
Dim valdFärg As Color = gult
If Rnd < 0.5 Then
    valdFärg = grönt
Else
    valdFärg = rött
End If
ruta1.BackColor = valdFärg
Dim knapp As New Button()
knapp.Text = "En gul knapp"
knapp.BackColor = gult
ruta1.Controls.Add(knapp)
ruta1.ShowDialog()
End Sub
End Module


Formuläret kommer slumpmässigt att bli antingen rött eller grönt. Knappen kommer att få gul bakgrundsfärg med texten ”En gul knapp”.


OOP-metoder

En klass kan innehålla klassvariabler och klassmetoder som inte var för sig tillhör de objekt som skapas av klassen. De är variabler och metoder som delas av alla objekt från en klass. En klassvariabel kan alltså bara ha ett enda värde, värdet kan läsas av och eventuellt ändras av alla objekt från den klassen, men objekten kan inte ha olika värden samtidigt på en sådan variabel. På samma sätt kan det inte finnas olika versioner av en klassmetod. Klassvariabler och klassmetoder deklareras med nyckelordet Shared.

Även subrutinen Main() kan ingå i en klass: Om ett objekt av klasssen skapas från subrutinen Main() kan det objektet köras som ett program, förutsatt att subrutinen Main() deklareras som Shared så att det är möjligt att få en ingångspunkt till programmet. Deklarationen Shared markerar att det bara finns en version av subrutinen – den som tillhör klassen. Utan den deklarationen skulle det ju inte gå att använda Main-metoden utan att första skapa ett objekt – en programmeringslogisk omöjlighet, eftersom det är just i Main-metoden som objektet måste skapas för att programmet ska komma igång!

Här ingår en subrutin Main(), deklarerad som Shared, i klassen Rutexempel. Ett objekt av den egna klassen skapas med New Rutexempel(). Objektet körs sedan som program av metoden Application.Run(). Klassen Rutexempel ärver alla egenskaper och metoder från klassen Form med hjälp av en enda programsats. Programsatsen som implementerar klassarvet är Inherits Form. Källkoden i konstruktorn New() kommer sedan som standard att förse nya objekt av klassen med rubriken "Klassen Rutexempel" i formuläret och en knapp märkt "Knapp". Nyckelordet Me (med punkt efter) syftar på det objekt som konstruktorn kan skapa. Me.Text är en egenskap som syftar på det blivande Form-objektets text, medan Me.Controls.Add(minKnapp) är en metod som lägger till knappen minKnapp till de kontroller som det blivande objektet erbjuder.


Imports System.Windows.Forms
Public Class Rutexempel
' Rutexempel ärver från Form:
Inherits Form
Public minKnapp As Button
Public
Me.Text = "Klassen Rutexempel"
minKnapp = New Button()
minKnapp.Text = "Knapp"
Me.Controls.Add(minKnapp)
End Sub
Shared Sub Main()
Application.Run(New Rutexempel())
' Skapar ett objekt av den egna klassen
' och exekverar det med Application.Run()

End Sub
End Class


Om man vill att två Form-objekt ska öppnas i sekvens (först en blankett – och sedan, när blanketten stängs – en blankett till) går det att ordna genom att skapa två Form-objekt i Main-metoden, det ena efter det andra. Här kommer blanketterna att få olika färger slumpmässigt:


Imports System.Windows.Forms
Imports System.Drawing
Public Class Sekvens
Inherits Form
Public Sub New()
Randomize()
Dim slump As Double
Me.Text = "Ett formulär med färg"
' Färgen ljusblått
Dim ljusblått As Color = Color.FromName("LightSkyBlue")
' Färgen blått
Dim blått As Color = Color.FromName("Blue")
' Färgen rött
Dim rött As Color = Color.FromName("Red")
' Färgen grönt
Dim grönt As Color = Color.FromName("Green")
Dim valdFärg As Color
valdFärg = grönt
slump = Rnd
' Färg med sannolikheten 2/3
If slump < 0.33 Then
    valdFärg = ljusblått
ElseIf slump > 0.33 And slump < 0.67
  valdFärg = rött
End If
' Behåller färgen grönt med sannolikheten 1/3
Dim oKnapp As New Button()
oKnapp.Text = "Knapptext"
valdFärg = blått
' Färg med sannolikheten 2/3
slump = Rnd
If slump < 0.33 Then
    valdFärg = ljusblått
ElseIf slump > 0.33 And slump < 0.67 Then
    valdFärg = rött
End If
' Behåller färgen blått med sannolikheten 1/3
oKnapp.BackColor = valdFärg
Me.Controls.Add(oKnapp)
End Sub
Shared Sub Main()
Application.Run(New Sekvens())
Application.Run(New Sekvens())
' Skapar två objekt av den egna klassen
' i sekvens och exekverar med Application.Run()
End Sub
End Class



Händelser i Visual Basic

Ett formulär kan innehålla flera möjligheter till interaktion med användaren, som till exempel knappar, inmatningsfält för text, alternativknappar, kombirutor, programmets igångsättning och checkboxar. Det behövs särskilda händelsehanterare som tar hand om händelsen då användaren startar programmet, klickar på en knapp, skriver in en text i ett inmatningsfält med mera.

För att hantera programmets igångsättning med ett formulär används formulärmetoden Load och en särskild subrutin med ett namn som slutar på _ och ett namntillägg samma som metoden, det vill säga _Load. Händelsehanteraren som tar hand om igångsättningen av formuläret ruta1 får i det här exemplet namnet ruta1.Load med en hänvisning till subrutinen start_Load. Den här programsatsen knyter samman händelsen med den subrutin som ska fånga upp den:

AddHandler ruta1.Load, AddressOf start_Load

Anropet av subrutinen görs utan argument:


Imports System.Windows.Forms
Module händelseexempel1
Sub Main()
Dim ruta1 As New Form()
ruta1.Text = "Formulär med händelse"
' Händelsehanteraren ruta1.Load:
AddHandler ruta1.Load, AddressOf start_Load
ruta1.ShowDialog()
End Sub
Private Sub start_Load()
MsgBox("Formuläret kommer att aktiveras!", , "Start")
End Sub
End Module


För att hantera en klickning med vänster musknapp används metoden Click och en särskild subrutin med ett namn som slutar på _ och ett namntillägg samma som metoden, det vill säga Click.

I det här exemplet får den händelsehanterare som tar hand om en klickning på formuläret namnet ruta1.Click med hänvisning till subrutinen klickning_Click:


Imports System.Windows.Forms
Module formulärexempel5
Sub Main()
Dim ruta1 As New Form()
ruta1.Text = "Formulär med två händelser"
' Händelsehanterare för Load
AddHandler ruta1.Load, AddressOf start_Load
' Händelsehanterare för Click
AddHandler ruta1.Click, AddressOf klickning_Click

ruta1.ShowDialog()
End Sub
Private Sub start_Load()
MsgBox("Formuläret kommer att aktiveras!", , "Start")
End Sub
Private Sub klickning_Click()
MsgBox("Du klickade på formuläret!", , "Klick")
End Sub
End Module


Om formuläret innehåller knappar eller andra kontrollobjekt kan inte händelsehanterare ta emot en klickning på själva formuläret. Användaren kan bara klicka eller dubbbelklicka på kontrollobjekten.

För att hantera en klickning på en knapp med namnet knapp1 behövs alltså händelsehanteraren knapp1.Click med en hänvisning till en subrutin som hej_Click. Den här programsatsen knyter samman händelsen med den subrutin som ska fånga upp den:


AddHandler knapp1.Click, AddressOf hej_Click

Anropet av subrutinen görs utan argument, men för att kunna skilja mellan klickningar på olika knappar kommer subrutinen att från systemet ta emot två argument som värden: Objektet sender, som talar om vilken knapp som har tryckts ned, och en eventuell felkod eller händelsekod, e.

Följande program kommer att visa ett formulär med en knapp märkt "Startknapp". Om användaren trycker på knappen aktiveras subrutinen hej_Click som visar en meddelanderuta med texten "Hej. Här börjar händelsen!"

Imports System.Windows.Forms
Module händelseexempel2
Sub Main()
Dim ruta1 As New Form()
ruta1.Text = "Formulär med händelse"
Dim knapp1 As New Button()
knapp1.Text = "Startknapp"
ruta1.Controls.Add(knapp1)
' Händelsehanteraren knapp1.Click
AddHandler knapp1.Click, AddressOf hej_Click
ruta1.ShowDialog()
End Sub
Sub hej_Click(ByVal sender As Object, ByVal e As System.EventArgs)
MsgBox("Hej. Här börjar händelsen!")
End Sub
End Module


Två eller flera kontrollobjekt i ett formulär kan aktivera var sin händelse, som i sin tur aktiverar var sin subrutin. De kan också dela på samma subrutin.

I formuläret i nästa exempel är knapparna märkta ”Aktuell lokal tid” och ”Aktuell UTC-tid”.

För att undvika att knapparna hamnar ovanpå varandra i layouten har den ena knappens övre vänstra hörn placerats i koordinaten 10, 20 (det vill säga att hörnet är 10 pixlar från vänsterkanten av formuläret och 20 pixlar från överkanten). Den andra knappens övre vänstra hörn har placerats i koordinaten 10, 120.

Knapparna har samma utseende, en bredd på 100 pixlar och en höjd på 50 pixlar.

En tryckning på knappen ”Aktuell lokal tid” tar fram en meddelanderuta med datum och klockslag för lokal tid (svensk tid om datorn är inställd på svensk tidzon). En tryckning på knappen ”Aktuell UTC-tid” tar fram datum och klockslag enligt UTC och GMT …


Imports System.Windows.Forms
Module händelseexempel3
Sub Main()
Dim ruta2 As New Form()
ruta2.Text = "Klocka med tidzoner"
Dim knapp1 As New Button()
knapp1.Text = "Aktuell lokal tid"
knapp1.SetBounds(10, 20, 100, 50)
' Knappens övre vänstra hörn får koordinater 10 och 20
' bredden blir 100 och höjden 50 pixlar

ruta2.Controls.Add(knapp1)
AddHandler knapp1.Click, AddressOf lokaltid_Click
Dim knapp2 As New Button()
knapp2.Text = "Aktuell UTC-tid"
knapp2.SetBounds(10, 120, 100, 50)
' Knappens övre vänstra hörn får koordinater 10 och 120
' bredden blir 100 och höjden 50 pixlar

ruta2.Controls.Add(knapp2)
AddHandler knapp2.Click, AddressOf utctid_Click
ruta2.ShowDialog()
End Sub
Sub lokaltid_Click(ByVal sender As Object, _
ByVal e As System.EventArgs)
MsgBox("Lokal tid: " & My.Computer.Clock.LocalTime)
End Sub
Sub utctid_Click(ByVal sender As Object, _
ByVal e As System.EventArgs)
MsgBox("UTC-tid: " & My.Computer.Clock.GmtTime),
End Sub

End Module


Om man vill att ett grafiskt objekt ska ha åtkomst till fler grafiska egenskaper och metoder, behövs även import av namnrymderna System, System.ComponentModel och System.Drawing.

I nästa exempel skapas en ny klass, Specialruta, som ärver alla egenskaper från formulärklassen Form. Dessutom förses nya objekt av klassen med en knapp, startknapp, med x/y koordinaterna 30, 30, bredden 100, höjden 40 och knapptexten ”Nya färger”.

Knappens händelse Click knyts till subrutinen färg_Click. Subrutinen ändrar formulärets och knappens bakgrundsfärger och byter ut knappens text ”Nya färger” mot en tom textsträng. Programmet startas från Main-metoden, som ingår i klassen. Där skapas ett nytt objekt av klassen:


Imports System
Imports System.ComponentModel
Imports System.Drawing
Imports System.Windows.Forms
Public Class Specialruta
' Specialruta ärver alla egenskaper och metoder från klassen Form:
Inherits Form
Public startknapp As Button
' Dessutom innehåller en ny Specialruta följande:
Public Sub New()
startknapp = New Button()
startknapp.SetBounds(30, 30, 100, 40)
startknapp.Text = "Nya färger"
' Me refererar till det aktuella objektet, dvs formuläret
Me.Controls.Add(startknapp)
' Me.startknapp refererar till ett objekt av Button
AddHandler Me.startknapp.Click, AddressOf färg_Click
End Sub
Private Sub färg_Click(ByVal sender As Object, ByVal e As EventArgs)
MessageBox.Show("Byter färger till blått och gult")
Dim ljusblått As Color = Color.FromName("LightSkyBlue")
Me.BackColor = ljusblått
Dim gult As Color = Color.FromName("Yellow")
Me.startknapp.BackColor = gult
Me.startknapp.Text = ""
End Sub
Shared Sub Main()
' Aktiverar grafik i applikationen:
Application.EnableVisualStyles()
' Skapar ett objekt av klassen Specialruta:
Application.Run(New Specialruta())
End Sub
End Class

Formuläret kommer först att visas i standardfärgerna, med en knapp med texten
”Nya färger”, men efter en tryckning på knappen kommer formuläret att få en ljusblå bakgrundsfärg, medan knappen får en gul bakgrundsfärg utan text.

 

     



Textrutor

Textrutor är mycket praktiska om man behöver en blankett med flera fält där användaren kan mata in text. En textruta har klassnamnet TextBox. Ett objekt av TextBox utlöser i normalfallet händelsen TextChanged då användaren skriver in eller ändrar text i inmatningsfältet. En händelsehanterare kan hålla texten uppdaterad fortlöpande.

I nästa exempel kan användaren skriva in sitt förnamn i ett inmatningsfält, och efternamnet i ett annat. Hela namnet visas fortlöpande som en text längst ner i formuläret.

Imports System : Imports System.ComponentModel
Imports System.Drawing : Imports System.Windows.Forms
Public Class Namnruta
Inherits Form
Public fält1, fält2 As TextBox
Public etikett1, etikett2, etikett3 As Label
Public Sub New()
fält1 = New TextBox() : fält2 = New TextBox()
etikett1 = New Label() : etikett2 = New Label()
etikett3 = New Label()
fält1.SetBounds(60, 20, 100, 50)
fält2.SetBounds(60, 70, 100, 50)
Me.Controls.Add(fält1) : Me.Controls.Add(fält2)
etikett1.SetBounds(5, 20, 100, 50)
etikett2.SetBounds(5, 70, 100, 50)
etikett3.SetBounds(60, 120, 200, 50)
etikett1.Text = "Förnamn" : etikett2.Text = "Efternamn"
etikett3.Text = "Fullständigt namn"
Me.Controls.Add(fält1) : Me.Controls.Add(fält2)
Me.Controls.Add(etikett1) : Me.Controls.Add(etikett2)
Me.Controls.Add(etikett3)
AddHandler Me.fält1.TextChanged, AddressOf fNamn_TextChanged
AddHandler Me.fält2.TextChanged, AddressOf eNamn_TextChanged

End Sub
Private Sub fNamn_TextChanged(ByVal sender As Object, _
ByVal e As EventArgs)
Me.etikett3.Text = fält1.Text & " " & fält2.Text
End Sub
Private Sub eNamn_TextChanged(ByVal sender As Object, _
ByVal e As EventArgs)
Me.etikett3.Text = fält1.Text & " " & fält2.Text
End Sub
Shared Sub Main()
Application.EnableVisualStyles()
Application.Run(New Namnruta())
End Sub
End Class



De två händelsehanterarna skickar textändringar vidare, så att ändringar i fält1 skickas till subrutinen fNamn_TextChanged() och ändringar i fält2 skickas till subrutinen eNamn_TextChanged(). Båda subrutinerna är konstruerade så att de sätter samman förnamn och efternamn till ett fullständigt namn, som sedan visas av etiketten etikett3.

Märk att en av subrutinerna egentligen är överflödig, eftersom subrutinerna är identiskt lika. Båda händelsehanterarna kan alltså använda samma subrutin med samma slutresultat som förut. Om vi låter händelsehanterarna enbart anropa subrutinen fNamn kan vi helt stryka subrutinen eNamn. De två anropen av addHandler skulle kunna skrivas så här i stället:

AddHandler Me.fält1.TextChanged, AddressOf fNamn_TextChanged
AddHandler Me.fält2.TextChanged, AddressOf fNamn_TextChanged

Som vi såg i exemplet Sekvens kan vi skapa två eller flera formulär som visas det ena efter det andra. Med hjälp av en händelsehanterare kan vi också ta fram två eller flera formulär som visas samtidigt, beroende hur användaren hanterar sina formulär.

I exemplet skapas först ett formulär, sedan, när användaren klickar på formuläret, visas ett formulär till samtidigt med det första formuläret:

' Samtidigt
Imports System.Windows.Forms
Module Samtidigt
Sub Main()
Dim oRuta1 As New Form()
oRuta1.Text = "Formulär 1"
' Händelsehanteraren:
AddHandler oRuta1.Click, AddressOf start_Click
oRuta1.ShowDialog()
End Sub
Private Sub start_Click()
Dim oRuta2 As New Form
oRuta2.Text = "Formulär 2"
oRuta2.ShowDialog()
End Sub
End Module



Klassarv

Ett vanligt användningsområde för klassarv är att ta en färdigskriven klass från ett standardbibliotek i Visual Basic och sedan skapa en specialiserad klass för något särskilt ändamål genom att lägga till några egenskaper och metoder. Det går också, vid behov, att omdefiniera de egenskaper och metoder som redan finns – det är bara att skriva en ny version av egenskapen eller metoden i den ärvande klassens definition. Det går alltså att skapa en ny klass genom att bygga på en redan färdig klass med nya egenskaper och metoder. Klassarvet kodas med en Inherits-sats i klassdefinitionen. Om vi till exempel vill skapa en ny typ av blankett i stället för den standardblankett som skapas från Form-klassen, kan vi använda programsatsen Inherits Form.

I exemplet här nedan skapas en ny klass med namnet Namnruta. Den får två inmatningsfält för text och tre etiketter. I det ena fältet kan man skriva en temperatur i gradet Fahrenheit, då visas motsvarande temperatur i grader Celsius. I det andra fältet kan man skriva in en temperatur i grader Celsius. Då visas motsvarande temperatur i grader Fahrenheit.



Imports System
Imports System.ComponentModel
Imports System.Drawing
Imports System.Windows.Forms
Public Class Namnruta
Inherits Form
Public oFält1, oFält2 As TextBox
Public oLapp1, oLapp2, oLapp3 As Label
' Kontruktor:
Public Sub New()
oFält1 = New TextBox()
oFält2 = New TextBox()
oLapp1 = New Label()
oLapp2 = New Label()
oLapp3 = New Label()
oFält1.SetBounds(80, 20, 100, 50)
oFält2.SetBounds(80, 70, 100, 50)
Me.Controls.Add(oFält1)
Me.Controls.Add(oFält2)
oLapp1.SetBounds(5, 20, 120, 50)
oLapp2.SetBounds(5, 70, 120, 50)
oLapp3.SetBounds(60, 120, 200, 50)
oLapp1.Text = "Celsius"
oLapp2.Text = "Fahrenheit"
oLapp3.Text = "Resultat"
Me.Controls.Add(oFält1)
Me.Controls.Add(oFält2)
Me.Controls.Add(oLapp1)
Me.Controls.Add(oLapp2)
Me.Controls.Add(oLapp3)
AddHandler Me.oFält1.TextChanged, AddressOf celsius_TextChanged
AddHandler Me.oFält2.TextChanged, AddressOf fahr_TextChanged
End Sub
Private Sub celsius_TextChanged(ByVal sender As Object, _
ByVal e As EventArgs)
Dim f As Double
f = 1.8 * CDbl(oFält1.Text) + 32
f = CInt(f + 0.5)
Me.oLapp3.Text = oFält1.Text & " C = " & f & " F"
End Sub
Private Sub fahr_TextChanged(ByVal sender As Object, _
ByVal e As EventArgs)
Dim c As Double
c = (CDbl(oFält2.Text) - 32) / 1.8
c = CInt(c + 0.5)
Me.oLapp3.Text = oFält2.Text & " F = " & c & " C"
End Sub
Shared Sub Main()
Application.EnableVisualStyles()
Application.Run(New Namnruta())
End Sub
End Class

Här är ett krypteringsprogram. Användaren skriver en okrypterad textrad i det ena fältet och en krypteringsnyckel i det andra. Då visas den krypterade texten längst ner i blanketten. Kryperingmetoden är ett enkelt Caesar-krypto.

' Användning:' Ordet får vara på högst 4 bokstäver
Imports System
Imports System.ComponentModel
Imports System.Drawing
Imports System.Windows.Forms
Public Class Krypto
Inherits Form
Public oFält1, oFält2 As TextBox
Public oLapp1, oLapp2, oLapp3 As Label
Public Sub New()
oFält1 = New TextBox()
oFält2 = New TextBox()
oLapp1 = New Label()
oLapp2 = New Label()
oLapp3 = New Label()
oFält1.SetBounds(110, 20, 100, 50)
oFält2.SetBounds(110, 70, 100, 50)
' Användaren ger nödvändigt initialvärde för oFält2:
oFält2.Text = InputBox("Skriv krypteringsnyckel")
' Här anges nyckel för kryptering eller dekryptering
Me.Controls.Add(oFält1)
Me.Controls.Add(oFält2)
oLapp1.SetBounds(5, 20, 120, 50)
oLapp2.SetBounds(5, 70, 120, 50)
oLapp3.SetBounds(60, 120, 200, 50)
oLapp1.Text = "Originaltext"
oLapp2.Text = "Krypteringsnyckel"
oLapp3.Text = "Ny text"
Me.Controls.Add(oFält1)
Me.Controls.Add(oFält2)
Me.Controls.Add(oLapp1)
Me.Controls.Add(oLapp2)
Me.Controls.Add(oLapp3)
AddHandler Me.oFält1.TextChanged, AddressOf krypt_TextChanged
AddHandler Me.oFält2.TextChanged, AddressOf krypt_TextChanged
End Sub

Private Sub krypt_TextChanged(ByVal sender As Object, _
ByVal e As EventArgs)
' Enkel algoritm för caesarkrypto
Dim ordet As String = "Originaltext"
Dim nyckel As Integer = 0
Dim max As Integer = 1
Dim txt As String = ""
Dim i As Integer = 1
Dim tkn As Char = "A"c
Dim nyttNr As Integer = 0
Dim nyttTkn As Char = "A"c
ordet = oFält1.Text
nyckel = CInt(oFält2.Text)
max = Len(ordet)
txt = ""
' For-slingan går igenom tecknen i texten och
' skapar motsvarande krypterade tecken i txt
For i = 1 To max
    tkn = Mid(ordet, i, 1)
    nyttNr = AscW(tkn) + nyckel
    nyttTkn = ChrW(nyttNr)
    txt = txt & tkn & " " & nyttTkn & vbNewLine
Next
' Den krypterade texten visas i oLapp3
Me.oLapp3.Text = txt
End Sub
Shared Sub Main()
Application.EnableVisualStyles()
Application.Run(New Krypto())
End Sub
End Class


Till startsidan

Frivilliga bidrag till arbetet med Visual Basic från kommandoprompten kan swishas till 0761145895. Märk meddelandet "VB".