JavascriptProva

mercoledì 12 settembre 2012

Creazione di classi derivate, funzioni template e prima realizzazione del calendario.

Ora mi devo cimentare a creare dei derivati di Label, funzione specifica di VB.NET che VB6 si sognava beatamente.
Ereditarietà!!!

Class Casella
    Inherits Label

End Class
Bene.
Praticamente a questo punto si chiama casella ma è praticamente ancora una Label.
Sostituisco Casella a Label nel codice.
    Sub disponiCaselle(ByVal larghezza As Integer, ByVal sinistra As Integer)
        Dim numero As Integer
        numero = DateTime.DaysInMonth(anno, mese)
        Dim cas As Casella
        For n = 0 To numero - 1
            cas = New Casella
            With cas
                .BorderStyle = BorderStyle.FixedSingle
                .BackColor = Color.White
                .Width = larghezza
                .Left = sinistra
                .Top = .Height * (n Mod numero)
                .Text = GiornoDellaSettimana(CDate(n + 1 & "/" & mese & "/" & anno))
            End With
            Me.Controls.Add(cas)
        Next
    End Sub
E ovviamente funziona ancora.

E' possibile aggiungervi delle proprietà?

Provo...

Ecco la mia caselluccia: forte di esperienze già fatte, ho fatto in modo che il codice dapprima imposti la sua proprietà pubblica testo, quindi inneschi il suo metodo Scrivi per fare apparire sulla casella il testo.
Class Casella
    Inherits Label
    Public testo As String
    Sub Scrivi()
        Text = testo
    End Sub
End Class
Ed ecco il codice che la usa:
    Sub disponiCaselle(ByVal larghezza As Integer, ByVal sinistra As Integer)
        Dim numero As Integer
        numero = DateTime.DaysInMonth(anno, mese)
        Dim cas As Casella
        For n = 0 To numero - 1
            cas = New Casella
            With cas
                .BorderStyle = BorderStyle.FixedSingle
                .BackColor = Color.White
                .Width = larghezza
                .Left = sinistra
                .Top = .Height * (n Mod numero)
                .testo = GiornoDellaSettimana(CDate(n + 1 & "/" & mese & "/" & anno))
                .Scrivi()
            End With
            Me.Controls.Add(cas)
        Next
    End Sub


Bene. Adesso voglio fare in modo che sia in qualche modo "passato come parametro" anche il tipo di controllo sul quale deve agire la subroutine disponicaselle.
Ricordo che si deve usare una funzione template o generica, con la parola chiave Of.
Ricordiamo...

In pratica, mi serve una subroutine che agisca con diverse classi di oggetti, in quanto devo poterla usare con una classe o con una classe derivata della prima...

Ecco rinfrescata la memoria.
Si deve mettere As New dopo la Of seguita dalla variabile, e bisogna dichiarare la variabile oggetto As Object.
Ecco:
    Sub disponiCaselle(Of T As New)(ByVal larghezza As Integer, ByVal sinistra As Integer)
        Dim numero As Integer
        numero = DateTime.DaysInMonth(anno, mese)
        Dim cas As Object
        For n = 0 To numero - 1
            cas = New T
Ora posso creare una sottoclasse di Label per la seconda colonna...
Ecco i due oggetti.
Ho creato prima Casella, destinato ad apparire sulla seconda e su eventuali altre colonne con sfondo bianco, successivamente Calendario, destinato ad apparire sulla prima colonna con le date scritte sopra.
Nella prima ho una proprietà testo e un metodo Scrivi che non fa assolutamente nulla (in modo che quando il codice li chiami non generi un errore in quanto inesistenti); il metodo Scrivi viene overridato nella classe derivata Calendario con un metodo Scrivi che invece scrive il valore della proprietà testo.
Class Casella
    Inherits Label
    Public testo As String
    Overridable Sub Scrivi()
    End Sub
End Class

Class Calendario
    Inherits Casella
    Overrides Sub Scrivi()
        Text = testo
    End Sub
