Guida al Visual Basic .NET
Capitolo 78° - DataGridView Parte II
Manipolazione dei dati
Nell'esempio precedente, l'utente poteva modificare ed eventualmente cancellare dati esistenti, ma, ancora una volta, ho tralasciato
di implementare l'aggiunta. In questo caso, però, aver lasciato la possibilità di agire liberamente sui dati aggiunti avrebbe
causato non pochi danni, poiché gli ID sono tutti nascosti e il controllo datagridview non implementa nessun tipo di
autoincremento per i valori numerici: aggiungendo nuove righe, l'utente non avrebbe potuto influire sulle celle ID, che sarebbero rimaste
vuote e avrebbero causato sempre lo stesso errore (avendolo noi gestito nel modo che sapete, l'unica scelta possibile sarebbe stata
quella di cancellare l'ultima riga e perciò non si sarebbe potuto aggiungere nulla in ogni caso). In poche parole, bisogna
intervenire a livello di codice. Public Class CreateOrderDialog Private CustomerID As Int32 Private _NewOrder As AppDataSet.OrdersRow 'Restituisce una nuova riga con gli attributi impostati 'nel dialog Public ReadOnly Property NewOrder() As AppDataSet.OrdersRow Get Return _NewOrder End Get End Property 'Per creare un nuovo ordine ci serve l'ID del cliente ad 'esso associato, perciò dobbiamo costringere il chiamante '(ossia noi stessi XD) a passarci questo dato in qualche 'modo. In questo caso, sovrascriviamo il metodo ShowDialog 'mediante shadowing: Public Shadows Function ShowDialog(ByVal CustomerID As Int32) As DialogResult Me.CustomerID = CustomerID Return MyBase.ShowDialog() End Function Private Sub OK_Button_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles OK_Button.Click If String.IsNullOrEmpty(txtItemName.Text) Then MessageBox.Show("Specificare una descrizione valida del prodotto!", Me.Text, MessageBoxButtons.OK, MessageBoxIcon.Exclamation) Exit Sub End If If nudItemPrice.Value = 0.0F Then MessageBox.Show("Prezzo non valido!", Me.Text, MessageBoxButtons.OK, MessageBoxIcon.Exclamation) Exit Sub End If _NewOrder = My.Forms.Form1.MainDatabase.Orders.NewOrdersRow() With _NewOrder .CustomerID = Me.CustomerID .ItemName = txtItemName.Text .ItemPrice = nudItemPrice.Value .ItemCount = nudItemCount.Value .CreationDate = dtpCreationDate.Value .Settled = chbSettled.Checked End With Me.DialogResult = System.Windows.Forms.DialogResult.OK Me.Close() End Sub Private Sub Cancel_Button_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles Cancel_Button.Click Me.DialogResult = System.Windows.Forms.DialogResult.Cancel Me.Close() End Sub End Class Tenendo conto del nuovo dialog appena scritto, il codice del form diventerebbe: Imports MySql.Data.MySqlClient Public Class Form1 '... Private Sub strAddCustomer_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles strAddCustomer.Click MainDatabase.Customers.AddCustomersRow("", "", "", "", Date.Now, 0) End Sub Private Sub strAddOrder_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles strAddOrder.Click If dgvCustomers.CurrentRow Is Nothing Then Exit Sub End If Dim CID As Int32 = CInt(dgvCustomers.CurrentRow.Cells(0).Value) Dim OrderDialog As New CreateOrderDialog() If OrderDialog.ShowDialog(CID) = Windows.Forms.DialogResult.OK Then MainDatabase.Orders.AddOrdersRow(OrderDialog.NewOrder) RefreshPreview(CID) End If End Sub End Class Manca ancora un'ultima cosa per terminare il programma, ossia il salvataggio. Possiamo, ad esempio, salvare tutto alla chiusura del form: Public Class Form1 '... Private Sub Form1_FormClosing(ByVal sender As System.Object, ByVal e As System.Windows.Forms.FormClosingEventArgs) Handles MyBase.FormClosing Dim Connection As New MySqlConnection("Server=localhost; Database=appdata; Uid=root; Pwd=root;") Dim Adapter As New MySqlDataAdapter() Dim Builder As MySqlCommandBuilder Try Connection.Open() Adapter.SelectCommand = New MySqlCommand("SELECT * FROM Customers;", Connection) Builder = New MySqlCommandBuilder(Adapter) Adapter.Update(MainDatabase.Customers) Adapter.SelectCommand = New MySqlCommand("SELECT * FROM Orders;", Connection) Builder = New MySqlCommandBuilder(Adapter) Adapter.Update(MainDatabase.Orders) Catch Ex As Exception If MessageBox.Show("Impossibile connettersi al database per il salvataggio. Proseguire nella chiusura? Tutti i dati non salvati andranno persi.", Me.Text, MessageBoxButtons.YesNo, MessageBoxIcon.Error) = Windows.Forms.DialogResult.No Then e.Cancel = True End If Finally Connection.Close() End Try End Sub End Class
P.S.: ricordatevi di riassegnare le immagini all'ultima cella dopo che le righe sono state riordinate (è possibile ordinare automaticamente
i dati cliccando sull'intestazione di una colonna). Stampa
Il passo successivo di un gestionale consiste, di norma, nel voler stampare i dati che si gestiscono. Esistono parecchi componenti già pronti per stampare un datagridview, nonché molti strumenti integrati nell'IDE (reports), acquistabili e non, e anche un discreto numero di "scorciatoie", che non fanno altro che disegnare il controllo su un qualche supporto e delegarne la stampa ad un'altra applicazione. Potete scegliere liberamente di usare uno dei metodi sopracitati senza sprecarvi più di tanto nel codice. In ogni caso, la soluzione che propongo potrebbe essere utile per ripassare l'uso di Graphics: Public Class Form1 '... 'Indici dei campi da stampare Private PrintingFields() As Int32 = {1, 2, 3, 4, 5, 7} Private Sub PrintDoc_PrintPage(ByVal sender As System.Object, ByVal e As System.Drawing.Printing.PrintPageEventArgs) Handles PrintDoc.PrintPage 'Indice della prima riga della prima pagina Static RowIndex As Int32 = 0 'Indica se si tratta della prima pagina Static FirstPage As Boolean = True 'Offset orizzontale Dim CellOffset As Int32 = e.MarginBounds.X 'Offset verticale Dim Y As Int32 = e.MarginBounds.Y 'Larghezza totale colonne Dim TotalWidth As Int32 = 0 'Se è la prima pagina, stampa le intestazioni If FirstPage Then For Each Column As DataGridViewColumn In dgvCustomers.Columns 'Stampa solo gli header dei campi contemplati If Array.IndexOf(PrintingFields, Column.Index) < 0 Then Continue For End If e.Graphics.DrawString(Column.HeaderText, dgvCustomers.ColumnHeadersDefaultCellStyle.Font, New SolidBrush(dgvCustomers.ColumnHeadersDefaultCellStyle.ForeColor), CellOffset, Y) CellOffset += Column.Width TotalWidth += Column.Width Next Y += dgvCustomers.ColumnHeadersHeight End If 'Stampa le righe For I As Int32 = RowIndex To dgvCustomers.RowCount - 1 Dim Row As DataGridViewRow = dgvCustomers.Rows(I) CellOffset = e.MarginBounds.X 'Ottiene lo stile delle celle Dim Style As DataGridViewCellStyle 'Distingue fra quelle pari e dispari If Row.Index Mod 2 = 0 Then Style = dgvCustomers.DefaultCellStyle Else Style = dgvCustomers.AlternatingRowsDefaultCellStyle End If 'Se nessun font particolare è specificato, prende 'quello del controllo If Style.Font Is Nothing Then Style.Font = dgvCustomers.Font End If 'Se nessun colore particolare è specificato (di 'default valore argb=(0,0,0,0)) prende quello del 'controllo If Style.ForeColor.A = 0 Then Style.ForeColor = dgvCustomers.ForeColor End If 'Disegna una striscia del colore di sfondo della riga e.Graphics.FillRectangle(New SolidBrush(Style.BackColor), CellOffset, Y, TotalWidth, dgvCustomers.RowTemplate.Height) 'E la contorna con un tratto nero e.Graphics.DrawRectangle(Pens.Black, CellOffset, Y, TotalWidth, dgvCustomers.RowTemplate.Height) For Each Cell As DataGridViewCell In Row.Cells 'Stampa solo gli attributi contemplati If Array.IndexOf(PrintingFields, Cell.ColumnIndex) < 0 Then Continue For End If 'Se la cella contiene un'immagime, la stampa If Cell.ValueType Is GetType(Image) Then Dim Img As Image = Cell.Value e.Graphics.DrawImage(Img, CellOffset + dgvCustomers.Columns(Cell.ColumnIndex).Width 2 - Cell.Value.Width 2, Y + dgvCustomers.CurrentRow.Height 2 - Img.Height 2) Else Dim Height As Int32 Dim StrVal As String 'Formatta la data in forma compatta If Cell.ValueType Is GetType(Date) Then StrVal = CType(Cell.Value, Date).ToShortDateString() Else StrVal = Cell.Value.ToString() End If 'Calcola l'altezza del testo per centrarlo Height = Cell.MeasureTextHeight(e.Graphics, StrVal, Style.Font, 500, TextFormatFlags.Default) 'Stampa il testo e.Graphics.DrawString(StrVal, Style.Font, New SolidBrush(Style.ForeColor), CellOffset, Y + dgvCustomers.RowTemplate.Height 2 - Height 2) End If 'Si sposta alla prossima ascissa CellOffset += dgvCustomers.Columns(Cell.ColumnIndex).Width 'Per tutte le celle tranne l'ultima, disegna una linea 'verticale di separazione dalla cella successiva If Array.IndexOf(PrintingFields, Cell.ColumnIndex) < PrintingFields.Length - 1 Then e.Graphics.DrawLine(Pens.Black, CellOffset - 4, Y, CellOffset - 4, Y + dgvCustomers.RowTemplate.Height) End If Next 'Aumenta l'ordinata Y += dgvCustomers.RowTemplate.Height Next If e.HasMorePages Then FirstPage = False Else FirstPage = True RowIndex = 0 End If End Sub 'strPrint è un sottoelemento del context menu Private Sub strPrint_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles strPrint.Click Dim PDialog As New PrintDialog PDialog.Document = PrintDoc If PDialog.ShowDialog = Windows.Forms.DialogResult.OK Then PrintDoc.PrinterSettings = PDialog.PrinterSettings 'Ridimensiona le colonne per far stare i testi in modo 'corretto. N.B.: ho impostato la larghezza minima della 'colonna Address a 190 pixel dgvCustomers.AutoResizeColumns() PrintDoc.Print() End If End Sub End Class Risultato: Validazione dell'input
E' possibile convalidare l'input dell'utente o indicare che alcuni dati sono erronei utilizzando l'evento CellValidating o RowValidating: Private Sub dgvCustomers_CellValidating(ByVal sender As System.Object, ByVal e As System.Windows.Forms.DataGridViewCellValidatingEventArgs) Handles dgvCustomers.CellValidating If e.ColumnIndex > 0 And e.ColumnIndex < 5 Then With dgvCustomers.Item(e.ColumnIndex, e.RowIndex) If String.IsNullOrEmpty(e.FormattedValue.ToString()) Then .ErrorText = "Il campo non dovrebbe essere lasciato vuoto" Else .ErrorText = Nothing End If End With End If End Sub
C#, TypeScript, java, php, EcmaScript (JavaScript), Spring, Hibernate, React, SASS/LESS, jade, python, scikit, node.js, redux, postgres, keras, kubernetes, docker, hexo, etc...
|