JavascriptProva

sabato 31 agosto 2013

Prima applicazione con i thread.

Il tutorial dice di importare System.threading.
L'ho già fatto impostando i riferimenti, non da codice, dunque: dovrebbe essere la stessa cosa.

Ora dichiariamo il thread, nella sezione proprietà del form...

Public Class Form1

    Private mioThread As Thread

    Private Sub Form1_Load(ByVal sender As Object, ByVal e As System.EventArgs) Handles Me.Load

    End Sub

    Private Sub Button1_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles Button1.Click
        MsgBox("questo è il thread principale")
    End Sub
End Class
Dopo la dichiarazione, l'istanziazione del thread con l'istruzione New.
    Private Sub Form1_Load(ByVal sender As Object, ByVal e As System.EventArgs) Handles MyBase.Load

        mioThread = New Thread(AddressOf CompitoDelThread)
        mioThread.IsBackground = True
        mioThread.Start()
    End Sub
che fa riferimento al "compito del thread", una semplice Sub aggiunta alla classe Form1:
    Private Sub CompitoDelThread()
        Dim contatore As Integer = 0
        Do
            If contatore > 10 Then
                MsgBox("arrivato!")
            End If
            contatore = contatore + 1
            Thread.Sleep(1000)
        Loop
    End Sub
Ed ecco: dopo 10 secondi, quando il valore del numero che ho predisposto nel "compito del thread" ha raggiunto l'opportuno valore, arriva la seconda MessageBox.
in pratica, funziona!
Una simpatica variante, mescolando le nozioni che ho ripassato sulla sintesi vocale, facendosi avvertire dalla voce della bella Silviotta ("ScanSoft Silvia_Dri40_16kHz") quando il numero aumentato in sottofondo dal mio secondo thread raggiunge il valore voluto:
Public Class Form1
    Private voce As New SpeechSynthesizer
    Private mioThread As Thread

    Private Sub Form1_Load(ByVal sender As Object, ByVal e As System.EventArgs) Handles MyBase.Load
        voce.SelectVoice("ScanSoft Silvia_Dri40_16kHz")

        mioThread = New Thread(AddressOf CompitoDelThread)
        mioThread.IsBackground = True
        mioThread.Start()
    End Sub

    Private Sub Button1_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles Button1.Click
        MsgBox("questo è il thread principale")
    End Sub

    Private Sub CompitoDelThread()
        Dim contatore As Integer = 0
        Do
            If contatore > 20 Then
                voce.Speak("Siamo arrivati al numero giusto, stronzo!")
            End If
            contatore = contatore + 1
            Thread.Sleep(1000)
        Loop
    End Sub
End Class
...e funziona ancora!

Uso di un'interfaccia da passare come parametro per il posizionamento relativo di oggetti in un form.

Per arrangiare spazialmente una serie di oggetti creati tutti a runtime, ho avuto l'idea di creare un'interfaccia che ho chiamato ISpazio, contenente le sole proprietà spaziali di questi oggetti personalizzati, i quali debbono implementarla, in modo che questa interfaccia venga passata come parametro ad altri oggetti, che adeguino la loro posizione di conseguenza.