End Class
la proprietà testo non viene specificata nella classe derivata Calendario in quanto già presente nella classe genitrice e quindi ereditata.

Ed ecco il codice che chiama per due volte la funzione disponiCaselle, l'una per la classe Calendario, prima colonna, e l'altra per la classe Casella, seconda colonna.
        disponiCaselle(Of Calendario)(larghezze(0), SommaElementiDiArray(larghezze, 0))
        disponiCaselle(Of Casella)(larghezze(1), SommaElementiDiArray(larghezze, 1))
Il risultato è perfetto:

Uso della funzione che somma gli elementi di una matrice

Ed ecco come ho impiegato la mia funzione che somma gli elementi di un array:
Public Class Form1
    Dim mese As Integer = 9
    Dim anno As Integer = 2012
    Dim larghezze As Integer() = {300, 100}
    Private Sub Form1_Load(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles MyBase.Load
        disponiCaselle(larghezze(0), SommaElementiDiArray(larghezze, 0))
        disponiCaselle(larghezze(1), SommaElementiDiArray(larghezze, 1))
    End Sub
    Sub disponiCaselle(ByVal larghezza As Integer, ByVal sinistra As Integer)
        Dim numero As Integer
        numero = DateTime.DaysInMonth(anno, mese)
        Dim casella As Label
        For n = 0 To numero - 1
            casella = New Label
            With casella
                .BorderStyle = BorderStyle.FixedSingle
                .BackColor = Color.White
                .Width = larghezza
                .Left = sinistra
                .Top = .Height * (n Mod numero)
                .Text = GiornoDellaSettimana(CDate(n + 1 & "/" & mese & "/" & anno))
            End With
            Me.Controls.Add(casella)
        Next
    End Sub
End Class
Ho isolato il codice che dispone le caselle in colonna nella subroutine disponicaselle, in modo da poterla richiamare più volte.
Quindi do alla routine due parametri, uno per la larghezza della colonna e uno per la posizione sinistra della colonna.
Ponendo in una matrice le larghezze predeterminate per ciascuna colonna, dalla prima all'ultima, faccio in modo che il primo parametro della subroutine sia il numero dell'elemento della matrice che contiene la larghezza della colonna, e il secondo parametro sia la somma delle larghezze delle colonne precedenti.
Funziona.
In questo modo posso predeterminare le larghezze delle varie colonne semplicemente modificando i valori della matrice larghezze, senza impazzire più di tanto.

Una funzione per sommare elementi di una matrice

Una funzioncina per sommare gli elementi di una matrice.
Mi serve per un'ulteriore evoluzione del mio progetto:

    Function SommaElementiDiArray(ByVal matrice As Integer(), ByVal num As Integer) As Integer
        Dim numero As Integer
        For n = 0 To num - 1
            numero += matrice(n)
        Next
        Return numero
    End Function
L'ho inserita nel modulo Funzioni.vb, e viene richiamata dal codice del Form1 in questo modo, usando un array larghezze:
        Debug.Print(SommaElementiDiArray(larghezze, 1))
si tratta di una prova con la quale faccio stampare il risultato nella finestra immediata. Il primo parametro esprime il nome della matrice, mentre il secondo è l'elemento della matrice prima del quale gli elementi vengono sommati.
La matrice con cui ho fatto la prova è questa:
    Dim larghezze As Integer() = {150, 100, 100}
e con il numero 1 come secondo parametro, ottengo la somma del solo elemento 0 della matrice, ossia 150:
150


Se come parametro metto il 2, invece, ottengo come risultato la somma dell'elemento 0 e dell'elemento 1 della matrice:
        Debug.Print(SommaElementiDiArray(larghezze, 2))
250
Mi servirà per un certo progetto che ho in mente...

Aggiunta del giorno della settimana

Aggiunngo il giorno della settimana.
Per fare questo ho dovuto aumentare la larghezza delle mie caselle rispetto a quella standard predeterminata.
Dim mese As Integer = 9
    Dim anno As Integer = 2012
    Private Sub Form1_Load(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles MyBase.Load
        'Calcolo del numero dei giorni del mese
        Dim numero As Integer

        numero = DateTime.DaysInMonth(anno, mese)



        'definizione della variabile oggetto
        Dim casella As Label

        For n = 0 To numero - 1
            casella = New Label
            'attribuzione delle proprietà alla label appena istanziata
            With casella
                .BorderStyle = BorderStyle.FixedSingle
                .BackColor = Color.White
                .Width = 200
                .Left = 0
                .Top = .Height * (n Mod numero)
                .Text = WeekdayName(Weekday(CDate(n + 1 & "/" & mese & "/" & anno)), , 1) & " " & CDate(n + 1 & "/" & mese & "/" & anno)
            End With
            Me.Controls.Add(casella)

        Next
    End Sub
Ma forse potrei inserire tutto quel po' po' di codice in una funzione usando come parametro la data.
Vediamo di ingegnarci.
Dim mese As Integer = 9
    Dim anno As Integer = 2012
    Private Sub Form1_Load(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles MyBase.Load
        'Calcolo del numero dei giorni del mese
        Dim numero As Integer

        numero = DateTime.DaysInMonth(anno, mese)

        'definizione della variabile oggetto
        Dim casella As Label

        For n = 0 To numero - 1
            casella = New Label
            'attribuzione delle proprietà alla label appena istanziata
            With casella
                .BorderStyle = BorderStyle.FixedSingle
                .BackColor = Color.White
                .Width = 200
                .Left = 0
                .Top = .Height * (n Mod numero)
                .Text = GiornoDellaSettimana(CDate(n + 1 & "/" & mese & "/" & anno))
            End With
            Me.Controls.Add(casella)

        Next
    End Sub
    Function GiornoDellaSettimana(ByVal d As Date) As String
        Dim testo As String = WeekdayName(Weekday(d), , 1) & " " & d
        Return testo
    End Function


Questa funzione posso toglierla di qua per non appesantire il codice del form e metterla in un modulo a parte, magari da dedicare ad altre eventuali funzioni.
(Tolgo i commenti per evitare la scocciatura di rimarcarli in verde ogni volta)

Codice del Form (ci aggiungo anche la definizione della classe Form1):
Public Class Form1
    Dim mese As Integer = 9
    Dim anno As Integer = 2012
    Private Sub Form1_Load(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles MyBase.Load

        Dim numero As Integer

        numero = DateTime.DaysInMonth(anno, mese)


        Dim casella As Label

        For n = 0 To numero - 1
            casella = New Label

            With casella
                .BorderStyle = BorderStyle.FixedSingle
                .BackColor = Color.White
                .Width = 200
                .Left = 0
                .Top = .Height * (n Mod numero)
                .Text = GiornoDellaSettimana(CDate(n + 1 & "/" & mese & "/" & anno))
            End With
            Me.Controls.Add(casella)

        Next
    End Sub
End Class


Codice del modulo Funzioni.vb:
Module Funzioni
    Function GiornoDellaSettimana(ByVal d As Date) As String
        Dim testo As String = WeekdayName(Weekday(d), , 1) & " " & d
        Return testo
    End Function
End Module
E funziona ugualmente, perchè la funzione del modulo è pubblica e quindi viene "vista" dal codice del Form1.

Date come testi delle caselle del calendario

Ora riempiamo le caselle con tutte le date. Prendo il numero n di ordine delle caselle e ci aggiungo 1 dato che inizia con 0 e termina con numero-1, e lo uso come numero del giorno del mese nel contesto della data.
Dichiaro una variabile mese e una anno di tipo integer in modo da poter poi cambiare la definizione del mese e dell'anno in rapporto alle esigenze del programma.
    Dim mese As Integer = 9
    Dim anno As Integer = 2012
    Private Sub Form1_Load(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles MyBase.Load
        'Calcolo del numero dei giorni del mese
        Dim numero As Integer

        numero = DateTime.DaysInMonth(anno, mese)



        'definizione della variabile oggetto
        Dim casella As Label

        For n = 0 To numero - 1
            casella = New Label
            'attribuzione delle proprietà alla label appena istanziata
            With casella
                .BorderStyle = BorderStyle.FixedSingle
                .BackColor = Color.White
                .Left = 0
                .Top = .Height * (n Mod numero)
                .Text = CDate(n + 1 & "/" & mese & "/" & anno)
            End With
            Me.Controls.Add(casella)

        Next
    End Sub
ed ottengo il risultato voluto.

martedì 11 settembre 2012

Calcolo del numero dei giorni del mese

Ecco riscritto il codice che calcola il numero dei giorni del mese.
Mi chiedo se esista un modo più breve per ottenere il numero dei giorni di un mese.

Private Sub Form1_Load(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles MyBase.Load
        'Calcolo del numero dei giorni del mese
        Dim dat, base As Date
        Dim numero As Integer = 1
        Dim mese As Integer = Now.Month
        dat = CDate("1/" & Now.Month & "/" & Now.Year)
        base = dat
        Do
            dat = dat.AddDays(1)
            If dat.Month <> base.Month Then Exit Do
            numero += 1
        Loop



        'definizione della variabile oggetto
        Dim casella As Label

        For n = 0 To numero - 1
            casella = New Label
            'attribuzione delle proprietà alla label appena istanziata
            With casella
                .BorderStyle = BorderStyle.FixedSingle
                .Left = 0
                .Top = .Height * (n Mod numero)
            End With
            Me.Controls.Add(casella)

        Next
    End Sub


Funziona, ma mi chiedo se esista un metodo in VB.NET che mi possa evitare di fare tutto questo casino...

Ho trovato qualcosina. Ora provo...

Private Sub Form1_Load(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles MyBase.Load
        'Calcolo del numero dei giorni del mese
        Dim numero As Integer
        numero = DateTime.DaysInMonth(2012, 2)



        'definizione della variabile oggetto
        Dim casella As Label

        For n = 0 To numero - 1
            casella = New Label
            'attribuzione delle proprietà alla label appena istanziata
            With casella
                .BorderStyle = BorderStyle.FixedSingle
                .Left = 0
                .Top = .Height * (n Mod numero)
            End With
            Me.Controls.Add(casella)

        Next
    End Sub
Funziona! Ed è molto più breve!!!
(ho provato con febbraio perchè si distingue dagli altri, e quest'anno è pure bisestile!)

Disposizione di elementi in colonna

Riparto da zero.
Ho bisogno di tante labels che vengano messe in colonna, ognuna delle quali rechi la data di un giorno del mese, dal primo all'ultimo giorno.
Inizio con un numero arbitrario 10
 Private Sub Form1_Load(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles MyBase.Load
        'numero delle caselle
        Dim numero As Integer = 10
        'definizione della variabile oggetto
        Dim casella As Label

        For n = 0 To numero - 1
            casella = New Label
            'attribuzione delle proprietà alla label appena istanziata
            With casella
                .BorderStyle = BorderStyle.FixedSingle
                .Left = 0
                .Top = .Height * (n Mod numero)
            End With
            Me.Controls.Add(casella)

        Next
    End Sub
Usare il Modulo è ingegnoso.
0/numero dà come resto 0, 1/numero dà come resto 1, 2/numero dè come resto 2, fino a (numero-1)/numero chè dà come resto numero-1.
Per ogni dividendo minore del divisore, l'operazione dà come resto il dividendo stesso. Quindi moltiplicando l'altezza dell'elemento per il resto della divisione fra il numero d'ordine dell'elemento e il numero degli elementi meno uno, e impostando la posizione verticale dell'elemento a questo numero, si ha che il primo elemento è alla posizione altezza x 0, ossia zero, il secondo a altezza x 1, il secondo a altezza x 2 fino all'ultimo, in cui la posizione è altezza x (numero-1).