venerdì 6 gennaio 2012

[VB.NET] Matrici di Controlli con LINQ

Descrizione :
Alcuni miei suggerimenti in merito al ricorrente problema delle Matrici Di Controlli "in salsa VB.NET".

+ Articolo :

Uno dei primi scogli che contribuiscono ad affondare gli entusiasmi dei neo-VB.NET/ex-VB6, è la totale assenza della vecchia "Matrice di Controlli."
Creando ad esempio una Label su Form, e facendo copia/incolla della stessa, NON uscirà più quel classico messaggio : "... creare una matrice di controlli ?".
Personalmente sono d'accordo con i molti che sostengono sia un bene.
Di contaminazioni ex-VB6 ce ne sono fin troppe : almeno questa se la sono risparmiata.

In questo Articolo voglio mostrare alcune tecniche che permettono di raggiungere e superare la gestione di Matrici di Controlli, anche grazie all'utilizzo di LINQ e delle Regular Expressions.
Ovviamente l'uso delle matrici in VB6 è più immediato, ma come spesso accade in VB.NET, ciò che alle prime sembra essere un po' più complesso del dovuto, poi permette di andare ben più lontano.

Ho deciso di raggruppare il tutto in 2 Esempi principali.
Ad ogni Esempio fa capo una Form.
Perciò avremo due Form : FormMainA e FormMainB.
La semplicità della struttura delle Form non rende necessaria alcuna immagine particolare.

1. FormMainA :
Nel primo esempio ho 5 Button su Form e un buon numero di Label.
Consiglio di avere almeno una dozzina di Label.
Lo scopo è gestire solo il gruppo di Label in modo centralizzato ( la matrice ) in modo che possano essere ordinate in base ad una o più Proprietà.
Tra i vari vantaggi, matriceLabels permette di definire a piacere uno o più criteri di ordinamento delle Labels all'interno della Matrice ossia l'ordine in cui verranno prese in un normale ciclo.
La cosa si ottiene semplicemente con i Metodi OrderBy, OrderByDescending, ThenBy, ThenByDescending.
Per semplicità gestisco solo l'Evento Click su tutte le Label presenti.

--> Codice FormMainA :

Public Class FormMainA

    Private matriceLabels As New List(Of Label)

    Private Sub matriceLabelsClick(ByVal sender As System.Object, ByVal e As System.EventArgs)
        MessageBox.Show("Click su " & DirectCast(sender, Label).Name)
    End Sub

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

        For Each C As Control In Me.Controls
            If TypeOf C Is Label Then
                matriceLabels.Add(C)
                AddHandler C.Click, AddressOf matriceLabelsClick
            End If
        Next

    End Sub

    Private Sub Button1_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles Button1.Click
        'Ordina per nome ASC
        matriceLabels = matriceLabels.OrderBy(Function(L) L.Name).ToList()
    End Sub

    Private Sub Button2_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles Button2.Click
        'Ordina per nome DESC
        matriceLabels = matriceLabels.OrderByDescending(Function(L) L.Name).ToList()
    End Sub

    Private Sub Button3_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles Button3.Click
        'Ordina per nome { [parte letterale]-[parte numerica] }
        matriceLabels = matriceLabels.OrderBy(Function(C) System.Text.RegularExpressions.Regex.Match(C.Name, "(?:(?<nome>[\p{L}]+))").Groups("nome").Value) _
                                     .ThenBy(Function(C) Convert.ToInt32(System.Text.RegularExpressions.Regex.Match(C.Name, "\d+$").Value)) _
                                     .ToList()
    End Sub

    Private Sub Button4_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles Button4.Click
        'Ordina per Colonne e Righe
        matriceLabels = matriceLabels.OrderBy(Function(L) L.Top).ThenBy(Function(L) L.Left).ToList()
    End Sub

    Private Sub Button5_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles Button5.Click
        'Test ordinamento
        For i As Integer = 0 To matriceLabels.Count - 1
            matriceLabels(i).Text = i
        Next
    End Sub

End Class

Basta uno sguardo alla semplicità dei Metodi LINQ di ordinamento OrderBy, OrderByDescending, ThenBy, ecc., e direi che il confronto con VB6 ce lo siamo già giocato.

- La funzione di Button1 e Button2 è chiara.
- La funzione di Button3 è quella di ottenere un ordinamento per Nome, ma rispettando la suddivisione interna dei nomi che VB assegna di default ad ogni nuovo controllo aggiunto in Design { Label1, Label2, ..., LabelN }, cosa che invece non accade nei precedenti due casi, dove il nome del controllo viene inteso nel suo senso letterale, e quindi ad esempio "Label10" verrebbe subito dopo "Label1".
- La funzione di Button4 è quella di ordinare per posizione. Se i controlli sono disposti in modo opportuno si otterrà un ordinamento per Righe ( dall'alto in basso ) e per Colonne ( da sinsitra a destra ).
- La funzione del Button5 è di testare i vari ordinamenti scrivendo nel controllo l'indice che lo stesso ha in matrice.

2. FormMainB :
Nel secondo esempio le cose si fanno ancora più interessanti.
Voglio una Matrice di Controlli eterogenea, ossia costituita da due o più tipi di controllo.
Anche qui per semplicità avrò una Form con un Button e due gruppi : Label e TextBox.
E decido che le TextBox abbiano la precedenza sulle Label.
Al solito gestisco Click e ordino per Nome, rispettando l'ordine numerico assegnato da VB.

--> Codice FormMainB :

Public Class FormMainB

    Private matriceControlli As New List(Of Control)

    Private Sub matriceControlliClick(ByVal sender As System.Object, ByVal e As System.EventArgs)
        MessageBox.Show("Click su " & DirectCast(sender, Control).Name)
    End Sub

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

        matriceControlli.AddRange(Me.Controls.OfType(Of TextBox)())
        matriceControlli.AddRange(Me.Controls.OfType(Of Label)())

        For Each C As Control In matriceControlli
            AddHandler C.Click, AddressOf matriceControlliClick
        Next

    End Sub

    Private Sub Button1_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles Button1.Click

        '1. Definisco l'ordine con cui prendere in considerazione i controlli, a seconda della tipologia
        Dim OrdineControlli As New Dictionary(Of Type, Integer)
        OrdineControlli.Add(GetType(TextBox), 1)
        OrdineControlli.Add(GetType(Label), 2)

        '2. Impongo il nuovo ordine
        matriceControlli = matriceControlli.OrderBy(Function(C) OrdineControlli(C.GetType)) _
                                           .ThenBy(Function(C) System.Text.RegularExpressions.Regex.Match(C.Name, "(?:(?<nome>[\p{L}]+))").Groups("nome").Value) _
                                           .ThenBy(Function(C) Convert.ToInt32(System.Text.RegularExpressions.Regex.Match(C.Name, "\d+$").Value)) _
                                           .ToList

        'Test sull'ordine imposto alla matrice di controlli
        For i As Integer = 0 To matriceControlli.Count - 1
            matriceControlli(i).Text = i
        Next

    End Sub

End Class

Alla matrice aggiungo i controlli desiderati per gruppi, naturalmente escludendo il Button.
Non mi soffermo ad approfondire oltre l'uso di LINQ e di System.Text.RegularExpressions.
Direi che per l'argomento "Matrici di Controlli" di spunti utili ce ne sono.

+ Fine Articolo.

Un Click su "Mi Piace" è il modo migliore per ringraziare l'autore di questo articolo.




0 commenti:

Posta un commento

 
Design by Free WordPress Themes Modificato da MarcoGG