Ho creato una classe prospettoCalendario in una libreria di classi:
Public Class prospettoCalendario(Of G As {New, Control}, T As {New, Control})
e una classe, sempre nella stessa libreria, chiamata nameGrid:
Public Class nameGrid(Of T As {New, Control}, N As {New, Control})
Nel modulo principale del programma, ho istanziato queste classi:
Public Class Form1

    Dim mioProspettoCalendario As prospettoCalendario(Of casella, turno)
    Dim miaGrigliaNomi As nameGrid(Of nome, numero)
    Dim bttSalva As bttSave


    Private Sub Form1_Load(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles MyBase.Load


        mioProspettoCalendario = New prospettoCalendario(Of casella, turno)(Me, 2013, 8, , 50, , , 5)

        miaGrigliaNomi = New nameGrid(Of nome, numero)(Me, 3, mioProspettoCalendario, , 50)

.......
Nel costruttore di ciascuna delle classi bisogna che fornisca un altro oggetto cui fare riferimento per il posizionamento spaziale degli oggetti che da queste classi vengono istanziati.

Per fare questo, inizialmente, non potendo passare una classe come parametro, ho passato direttamente l'oggetto concreto, ma trattandosi di una libreria di classi che presuppone un eventuale riutilizzo del codice questa è una fortissima limitazione, in quanto praticamente l'uso della classe si troverebbe vincolato al nome dell'oggetto.
Ho quindi avuto l'idea di creare, nella libreria di classi, un'interfaccia che esponga soltanto il comportamento spaziale di un oggetto:
Public Interface ISpazio
    Property Left As Integer
    Property Top As Integer
    Property Width As Integer
    Property Height As Integer
End Interface
...che viene così implementata dalla classe prospettoCalendario i cui oggetti istanziati serviranno come riferimento per il posizionamento spaziale degli oggetti istanziati da nameGrid:
Public Class prospettoCalendario(Of G As {New, Control}, T As {New, Control})
    Implements ISpazio

........

Public Property Left As Integer Implements ISpazio.Left
        Get
            Return _Left
        End Get
        Set(ByVal value As Integer)
            _Left = value
        End Set
    End Property
    Private _Top As Integer
    Public Property Top As Integer Implements ISpazio.Top
        Get
            Return _Top
        End Get
        Set(ByVal value As Integer)
            _Top = value
        End Set
    End Property
    Private _Width As Integer
    Public Property Width As Integer Implements ISpazio.Width
        Get
            Return _Width
        End Get
        Set(ByVal value As Integer)
            _Width = value
        End Set
    End Property

    Private _Height As Integer
    Public Property Height As Integer Implements ISpazio.Height
        Get
            Return _Height
        End Get
        Set(ByVal value As Integer)
            _Height = value
        End Set
    End Property
...e dunque nel costruttore di nameGrid appare l'interfaccia ISpazio passata come parametro:
Sub New(ByRef frm As Form, ByVal nCaselle As Integer, Optional ByRef refObj As ISpazio = Nothing, Optional ByVal refXDistance As Integer = 100, Optional ByVal refYDistance As Integer = 0, Optional ByVal HSpazio As Integer = 0, Optional ByVal VSpazio As Integer = 0)
(che appare come parametro opzionale per mia scelta).

mercoledì 28 agosto 2013

Aggiunta evento in controllo creato a runtime (la sapevo ma l'avevo scordata)

Bene.
Il primo passo lo abbiamo fatto, nell'ambito del progetto: abbiamo creato un file con FileOpen in modalità Random.
Ora dobbiamo essere capaci di inserire una merdarella qualunque nelle TextBoxes e salvarla in questo maledetto file...

Ach! Non sono TextBoxes, ma caselle che ereditano da Label, per cui non è possibile inserirci niente. Devo creare una classe apposita che erediti da TextBox.

Fatto nel giro di pochissimi secondi!

Ora si può ragionare meglio...
Ci vuole anche un Button che dia il comando di salvare le suddette merdarelle nel file.
Inseriamo il Button...

Fatto... ma devo rivedere come si assegna un evento a un controllo generato a runtime..
Ecco:

.......


Dim bttSave As New Button
        With bttSave

            .Left = miaGrigliaNomi.nomi(0).left
            .Top = miaGrigliaNomi.nomi(miaGrigliaNomi.nomi.Count - 1).top + miaGrigliaNomi.nomi(miaGrigliaNomi.nomi.Count - 1).height
            .BackColor = Color.Gray
            .Width = 200
            .Height = 50
            .Text = "Salva"
        End With
        Me.Controls.Add(bttSave)
        AddHandler bttSave.Click, AddressOf eventoBottone
    End Sub

    Sub eventoBottone()
        MsgBox("cliccato bottone, AUGH!")
    End Sub
Fatto, semplicemente!

giovedì 22 agosto 2013

Scrittura di records in un file ad accesso casuale partendo da textboxes.

Questo tentare di trovare una soluzione per memorizzare in un file ad accesso casuale i dati presi da una serie di textboxes è piuttosto impegnativo.
Più che altro, voglio evitare di costruire soluzioni appiccicaticce e arrangiaticce.

Ecco il codice che scrive in un file i contenuti di una serie di caselle di testo create tramite la mia classe griglia(Of T).
    Private Sub Button1_Click(ByVal sender As Object, ByVal e As System.EventArgs) Handles Button1.Click

        FileOpen(1, "C:\users\antonello\fileTest", OpenMode.Random, , , 10)

        For n As Integer = 0 To gridNames.Count - 1
            Dim variabile As persona = Nothing
            If gridNames(n).text.ToString = "" Then Return
            Try
                variabile.nome = gridNames(n).text.ToString.Substring(0, 8)
            Catch ex As Exception
                variabile.nome = gridNames(n).text.ToString
            End Try

            FilePut(1, variabile.nome, n + 1)
        Next
        FileClose(1)
    End Sub
Cerco di farne una descrizione in termini "umani".
Dopo aver aperto il file, per ognuna delle caselle di testo, inserite nella matrice ArrayList chiamata gridNames:
  • creo una variabile del tipo personalizzato persona, destinata ad accogliere il contenuto delle caselle di testo.
  • se il contenuto della casella è nullo, esco dalla routine.
  • Poi tramite una gestione dell'errore gestisco l'eventualità che il testo della casella di testo sia troppo lungo, e inserisco il suo contenuto nel membro "nome" della variabile.
  • Inserisco nel file il record corrispondente.


Mi sembra piuttosto ben fatto, anche se ho dei dubbi sulla necessità di gestire l'errore e sulla necessità dell'uso della variabile in cui "versare" il contenuto della casella di testo.

mercoledì 21 agosto 2013

Una funzione per raccogliere in una ArrayList controlli dello stesso tipo con parte del nome uguale.

Ho elaborato una funzione che mi permette di inserire in una ArrayList tutti gli elementi che hanno uno stesso nome o parte del nome.
    Function CtrlByName(ByRef frm As Form, ByVal nome As String) As ArrayList
        Dim matrice As New ArrayList
        For Each elemento In frm.Controls
            Dim stringa As String = elemento.name
            Try
                If stringa.Substring(0, nome.Length) = nome Then matrice.Add(elemento)
            Catch ex As Exception
            End Try
        Next
        Return matrice
    End Function
La funzione è un ArrayList.
Ecco come viene usata:
    Private Sub Form1_Load(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles MyBase.Load
        Dim lista As ArrayList
        lista = CtrlByName(Me, "TextBox")
        For n = 0 To lista.Count - 1
            lista(n).text = "Io faccio parte della matrice"
        Next
    End Sub
In questo modo la scritta "Io faccio parte della matrice" viene scritta solo nei controlli che hanno il nome che inizia per "TextBox".
Si potrebbe renderla più precisa limitandola per tipo di controlli (se il controllo non ha la proprietà Text si genererebbe un errore).

    Function CtrlByName(ByRef frm As Form, ByVal nome As String) As ArrayList
        Dim matrice As New ArrayList
        For Each elemento In frm.Controls
            Dim stringa As String = elemento.name
            Try
                If TypeOf elemento Is TextBox And stringa.Substring(0, nome.Length) = nome Then matrice.Add(elemento)
            Catch ex As Exception
            End Try
        Next
        Return matrice
    End Function
    
End Class
Ovviamente, il tipo deve essere flessibile, quindi andrebbe riportato fra i parametri...

Ecco risolto! Ovviamente, un tipo non si può passare come parametro, ma si può usare il generic:
    Function CtrlByName(Of T)(ByRef frm As Form, ByVal nome As String) As ArrayList
        Dim matrice As New ArrayList
        For Each elemento In frm.Controls
            Dim stringa As String = elemento.name
            Try
                If TypeOf elemento Is T And stringa.Substring(0, nome.Length) = nome Then matrice.Add(elemento)
            Catch ex As Exception
            End Try
        Next
        Return matrice
    End Function
Ed ecco come viene usata la funzione:
    Private Sub Form1_Load(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles MyBase.Load
        Dim lista As ArrayList
        lista = CtrlByName(Of TextBox)(Me, "TextBox")
        For n = 0 To lista.Count - 1
            lista(n).text = "Io faccio parte della matrice"
        Next
    End Sub

Alcuni tentativi e divagazioni per raggruppare delle TextBoxes in matrici, con tentativi di gestione delle stringhe

Bene.
Ora che ho sistemato la questione della scrittura di dati in un file, devo creare un'interfaccia per scrivere e leggere i dati.
Ammettiamo che il mio "database" rudimentale debba contenere quattro records, pongo quattro TextBoxes sul form, cosicchè il codice legga ciò che ci è scritto e ponga i valori nel file.

Come raggruppare le TextBoxes?

Mi risulta che in VB.NET non si possano fare le matrici di controlli.
Approfondiamo...

Ecco un codice che mi raggruppa in un ArrayList tutte le textboxes (l'ho trovato anche in rete, esattamente come lo avevo concepito io):
    Dim TBoxes As ArrayList

    Private Sub Form1_Load(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles MyBase.Load
        TBoxes = New ArrayList()
        For Each elemento As Control In Me.Controls
            If TypeOf elemento Is TextBox Then
                TBoxes.Add(elemento)
            End If
        Next
...col risultato che se ci sono altre TextBoxes nel form oltre a quelle quattro che mi interessano succede un casino.
Ci deve essere il modo di raggruppare le textboxes.
Se queste sono in numero predeterminato, allora potrebbe mettersi il nome di tutte le textboxes una per una, però...

Mi viene il dubbio se si possano raggruppare per nome...

Si pone il vecchio problema di convertire il nome di un controllo o di una variabile in una stringa.
Devo ripassare come manipolare una stringa, eventualmente con le novità in VB.NET.

Ecco un modo per identificare i controlli per nome.
Public Class Form1
  

    Private Sub Form1_Load(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles MyBase.Load
        For Each elemento In Me.Controls
            Dim nome As String = elemento.name.ToString
            If nome.Substring(0, nome.Length - 1) = "TextBox" Then elemento.text = "fanqulo"
        Next


    End Sub
End Class
In questo modo prendo tutti i controlli il cui nome inizia per "TextBox".
Non è perfetto in quanto se ci sono più di 10 textboxes il numero ha due cifre.
Devo trovare il modo di sistemare meglio la cosa.

martedì 20 agosto 2013

Aggiunta di records a un file ad accesso casuale.

Bah...
Non sono riuscito a capire la differenza fra Input e LineInput.
Ci torneremo...

Per ora fammi un po' esplorare l'accesso casuale, che dovrebbe essere quello che più mi interessa ai fini del programma che voglio realizzare.
Anche il mio è, in fondo, un accesso casuale al problema...

Ecco: per realizzare una tabella su un file, bisogna dichiarare una struttura.
Ci provo...

Structure Persona
    <VBFixedString(10)> Public Nome As String
    Public Peso As Integer
    Public Altezza As Double
End Structure


Sono ammattito a mettere su un codice che inserisse nuovi records in un file ad accesso casuale...
Ogni tanto succede che per una distrazione si facciano degli errori banali e ci si perda tempo.
Comunque ecco il codice che aggiunge dieci persone di nome Ciccio con lo stesso peso e altezza in un file aperto ad accesso casuale:
Public Class Form1
    Private Sub Form1_Load(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles MyBase.Load
        Dim Uomo As Persona
        Dim nRecords As Integer
        Uomo.Nome = "Ciccio"
        Uomo.Altezza = 170
        Uomo.Peso = 80
        FileOpen(1, "C:\Users\Antonello\Documents\FileProva", OpenMode.Random, , , Len(Uomo))
        For n = 1 To 10
            nRecords = LOF(1) / Len(Uomo)
            FilePut(1, Uomo, nRecords + 1)
        Next
        FileClose(1)
    End Sub
End Class

Structure Persona
     Public Nome As String
    Public Peso As Integer
    Public Altezza As Integer
End Structure
Certo, non ha senso aggiungere dieci persone con gli stessi parametri in un file, ma mi serviva per definire lo scheletro della questione, successivamente si penserà a come inserire records diversi.
7 In particolare, il problema era individuare l'ultimo record in modo da aggiungere i record successivi al posto giusto.
La chiave di tutto è nella linea di codice
nRecords = LOF(1) / Len(Uomo)
...che dividendo la lunghezza del file per la lunghezza del record ottiene il numero di records.
Avevo trovato in rete una soluzione incredibilmente cervellotica, che consisteva nel porre uno dopo l'altro i record in una variabile facendo avanzare una variabile-contatore, ma anche se il risultato è lo stesso è come usare i calcoli infinitesimali per fare due più due...

Lettura di files ad accesso sequenziale con InputString.

Adesso vediamo gli altri metodi di lettura di un file aperto per accesso sequenziale.
Sono InputString e Input.

Proviamo con InputString, che consente di leggere,a quanto ho capito, una stringa di lunghezza voluta.
Module Module1

    Sub Main()
        Dim stringa As String
        FileOpen(1, "C:\Users\Antonello\AltroFile2", OpenMode.Input)

        Do Until EOF(1)
            stringa = InputString(1, 15)
            Console.WriteLine(stringa)
            Console.ReadKey()
        Loop
        
        FileClose(1)
    End Sub
End Module
Ed ecco:
Nel mezzo del c


Una stringa di 15 caratteri (inclusi gli spazi, ovviamente).

Ma se io ripeto più volte la pressione del tasto, ecco cosa ottengo:
Nel mezzo del c
ammin di nostra
 vita
mi ritro
vai per una sel
va oscura
che
la diritta via
era smarrita.

Perchè???

Analizziamo:
 vita
mi ritro
Contando i caratteri di questa stringa, oltre a tener conto della spaziatura devo tener conto anche del carattere di fine riga, che viene contato nella lunghezza della stringa, e fa effetto nella presentazione a video dei risultati. Contando in totale 11 caratteri letterali, 3 spazi e 1 cambio riga, ottengo il totale atteso di 15.

Accesso sequenziale a un file: lettura di linee.

Bene.
Non avendo salvato, riscrivo di nuovo il codice (stavolta su un progetto tipo Console)
Module Module1

    Sub Main()

        FileOpen(1, "C:\Users\Antonello\AltroFile2", OpenMode.Output)
        FileClose(1)
        Console.ReadKey()
    End Sub

End Module


Fatto questo, esercitiamoci a leggere e scrivere su un file aperto per accesso sequenziale.

Mi sono creato un file con i primi versi della Divina Commedia, come materiale per l'esercizio.
Nel mezzo del cammin di nostra vita
mi ritrovai per una selva oscura
che la diritta via era smarrita.

Ahi quanto a dir qual era è cosa dura
esta selva selvaggia e aspra e forte
che nel pensier rinova la paura!
E vediamo:
Module Module1

    Sub Main()
        Dim linea As String
        FileOpen(1, "C:\Users\Antonello\AltroFile2", OpenMode.Input)

        Do Until EOF(1)
            linea = LineInput(1)
            Console.WriteLine(linea)
            Console.ReadKey()
        Loop
        
        FileClose(1)
    End Sub
End Module
Ecco! Così facendo ottengo uno per volta i versi, ognuno dei quali sta su una linea, ogni volta che schiaccio un tasto.
Nel mezzo del cammin di nostra vita
mi ritrovai per una selva oscura
che la diritta via era smarrita.

Ahi quanto a dir qual era è cosa dura


ottenendo le linee in sequenza, da zero fino a quando finisce il file (EOF).

lunedì 19 agosto 2013

Operazioni di scrittura e lettura su un file in genere.

Ed ecco invece il codice per eseguire operazioni di input-output su file in generale.
Public Class Form1
    Private Sub Form1_Load(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles MyBase.Load
        FileOpen(1, "C:\Users\Antonello\AltroFile", OpenMode.Output)
        FileClose(1)
    End Sub
End Class
Devo ristudiarmi un po' tutta la questione. Non sembra che ci siano sostanziali differenze con il VB6.

Creazione e scrittura di un file di testo.

Cominciamo a scrivere sui files (e a leggerci).
Ecco un codicetto che scrive una linea su un file di testo.

Riferimenti del progetto: System.IO.Log.
Spazio dei nomi importati: System.IO.Log.

Public Class Form1
    Private Sub Form1_Load(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles MyBase.Load
        Dim scrivi As System.IO.StreamWriter
        scrivi = IO.File.CreateText("C:\Users\Antonello\Prova.txt")
        scrivi.WriteLine("Ciao bello")
        scrivi.Close()
    End Sub
End Class
Risultato, questo file di testo:
Ciao bello


Tutto qui... per il momento.

domenica 18 agosto 2013

Orientamento della griglia, sempre come parametro opzionale

Ed ecco riaggiunto l'orientamento della griglia:
Per mezzo di questo modulo dichiarato nella libreria:
Public Module Costanti
    Public Const HGrid As Integer = 1
    Public Const VGrid As Integer = 0
End Module
definisco (per puro artifizio mnemonico) due costanti che mi danno l'orientamento della griglia, HGrid e VGrid.

Quindi rendo l'orientamento parametro opzionale con HGrid come valore di default (griglia orizzontale).
    Sub New(ByRef frm As Form, ByVal nCaselle As Integer, ByVal rowLength As Integer, Optional ByVal Orientamento As Integer = HGrid, Optional ByVal XCoord As Integer = 0, Optional ByVal YCoord As Integer = 0, Optional ByVal HSpace As Integer = 0, Optional ByVal VSpace As Integer = 0)
        For n As Integer = 0 To nCaselle - 1
            Dim casella As T = New T
            If Orientamento = HGrid Then
                casella.Left = XCoord + (HSpace + casella.Width) * (n Mod rowLength)
                casella.Top = YCoord + (VSpace + casella.Height) * (n \ rowLength)
            ElseIf Orientamento = VGrid Then
                casella.Left = XCoord + (HSpace + casella.Width) * (n \ rowLength)
                casella.Top = YCoord + (VSpace + casella.Height) * (n Mod rowLength)
            End If
            Add(casella)
            frm.Controls.Add(casella)
        Next
    End Sub
End Class
E il programma chiamante può tacere quel parametro o specificare VGrid, ottenendo rispettivamente una griglia a disposizione orizzontale o verticale.
Public Class Form1

    Private Sub Form1_Load(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles MyBase.Load
        Dim miaGriglia = New Griglia(Of Controllo)(Me, 10, 4, VGrid)
        For n As Integer = 0 To miaGriglia.Count - 1
            miaGriglia(n).text = n

        Next
    End Sub
End Class
Class Controllo
    Inherits TextBox
End Class
Ecco: questo codice specifica una griglia verticale.
Funziona.

Rendere opzionali i parametri delle coordinate della griglia di controlli

Perchè non rendere opzionali anche i parametri relativi alle coordinate della griglia, con valore di default pari a zero?
Public Class Griglia(Of T As {New, Control})
    Inherits ArrayList
    Sub New(ByRef frm As Form, ByVal nCaselle As Integer, ByVal rowLength As Integer, Optional ByVal XCoord As Integer = 0, Optional ByVal YCoord As Integer = 0, Optional ByVal HSpace As Integer = 0, Optional ByVal VSpace As Integer = 0)
        For n As Integer = 0 To nCaselle - 1
            Dim casella As T = New T
            casella.Left = XCoord + (HSpace + casella.Width) * (n Mod rowLength)
            casella.Top = YCoord + (VSpace + casella.Height) * (n \ rowLength)
            frm.Controls.Add(casella)
            Add(casella)
        Next
    End Sub

End Class
con la conseguenza che il programma "chiamante" deve specificare obbligatoriamente solo:
  • Il form in cui creare la griglia di controlli;
  • il numero di caselle;
  • la lunghezza delle righe.
Ecco:
Public Class Form1

    Private Sub Form1_Load(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles MyBase.Load
        Dim miaGriglia = New Griglia(Of Controllo)(Me, 10, 3)
        For n As Integer = 0 To miaGriglia.Count - 1
            miaGriglia(n).text = n

        Next
    End Sub
End Class
Class Controllo
    Inherits TextBox
End Class
Funziona lo stesso, ed è mnemonicamente più facile!

Dal cervellotico al lapalissiano: ovvero, non più una classe che inserisce i controlli in un ArrayList, ma una classe che eredita da ArrayList.

Tutto da rifare.
Parto sempre dalle cose più cervellotiche per poi semplificare.
Anzichè creare una classe che inserisce oggetti in un ArrayList, perchè non creare una classe che eredita ArrayList?

Public Class griglia
    Inherits ArrayList


End Class
Ricominciamo da capo.
Voglio semplicemente che la mia classe mi crei, come prima, una griglia di caselle.
Public Class Griglia(Of T As {New, Control})
    Inherits ArrayList
    Sub New(ByRef frm As Form, ByVal nCaselle As Integer, ByVal rowLength As Integer, ByVal XCoord As Integer, ByVal YCoord As Integer, Optional ByVal HSpace As Integer = 0, Optional ByVal VSpace As Integer = 0)
        For n As Integer = 0 To nCaselle - 1
            Dim casella As T = New T
            casella.Left = XCoord + (HSpace + casella.Width) * (n Mod rowLength)
            casella.Top = YCoord + (VSpace + casella.Height) * (n \ rowLength)
            frm.Controls.Add(casella)
            Add(casella)
        Next
    End Sub

End Class
Banale! Ho creato una classe che non usa vie contorte per caricare in un array i controlli creati, ma, ereditando da un ArrayList, è essa stessa l'array in cui i controlli vengono caricati.

Il programma che usa questa classe:
Public Class Form1
    Private Sub Form1_Load(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles MyBase.Load
        Dim miaGriglia = New Griglia(Of Controllo)(Me, 10, 3, 0, 0)
        For n As Integer = 0 To miaGriglia.Count - 1
            miaGriglia(n).text = n
        Next
    End Sub
End Class
Class Controllo
    Inherits TextBox
End Class
dove in rosso è marcato il codice che ho usato per testare che l'array "funzionasse" a dovere!

domenica 11 agosto 2013

Aggiunta di spaziature nella mia classe Calendario

E adesso inserisco i parametri opzionali spaziatura anche nella mia classe Calendario che usa la classe griglia per rappresentare un calendario su un form.
Public Class Calendario(Of T As {New, Control})
    Dim giorni As Integer, miaData As Date, secondaData As Date
    Sub New(ByRef myForm As Form, ByVal mese As Integer, ByVal anno As Integer, ByVal xCoord As Integer, ByVal yCoord As Integer, ByVal formato As String, Optional ByVal HSpace As Integer = 0, Optional ByVal VSpace As Integer = 0)
        miaData = CDate("1/" & mese & "/" & anno)
        'calcolare il numero di giorni del mese
        secondaData = CDate("1/" & (mese + 1) & "/" & anno)
        giorni = DateDiff(DateInterval.Day, miaData, secondaData)
        Dim miaGriglia As New Griglia(Of T)(myForm, giorni, 1, GRIGLIA_ORIZZONTALE, xCoord, yCoord, HSpace, VSpace)
        For n As Integer = 1 To giorni

            If TypeOf miaGriglia.GridArray(n - 1) Is ListBox Then

                miaGriglia.GridArray(n - 1).items.add(UCase(miaData.ToString(formato)))
            Else
                miaGriglia.GridArray(n - 1).text = UCase(miaData.ToString(formato))
            End If

            miaData = miaData.AddDays(1)
        Next
    End Sub
End Class
Ed ecco il risultato:
Dim mioCalendario As New Calendario(Of Controllo)(Me, 8, 2013, 100, 0, "dddd dd MMMM yyyy", , 10)


Elementare!

Inserimento di spaziature orizzontali e verticali nella mia classe griglia.

Come si dichiarano i parametri opzionali?
con Optional, come in VB6.
Però è necessario stabilire anche il valore di default.

Inserisco una spaziatura orizzontale e una verticale nella mia classe griglia, come parametri opzionali con valore di default zero, in modo che se non specificati la griglia verrà senza spaziature fra i controlli che la costituiscono.
Ho imparato come fare in pochi minuti, e il risultato mi è venuto perfetto al primo tentativo.
Public Class Griglia(Of T As {New, Control})
    Public GridArray = New ArrayList
    Sub New(ByRef myForm As Form, ByVal numCaselle As Integer, ByVal rowLength As Integer, ByVal Orientamento As Integer, ByVal XCoord As Integer, ByVal YCoord As Integer, Optional ByVal HSpace As Integer = 0, Optional ByVal VSpace As Integer = 0)

        For n As Integer = 0 To (numCaselle - 1)
            Dim casella As T = New T

            If Orientamento = GRIGLIA_ORIZZONTALE Then
                casella.Left = XCoord + (HSpace + casella.Width) * (n Mod rowLength)
                casella.Top = YCoord + (VSpace + casella.Height) * (n \ rowLength)
            ElseIf Orientamento = GRIGLIA_VERTICALE Then
                casella.Left = XCoord + (HSpace + casella.Width) * (n \ rowLength)
                casella.Top = YCoord + (VSpace + casella.Height) * (n Mod rowLength)
            End If

            GridArray.Add(casella)
            myForm.Controls.Add(casella)
        Next
    End Sub

End Class
Ora faccio qualche prova:

Spaziatura orizzontale:
Dim miaGriglia As New Griglia(Of Controllo)(Me, 30, 4, GRIGLIA_ORIZZONTALE, 0, 0, 10)


Spaziatura verticale:
Dim miaGriglia As New Griglia(Of Controllo)(Me, 30, 4, GRIGLIA_ORIZZONTALE, 0, 0, , 10)


Spaziatura doppia:
Dim miaGriglia As New Griglia(Of Controllo)(Me, 30, 4, GRIGLIA_ORIZZONTALE, 0, 0, 10, 10)

Credo che la cosa sia perfettamente riuscita!

venerdì 9 agosto 2013

Soluzione per l'orientamento delle griglie di controlli

Soluzione definitiva per queste maledette griglie.

Ecco il codice della libreria di classi:
Imports System.Windows.Forms

Public Class Griglia(Of T As {New, Control})
    Sub New(ByRef myForm As Form, ByVal numCaselle As Integer, ByVal rowLength As Integer, ByVal Orientamento As Integer)
        For n As Integer = 0 To (numCaselle - 1)
            Dim casella As T = New T

            If Orientamento = GRIGLIA_ORIZZONTALE Then
                casella.Left = casella.Width * (n Mod rowLength)
                casella.Top = casella.Height * (n \ rowLength)
            ElseIf Orientamento = GRIGLIA_VERTICALE Then
                casella.Top = casella.Height * (n Mod rowLength)
                casella.Left = casella.Width * (n \ rowLength)
            End If

            myForm.Controls.Add(casella)
        Next
    End Sub

End Class


Public Module Costanti
    Public Const GRIGLIA_ORIZZONTALE As Integer = 1
    Public Const GRIGLIA_VERTICALE As Integer = 0
End Module
ed ecco il codice dell'applicazione che usa la classe Griglia (che è tornata unica):
Public Class Form1
    Private Sub Form1_Load(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles MyBase.Load

        Dim miaGriglia As New Griglia(Of GTextBox)(Me, 46, 5, GRIGLIA_VERTICALE)
    End Sub
End Class

Class GTextBox
    Inherits TextBox
    Sub New()
        Multiline = True
        Height = 50
        Width = 50
    End Sub
End Class
In tal modo basta inserire una costante come parametro, e a seconda della costante inserita la griglia avrà un orientamento orizzontale o verticale.
Senza tante chiacchiere!

Una soluzione migliore per impostare le proprietà del controllo di cui si vuole ottenere una griglia

Ma ho un'idea migliore e meno cervellotica (spesso parto dalle soluzioni cervellotiche per poi trovare la più lineare).
Lasciare alla classe Griglia il solo compito di disporre i controlli, lasciando alla creazione della classe da dare in pasto all'istanziazione della griglia tutto ciò che riguarda le caratteristiche intrinseche dei controlli.
Riporto le classi Griglia ai loro codici originari:
Imports System.Windows.Forms
Public Class HorGriglia(Of T As {New, Control})
    Sub New(ByRef myForm As Form, ByVal numCaselle As Integer, ByVal rowLength As Integer)
        For n As Integer = 0 To (numCaselle - 1)
            Dim casella As T = New T
            casella.Left = casella.Width * (n Mod rowLength)
            casella.Top = casella.Height * (n \ rowLength)

            myForm.Controls.Add(casella)
        Next
    End Sub

End Class
Public Class VertGriglia(Of T As {New, Control})
    Sub New(ByRef myForm As Form, ByVal numCaselle As Integer, ByVal colLength As Integer)
        For n As Integer = 0 To (numCaselle - 1)
            Dim casella As T = New T
            casella.Top = casella.Height * (n Mod colLength)
            casella.Left = casella.Width * (n \ colLength)

            myForm.Controls.Add(casella)
        Next
    End Sub

End Class
e il programma che usa queste classi diventa così:
Public Class Form1
    Private Sub Form1_Load(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles MyBase.Load

        Dim miaGriglia As New VertGriglia(Of GTextBox)(Me, 14, 4)
    End Sub
End Class

Class GTextBox
    Inherits TextBox
    Sub New()
        Multiline = True
        Height = 50
        Width = 50
    End Sub
End Class
definendo qui, nel costruttore della classe GTextBox, anche l'altezza e la larghezza delle TextBoxes da usare nella griglia (e se si vuole anche colore, testo eccetera). Mi sembra più razionale.

Soluzione del problema Multiline = True necessaria per cambiare l'altezza delle TextBoxes nelle mie classi Griglia.

Mi sono scontrato con un problema.
Nelle mie classi Griglia ho un tipo generico.
Ho provato a manipolare la proprietà Height delle TextBoxes in caso di una griglia costruita con questo tipo di controlli, ma ho scoperto che qui (diversamente dal VB6) per impostare un'altezza diversa da quella standard nelle TextBoxes è necessario impostare la proprietà Multiline di questi controlli a True.
Se nel corpo della dichiarazione della classe imposto la proprietà Multiline, ottengo un messaggio di errore perchè Multiline non è un membro di T. Infatti, diversamente da Height, Width, Top, Left, che sono condivise da tutti i Controls, per le quali basta porre un vincolo Control al generico T, questa proprietà non è condivisa da tutti i Controls, per cui quel vincolo non basta.

La soluzione che ho trovato è, nel caso delle TextBoxes, dichiarare nel programma istanziante la classe Griglia una classe che eredita da TextBox, nel cui costruttore agire sulla proprietà Multiline, dando in pasto alla istanziazione della classe Griglia questo nuovo tipo anzichè la TextBox.

In questo modo funziona. Posso così arricchire il costruttore della classe Griglia di altri due parametri relativi all'altezza e all'ampiezza delle TextBoxes.

Questo è il codice delle due classi HorGriglia e VertGriglia:
Imports System.Windows.Forms
Public Class HorGriglia(Of T As {New, Control})
    Sub New(ByRef myForm As Form, ByVal numCaselle As Integer, ByVal rowLength As Integer, ByVal larghezza As Integer, ByVal altezza As Integer)
        For n As Integer = 0 To (numCaselle - 1)
            Dim casella As T = New T
            casella.Width = larghezza
            casella.Height = altezza
            casella.Left = casella.Width * (n Mod rowLength)
            casella.Top = casella.Height * (n \ rowLength)

            myForm.Controls.Add(casella)
        Next
    End Sub

End Class
Public Class VertGriglia(Of T As {New, Control})
    Sub New(ByRef myForm As Form, ByVal numCaselle As Integer, ByVal colLength As Integer, ByVal larghezza As Integer, ByVal altezza As Integer)
        For n As Integer = 0 To (numCaselle - 1)
            Dim casella As T = New T
            casella.Width = larghezza
            casella.Height = altezza
            casella.Top = casella.Height * (n Mod colLength)
            casella.Left = casella.Width * (n \ colLength)

            myForm.Controls.Add(casella)
        Next
    End Sub

End Class
e questo è il codice del programma che istanzia una delle classi:
Public Class Form1
    Private Sub Form1_Load(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles MyBase.Load

        Dim miaGriglia As New VertGriglia(Of GTextBox)(Me, 14, 4, 100, 50)
    End Sub
End Class

Class GTextBox
    Inherits TextBox
    Sub New()
        Multiline = True
    End Sub
End Class
La parte evidenziata in rosso è la nuova classe che eredita da TextBox con la proprietà Multiline impostata a True, la quale viene data in pasto al codice che istanzia la classe VertGriglia (in verde)

Così funziona.

giovedì 8 agosto 2013

Griglie orizzontali e verticali.

Con una piccola modifica, distinguo la mia Griglia originale in due tipi: una griglia orizzontale, in cui le caselle vengono disposte progressivamente in file, e una griglia verticale, in cui vengono disposte in colonne.
Imports System.Windows.Forms
Public Class HorGriglia(Of T As {New, Control})
    Sub New(ByRef myForm As Form, ByVal numCaselle As Integer, ByVal rowLength As Integer)
        For n As Integer = 0 To (numCaselle - 1)
            Dim casella As T = New T

            casella.Left = casella.Width * (n Mod rowLength)
            casella.Top = casella.Height * (n \ rowLength)

            myForm.Controls.Add(casella)
        Next
    End Sub

End Class
Public Class VertGriglia(Of T As {New, Control})
    Sub New(ByRef myForm As Form, ByVal numCaselle As Integer, ByVal colLength As Integer)
        For n As Integer = 0 To (numCaselle - 1)
            Dim casella As T = New T

            casella.Top = casella.Height * (n Mod colLength)
            casella.Left = casella.Width * (n \ colLength)

            myForm.Controls.Add(casella)
        Next
    End Sub

End Class
Tutto si rivela esatto, e con un codice del genere:
Public Class Form1
    Private Sub Form1_Load(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles MyBase.Load

        Dim miaGriglia As New VertGriglia(Of TextBox)(Me, 30, 4)
    End Sub
End Class
...ottengo trenta TextBoxes disposte in colonne di 4 elementi:



Tutto incasellato nella mia DLL.

La mia prima DLL

Adesso che ho imparato un mucchio di cose sulla creazione di riferimenti alle DLL e sull'importazione dei namespaces, cerco di creare la mia DLL.

Una DLL con una sola classe.

E' ovvio che una libreria possa fare riferimento benissimo ad altre librerie. Già da quando facevo palestra con l'Assembly creavo librerie che facevano riferimento ad altre librerie (a quando un ripasso dell'Assembly?...)

Vediamo un po'...
Mah... Ho seguito una serie di passaggi un po' alla smanettona... che analizzerò successivamente.
Per il momento ho ottenuto un file miaLibreria.dll.
Adesso vediamo se riesco a fare riferimento ad essa da un altro progetto VB.
Chiudo questo progetto e ne creo uno nuovo, che, con tutti i riferimenti fatti, dovrebbe sapere cos'è una classe Griglia
AHAHAHAHAH!!!!! Perfetto!!!
Aggiungendo il riferimento alla mia miaLibreria.dll (che ho posto in un'altra directory rispetto a quella in cui mi è stata creata prima), e aggiungendo il nameSpace jacLibrary (nome dato da me nel corso delle pratiche smanettone di cui sopra) il mio nuovo progetto ha imparato cos'è una griglia, e con il seguente codice:
Public Class Form1
    Private Sub Form1_Load(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles MyBase.Load
        Dim miaGriglia As New Griglia(Of TextBox)(Me, 30, 4)
    End Sub
End Class
...sono riuscito a creare una griglia di TextBoxes su un form, secondo quanto permesso dalla mia classe.



Faccio paura!!!

Importare namespaces in VB.NET

Procediamo per ordine.
Inseriamo la mia classetta "Griglia" in un progetto di classi (non ricordo come si chiama).
Copio il codice negli appunti, chiudo il progetto attuale (in cui la classe Griglia è in un modulo di classe) e apro un progetto di classi (così mi ricordo come si chiama quel tipo di progetto).

Ecco: si chiama Libreria di classi

Bene.
Ho incollato il codice della mia classe sull'IDE:
Public Class Griglia(Of T As {New, Control})
    Sub New(ByRef myForm As Form, ByVal numCaselle As Integer, ByVal rowLength As Integer)
        For n As Integer = 0 To (numCaselle - 1)
            Dim casella As T = New T

            casella.Left = casella.Width * (n Mod rowLength)
            casella.Top = casella.Height * (n \ rowLength)

            myForm.Controls.Add(casella)
        Next
    End Sub

End Class
e ottengo una serie di erroracci:



A quanto pare, la mia libreria di classi non sa cosa sia un Form con tutti gli annessi e connessi.

Devo porre il riferimento alla classe Form.
In quale namespace è compresa questa classe?

Accertato che la classe è compresa nella libreria (dll) System.Windows.Forms, vado alla finestra Proprietà del progetto, alla scheda Riferimenti, e clicco su "Aggiungi":



Ecco: ho aggiunto il riferimento alla libreria System.Windows.Forms ma dopo questa operazione la finestra del codice continuava a segnalarmi che i Forms erano dei perfetti sconosciuti, quindi nella finestra sotto (spazi dei nomi importati), ho barrato la checkbox relativa al namespace System.Windows.Forms, e finalmente sono sparite le segnalazioni di errore.


Forse anzichè barrare la casella relativa al namespace posso importare il namespace direttamente da codice.
Proviamo.
Deseleziono la checkbox.
Ottengo di nuovo gli errori dovuti al fatto che il mio codice non conosce i Forms.
Aggiungo questo codice (la parte evidenziata in rosso):
Imports System.Windows.Forms
Public Class Griglia(Of T As {New, Control})
    Sub New(ByRef myForm As Form, ByVal numCaselle As Integer, ByVal rowLength As Integer)
        For n As Integer = 0 To (numCaselle - 1)
            Dim casella As T = New T

            casella.Left = casella.Width * (n Mod rowLength)
            casella.Top = casella.Height * (n \ rowLength)

            myForm.Controls.Add(casella)
        Next
    End Sub

End Class
e, come prevedevo, le segnalazioni di errore spariscono: il codice sa di nuovo cos'è un Form!

mercoledì 7 agosto 2013

Un modulo salvato da qualche parte sul computer, che faccia da libreria.

Ho inserito in un progetto un modulo.
Module Funzioni
    Sub scrivi(ByVal contenuto As String)
        Console.WriteLine(contenuto)
        Console.ReadKey()
    End Sub
End Module
L'ho salvato nella mia cartella utente (C:\Users\Antonello), anzichè nella cartella dove vengono salvati i progetti VB.

Adesso rimuovo il modulo, chiudo il progetto e ne apro un altro.

Ecco il modulo nella cartella utente:
Microsoft Windows [Versione 6.0.6001]
Copyright (c) 2006 Microsoft Corporation. Tutti i diritti riservati.

C:\Users\Antonello>dir
 Il volume nell'unità C è ACER
 Numero di serie del volume: AC40-F131

 Directory di C:\Users\Antonello

07/08/2013  16.05    <DIR>          .
07/08/2013  16.05    <DIR>          ..
06/08/2013  18.16               678 .jmf-resource
05/08/2013  11.04    <DIR>          Contacts
06/08/2013  18.58    <DIR>          Desktop
06/08/2013  18.58    <DIR>          Documents
07/08/2013  00.31    <DIR>          Downloads
07/08/2013  11.03    <DIR>          Dropbox
05/08/2013  11.04    <DIR>          Favorites
07/08/2013  16.06               153 Funzioni.vb
05/08/2013  15.19    <DIR>          Links
05/08/2013  11.04    <DIR>          Music
07/08/2013  03.08    <DIR>          Pictures
05/08/2013  11.04    <DIR>          Saved Games
05/08/2013  11.04    <DIR>          Searches
05/08/2013  11.04    <DIR>          Videos
               2 File            831 byte
              14 Directory  32.706.347.008 byte disponibili

C:\Users\Antonello> 


Ora creo un nuovo progetto, e vi aggiungo il modulo esistente C:\Antonello\Funzioni.vb:



Ed ecco cosa scrivo:
Module Module1
    Sub Main()
        scrivi("Ciao, Mondo crudele!")
    End Sub
End Module
che, ovviamente, funziona:
Ciao, Mondo crudele!



Sembra una banalità, ma questo mi permette di tenere una libreria, e non è poco!
Lo so che anche con VB6 era possibile fare altrettanto, ma ripassare le banalità mi piace ed è utile! Solo, vorrei vedere se è possibile importare questo modulo senza agire sull'IDE ma con il semplice codice...

martedì 6 agosto 2013

Classe "griglia" resa flessibile riguardo al tipo di controllo

Ho trovato il modo di flessibilizzare la mia classe Griglia rendendola usabile con diversi tipi di controlli.
Class griglia(Of T As {New, Control})
    Sub New(ByRef myForm As Form, ByVal numCaselle As Integer, ByVal rowLength As Integer)
        For n As Integer = 0 To (numCaselle - 1)
            Dim casella As T = New T

            casella.Left = casella.Width * (n Mod rowLength)
            casella.Top = casella.Height * (n \ rowLength)

            myForm.Controls.Add(casella)
        Next
    End Sub
End Class
Istanziandola in questo modo...
Public Class Form1

    Private Sub Form1_Load(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles MyBase.Load
        Dim miaGriglia As New griglia(Of TextBox)(Me, 60, 4)
        
    End Sub

End Class
...ottengo una griglia di TextBoxes:



mentre istanziando in questo modo...
Public Class Form1

    Private Sub Form1_Load(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles MyBase.Load
        Dim miaGriglia As New griglia(Of Button)(Me, 60, 4)
        
    End Sub

End Class
...ottengo una griglia di Buttons:



Mi chiedo se non ci sia un altro modo più sintetico di scrivere il codice, anche se così funziona perfettamente...

Disposizione di controlli in griglia su un form: creazione di una classe "griglia".

Ora torniamo all'applicazione Form.

Ripercorriamo le tappe della disposizione di una TextBox sul form.
Public Class Form1

    Private Sub Form1_Load(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles MyBase.Load
        Dim casella As TextBox = New TextBox
        casella.Text = "fanqulo"
        Me.Controls.Add(casella)
    End Sub

End Class


Ripassiamo (a mente, senza sbirciare) il codice per disporle in griglia...

Public Class Form1

    Private Sub Form1_Load(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles MyBase.Load

        For n As Integer = 0 To 41
            Dim casella As TextBox = New TextBox
            casella.Left = casella.Width * (n Mod 3)
            casella.Top = casella.Height * (n \ 3)
            casella.Text = n
            Me.Controls.Add(casella)

        Next
    End Sub

End Class
Ecco il risultato:


Benissimo!

Adesso cominciamo a rendere un po' "autonomo" e "flessibile" il codice...

Ecco il primo passo: creazione di una classe Griglia
Public Class Form1

    Private Sub Form1_Load(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles MyBase.Load
        Dim miaGriglia As griglia = New griglia(Me)
        
    End Sub

End Class

Class griglia
    Sub New(ByRef myForm As Form)
        For n As Integer = 0 To 41
            Dim casella As TextBox = New TextBox
            casella.Left = casella.Width * (n Mod 3)
            casella.Top = casella.Height * (n \ 3)
            casella.Text = n
            myForm.Controls.Add(casella)
        Next
    End Sub
End Class
che funziona esattamente allo stesso modo di prima.
Con questa piccola modifica iniziale, può essere chiamata in seno a diversi form.
Adesso rendiamo flessibili, ovviamente, il numero delle caselle e la lunghezza delle varie righe.
Public Class Form1

    Private Sub Form1_Load(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles MyBase.Load
        Dim miaGriglia As griglia = New griglia(Me, 42, 5)
        
    End Sub

End Class

Class griglia
    Sub New(ByRef myForm As Form, ByVal numCaselle As Integer, ByVal rowLength As Integer)
        For n As Integer = 0 To (numCaselle - 1)
            Dim casella As TextBox = New TextBox
            casella.Left = casella.Width * (n Mod rowLength)
            casella.Top = casella.Height * (n \ rowLength)
            casella.Text = n
            myForm.Controls.Add(casella)
        Next
    End Sub
End Class
con questo esempio, ottengo 42 caselle in file di 5.
Istanziando la classe con altri parametri...
Public Class Form1

    Private Sub Form1_Load(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles MyBase.Load
        Dim miaGriglia As griglia = New griglia(Me, 60, 4)
        
    End Sub

End Class
...ottengo 60 caselle in file di 4, eccetera...
Ma adesso viene la sfida più appassionante e più "da ragionarci sopra", ossia la flessibilità per quanto riguarda il tipo di elementi da disporre in griglia, cessando di essere vincolati alle TextBoxes.
Per far questo, devo prendere dimestichezza con i Generics...

Medici e chirurghi: un esempio di uso di ereditarietà e interfacce in VB.NET.

Bene.
Abbiamo un IDE vergine, applicazione tipo console.
Cominciamo con il creare una classe Medico, che sarebbe il medico generico.
Class Medico
    Sub visita()
        Console.WriteLine("Visito il paziente")
    End Sub
    Sub prescrivi()
        Console.WriteLine("Prescrivo una terapia farmacologica")
    End Sub
End Class
Questa non può essere una classe astratta, in quanto il medico generico, che visita e prescrive una terapia, esiste realmente.

Adesso creiamo un'interfaccia Chirurgo.
Interface Chirurgo
    Sub opera()
    Sub sutura()
End Interface

Il medico generico non implementa l'interfaccia (un po' ridicola perchè operare e suturare non sono due cose realmente separate, ma le ho distinte giusto per non creare un'interfaccia con un solo metodo).

Adesso creiamo una classe derivata ChirurgoGenerale e una classe derivata Ginecologo, che implementano l'interfaccia Chirurgo.
Class ChirurgoGenerale
    Inherits Medico
    Implements Chirurgo

    Public Sub opera() Implements Chirurgo.opera
        Console.WriteLine("Io opero sull'addome")
    End Sub

    Public Sub sutura() Implements Chirurgo.sutura
        Console.WriteLine("Io suturo la parete addominale")
    End Sub
End Class

Class Ginecologo
    Inherits Medico
    Implements Chirurgo

    Public Sub opera() Implements Chirurgo.opera
        Console.WriteLine("Io opero sull'utero")
    End Sub

    Public Sub sutura() Implements Chirurgo.sutura
        Console.WriteLine("Io suturo la pelvi delle pazienti")
    End Sub
End Class


Bene.
Adesso istanzio un oggetto Chirurgo, che sia un Chirurgo Generale, e uno che sia un Ginecologo.
Module Module1

    Sub Main()
        Dim mioChirurgo As Chirurgo = New ChirurgoGenerale
        Dim mioGinecologo As Chirurgo = New Ginecologo
        mioChirurgo.opera()
        mioGinecologo.opera()

        Console.ReadKey()
    End Sub



End Module
Ecco l'output:
Io opero sull'addome
Io opero sull'utero


Bene: io ho definito l'oggetto istanziato come Chirurgo anche se in un caso si tratta di Chirurgo Generale e nell'altro di Ginecologo.
Quello che mi serve è una funzione da chirurgo, che ogni oggetto istanziato esegue, sia pure con le competenze specialistiche dell'uno e dell'altro.

Adesso provo a far eseguire al Chirurgo Generale il metodo Visita definito nella classe Medico.
Module Module1

    Sub Main()
        Dim mioChirurgo As Chirurgo = New ChirurgoGenerale
        Dim mioGinecologo As Chirurgo = New Ginecologo
        mioChirurgo.opera()
        mioGinecologo.opera()
        Console.WriteLine()
        mioChirurgo.visita
        Console.ReadKey()
    End Sub



End Module
Ottengo una segnalazione di errore nella fase di scrittura del codice (intercettato dall'IDE). L'errore è "Errore 1 'visita' non è un membro di 'ConsoleApplication1.Chirurgo'."

Certo! Perchè a me serve un'interfaccia Chirurgo, per cui anche se il chirurgo generale è perfettamente in grado di visitare, non è quella la funzione che mi serve, avendo dichiarato l'oggetto come interfaccia Chirurgo!

Se invece adesso dichiaro l'oggetto istanziato come ChirurgoGenerale, ossia se mi servono funzioni del Chirurgo Generale che non sono "chirurgiche", ossia implementate dall'interfaccia Chirurgo, posso benissimo far visitare un paziente dal chirurgo.
Module Module1

    Sub Main()
        Dim mioChirurgo As Chirurgo = New ChirurgoGenerale
        Dim mioGinecologo As Chirurgo = New Ginecologo
        mioChirurgo.opera()
        mioGinecologo.opera()
        Console.WriteLine()
        
        Dim altroChirurgo As ChirurgoGenerale = New ChirurgoGenerale
        altroChirurgo.visita()
        altroChirurgo.opera()



        Console.ReadKey()
    End Sub



End Module
Io opero sull'addome
Io opero sull'utero

Visito il paziente
Io opero sull'addome


Ecco: così il chirurgo può eseguire sia le sue funzioni specialistiche sia le funzioni da medico generico ereditate dalla classe base Medico.

Ancora, però, posso istanziare l'oggetto dichiarandolo come Medico, in quanto mi servono solo le funzioni da medico generico, riferendomi alla classe base, non alla classe derivata, in quanto un chirurgo è comunque un medico generico prima di tutto:
    Sub Main()
        Dim mioChirurgo As Chirurgo = New ChirurgoGenerale
        Dim mioGinecologo As Chirurgo = New Ginecologo
        mioChirurgo.opera()
        mioGinecologo.opera()
        Console.WriteLine()
        
        Dim altroChirurgo As ChirurgoGenerale = New ChirurgoGenerale
        altroChirurgo.visita()
        altroChirurgo.opera()

        Console.WriteLine()
        Dim nuovoChirurgo As Medico = New ChirurgoGenerale
        nuovoChirurgo.visita()


        Console.ReadKey()
    End Sub



End Module
Io opero sull'addome
Io opero sull'utero

Visito il paziente
Io opero sull'addome

Visito il paziente


...ma adesso non posso avere una funzione specialistica da questo oggetto, in quanto ho preso il chirurgo nell'accezione di medico generico:
Module Module1

    Sub Main()
        Dim mioChirurgo As Chirurgo = New ChirurgoGenerale
        Dim mioGinecologo As Chirurgo = New Ginecologo
        mioChirurgo.opera()
        mioGinecologo.opera()
        Console.WriteLine()
        
        Dim altroChirurgo As ChirurgoGenerale = New ChirurgoGenerale
        altroChirurgo.visita()
        altroChirurgo.opera()

        Console.WriteLine()
        Dim nuovoChirurgo As Medico = New ChirurgoGenerale
        nuovoChirurgo.visita()
        nuovoChirurgo.opera()

        Console.ReadKey()
    End Sub



End Module
Ottengo l'errore in fase di scrittura del codice: "Errore 1 'opera' non è un membro di 'ConsoleApplication1.Medico'.

Come mi aspettavo!

Dichiarazione di una variabile oggetto come interfaccia istanziando una classe.

istanziazione di una classe dichiarando l'oggetto come interfaccia.
Module Module1

    Sub Main()
        Dim miaClasse As Interfaccia = New base
        miaClasse.metodo()

    End Sub

End Module

Interface Interfaccia
    Property proprieta As Integer
    Sub metodo()
End Interface

Class base

    Implements Interfaccia
    Private _variabile As Integer
    Public Sub metodo() Implements Interfaccia.metodo
        Console.WriteLine("esecuzione del metodo")
        Console.ReadKey()

    End Sub

    Public Property proprieta As Integer Implements Interfaccia.proprieta
        Get
            proprieta = _variabile
        End Get
        Set(ByVal value As Integer)
            _variabile = value
        End Set
    End Property
End Class

domenica 4 agosto 2013

Interfacce in VB.NET

Ecco creata un'interfaccia:
Module Module1

    Sub Main()
        Dim insieme As New ArrayList

        Dim Mario As New Programmatore
        Dim Giuseppe As New Medico
        Dim Antonio As New Medico
        Dim Luigi As New Programmatore
        Dim Pasquale As New Medico

        insieme.Add(Mario)
        insieme.Add(Giuseppe)
        insieme.Add(Antonio)
        insieme.Add(Luigi)
        insieme.Add(Pasquale)

        Dim num As VBProgram
        For Each num In insieme
            num.createVBConsoleApplications()
        Next
        Console.ReadKey()


    End Sub

End Module

Interface VBProgram
    Sub createVBWindowApplications()
    Sub createVBConsoleApplications()

End Interface

Class Programmatore
    Implements VBProgram

    Public Sub createVBConsoleApplications() Implements VBProgram.createVBConsoleApplications
        Console.WriteLine("Sviluppo un'applicazione Console con VB")
    End Sub

    Public Sub createVBWindowApplications() Implements VBProgram.createVBWindowApplications
        Console.WriteLine("Sviluppo un'applicazione Windows con VB")
    End Sub


    Public Sub createCPPConsoleApplications()
        Console.WriteLine("Sviluppo un'applicazione Console con C++")
    End Sub

    Public Sub createCPPWindowsApplications()
        Console.WriteLine("Sviluppo un'applicazione Windows con C++")
    End Sub
End Class

Class Medico
    Implements VBProgram

    Public Sub createVBConsoleApplications() Implements VBProgram.createVBConsoleApplications
        Console.WriteLine("Sviluppo un'applicazione Console con VB per le diagnosi differenziali")
    End Sub

    Public Sub createVBWindowApplications() Implements VBProgram.createVBWindowApplications
        Console.WriteLine("Sviluppo un'applicazione Windows con VB per le diagnosi differenziali")
    End Sub

    Public Sub visitaPaziente()
        Console.WriteLine("Visito un paziente")
    End Sub

    Public Sub decidiTerapia()
        Console.WriteLine("Imposto una terapia farmacologica")
    End Sub
End Class
Ecco l'output:
Sviluppo un'applicazione Console con VB
Sviluppo un'applicazione Console con VB per le diagnosi differenziali
Sviluppo un'applicazione Console con VB per le diagnosi differenziali
Sviluppo un'applicazione Console con VB
Sviluppo un'applicazione Console con VB per le diagnosi differenziali


venerdì 2 agosto 2013

Overloading (un po' maldestro, direi) applicato al mio esempio delle figure geometriche

Un overloading un po' "maldestro", direi...

Ho sovraccaricato la funzione Area() in modo da potere, con l'aggiunta di un parametro qualunque, avere il risultato intero anzichè decimale.
Non mi piace molto, perchè il parametro c'entra un po' come un cavolo a merenda...
Comunque il meccanismo dell'overloading funziona: se c'è il parametro l'area viene calcolata come Integer, se non c'è viene calcolata come Double.
Module Module1

    Sub Main()
        Dim mioTriangolo As New Triangolo(5.5, 3)
        Console.WriteLine(mioTriangolo.Area())
        Console.ReadKey()
        Console.WriteLine(mioTriangolo.Area(""))
        Console.ReadKey()
    End Sub

End Module



MustInherit Class figura
    Protected base As Double
    Protected altezza As Double

    Sub New(ByVal b As Double, ByVal a As Double)
        base = b
        altezza = a
    End Sub

    MustOverride Function Area() As Double
    MustOverride Function Area(ByVal p As String) As Integer
End Class



Class Triangolo
    Inherits figura

    Sub New(ByVal b As Double, ByVal a As Double)
        MyBase.New(b, a)
    End Sub


    Public Overrides Function Area() As Double
        Return base * altezza / 2
    End Function

    Public Overrides Function Area(ByVal par As String) As Integer
        Return base * altezza / 2
    End Function
End Class
Ed ecco il calcolo come Double e come Integer.
8,25
8



Classe derivata Trapezio dalla classe astratta della figura geometrica: aggiunta di una proprietà nella classe derivata.

Ecco un esempio di overloading!
E' possibile usare una funzione Somma per numeri di diverso tipo.
Posso applicarlo al mio esempio delle figure geometriche...

Ma prima deriviamo dalla classe astratta figura il trapezio, con il calcolo dell'area...

Class Trapezio
    Inherits figura

    Private baseMinore As Integer

    Sub New(ByVal b As Integer, ByVal a As Integer, ByVal bm As Integer)
        MyBase.New(b, a)
        baseMinore = bm
        Console.WriteLine("la base minore è " & baseMinore)
        Console.ReadKey()
    End Sub

    Public Overrides Function Area() As Object
        Return (base + baseMinore) * altezza / 2
    End Function
End Class
Ecco: a parte overridare la funzione per il calcolo dell'area, ovviamente, devo aggiungere fra le proprietà la base minore, e inserirla nei parametri del costruttore.
Le righe in rosso non sono necessarie per la funzione, ma sono un mio test per vedere se effettivamente la base minore viene modificata come da parametri del costruttore, richiamando il costruttore della classe base e aggiungendo la base minore col valore del terzo parametro.
Ecco l'istanziazione e l'output:
    Sub Main()
        Dim mioTrapezio As New Trapezio(5, 3, 2)
    End Sub
la base minore è 2


Perfetto!

Ecco quindi la classe derivata Trapezio:
Class Trapezio
    Inherits figura

    Private baseMinore As Integer

    Sub New(ByVal b As Integer, ByVal a As Integer, ByVal bm As Integer)
        MyBase.New(b, a)
        baseMinore = bm
    End Sub

    Public Overrides Function Area() As Object
        Return (base + baseMinore) * altezza / 2
    End Function
End Class
E adesso, oltre a istanziarla, ne mettiamo a video il risultato della funzione per il calcolo dell'area:
    Sub Main()
        Dim mioTrapezio As New Trapezio(5, 3, 2)
        Console.WriteLine(mioTrapezio.Area)
        Console.ReadKey()

    End Sub
Ed ecco l'output:
10,5


Giusto: infatti (5+2)*3=21 e 21/2=10.5

Bene: HO COMMESSO UN ERRORE CLAMOROSO! Dal momento che nella classe astratta ho dimenticato di specificare il tipo della funzione Area(), l'IDE me l'ha tipizzata automaticamenteo come Object. Per questo ottengo un risultato decimale!
Correggo tutto:
Module Module1

    Sub Main()
        Dim mioTrapezio As New Trapezio(5, 3, 2)
        Console.WriteLine(mioTrapezio.Area)
        Console.ReadKey()

    End Sub

End Module

MustInherit Class figura
    Protected base As Integer
    Protected altezza As Integer

    Sub New(ByVal b As Integer, ByVal a As Integer)
        base = b
        altezza = a
    End Sub

    MustOverride Function Area() As Integer
End Class

Class Triangolo
    Inherits figura

    Sub New(ByVal b As Integer, ByVal a As Integer)
        MyBase.New(b, a)
    End Sub

    Public Overrides Function Area() As Integer
        Return base * altezza / 2
    End Function
End Class

Class Rettangolo
    Inherits figura

    Sub New(ByVal b As Integer, ByVal a As Integer)
        MyBase.New(b, a)
    End Sub
    Public Overrides Function Area() As Integer
        Return base * altezza
    End Function
End Class

Class Trapezio
    Inherits figura

    Private baseMinore As Integer

    Sub New(ByVal b As Integer, ByVal a As Integer, ByVal bm As Integer)
        MyBase.New(b, a)
        baseMinore = bm
    End Sub

    Public Overrides Function Area() As Integer
        Return (base + baseMinore) * altezza / 2
    End Function
End Class
Ed ecco l'output senza decimali:
10



giovedì 1 agosto 2013

Esordiamo con le classi astratte!

Facciamo un tentativo di creare una classe astratta di una generica figura geometrica con le poche parole chiave che ho imparato...

MustInherit Class figura
    Protected base As Integer
    Protected altezza As Integer

    Sub New(ByVal b As Integer, ByVal a As Integer)
        base = b
        altezza = a
    End Sub

    MustOverride Function Area()
End Class
Ecco: le proprietà sono Protected per essere visibili dalle classi derivate.
La funzione è MustOverride, ovviamente...
E ho creato un costruttore, perchè il costruttore della classe derivata possa fare riferimento a questo.

Ecco le classi derivate:
Class Triangolo
    Inherits figura

    Sub New(ByVal b As Integer, ByVal a As Integer)
        MyBase.New(b, a)
    End Sub

    Public Overrides Function Area() As Object
        Return base * altezza / 2
    End Function
End Class

Class Rettangolo
    Inherits figura

    Sub New(ByVal b As Integer, ByVal a As Integer)
        MyBase.New(b, a)
    End Sub
    Public Overrides Function Area() As Object
        Return base * altezza
    End Function
End Class


Le istanzio così:
    Sub Main()
        Dim mioTriangolo As New Triangolo(5, 4)
        Console.WriteLine(mioTriangolo.Area)
        Console.ReadKey()

        Dim mioRettangolo As New Rettangolo(5, 4)
        Console.WriteLine(mioRettangolo.Area)
        Console.ReadKey()

    End Sub
E sembra funzionare:
10
20





Ma ho subito un'altra idea... E se volessi creare una classe derivata Trapezio? La questione è interessante, perchè in questo caso dovrei aggiungere una proprietà in più nella sola classe derivata, ossia la base minore...
Lo vedrò successivamente...

Override: l'esempio delle figure geometriche.

Dovrei ricordare agevolmente come si scrive il codice di un membro overridable.
Ci provo...

L'esempio che mi viene in mente è piuttosto calzante.
Class Triangolo
    Private base As Integer
    Private altezza As Integer

    Sub New(ByVal b As Integer, ByVal a As Integer)
        base = b
        altezza = a
    End Sub

    Sub calcolaArea()
        Console.WriteLine(base * altezza / 2)
        Console.ReadKey()
    End Sub
End Class
Ecco: ho costruito una classe Triangolo, che ha come proprietà Private base e altezza, che vengono definite all'atto dell'istanziazione della figura per mezzo del costruttore.
Il metodo calcolaArea restituisce il valore dell'area secondo la formula per il calcolo dell'area di un triangolo.
10


Elementare, da un punto di vista matematico.
Tuttavia questo esempiuccio ridicolo offre una serie di sviluppi...

Intanto, istanziamo diversi triangoli di diverse misure.
    Sub Main()
        Dim figura As New Triangolo(5, 4)
        figura.calcolaArea()

        Dim figura2 As New Triangolo(6, 7)
        figura2.calcolaArea()

        Dim figura3 As New Triangolo(3, 6)
        figura3.calcolaArea()


    End Sub
10
21
9


e ci sbizzarriamo a volonta...
Ma adesso facciamo qualcosa di più interessante...

Module Module1

    Sub Main()
        Dim figura As New Triangolo(5, 4)
        Console.WriteLine(figura.Area)
        Console.ReadKey()

    End Sub

End Module

Class Triangolo
    Private base As Integer
    Private altezza As Integer

    Sub New(ByVal b As Integer, ByVal a As Integer)
        base = b
        altezza = a
    End Sub

    Function Area() As Integer
        Return base * altezza / 2
    End Function
End Class
Ecco qua. Anzichè un metodo che metta a video il risultato del calcolo dell'area, è più serio creare una function che restituisca il valore dell'area, che può poi essere messa a video o usata in qualsiasi altro modo...
10



Ora creiamo una classe Rettangolo!
Class Rettangolo
    Inherits Triangolo
    Sub New(ByVal b As Integer, ByVal a As Integer)
        MyBase.new(b, a)
    End Sub

    Overrides Function Area() As Integer
        Return base * altezza
    End Function

End Class
Dato che i costruttori con parametri non si ereditano, devo scrivere esplicitamente il costruttore e richiamare il corrispondente costruttore della classe base.
Specifico Overrides dietro a Function.
Ma devo fare tutta una serie di modifiche alla classe base.
Class Triangolo
    Protected base As Integer
    Protected altezza As Integer

    Sub New(ByVal b As Integer, ByVal a As Integer)
        base = b
        altezza = a
    End Sub

    Overridable Function Area() As Integer
        Return base * altezza / 2
    End Function
End Class
  • dichiarare Protected le proprietà, altrimenti la classe derivata non le vede;
  • dichiarare Overridable la function.
Adesso provo a istanziare.
Sub Main()
        Dim mioTriangolo As New Triangolo(5, 4)
        Console.WriteLine(mioTriangolo.Area)
        Console.ReadKey()

        Dim mioRettangolo As New Rettangolo(5, 4)
        Console.WriteLine(mioRettangolo.Area)
        Console.ReadKey()

End Sub
10
20


Perfetto!

Ma adesso sorge un dubbio: Perchè un Rettangolo dovrebbe essere una classe derivata da un Triangolo???