DatagridView evento CellPainting

Ultimamente nei ritagli di tempo mi sto dedicando allo sviluppo di Hyper-V Guest Console e per la la prossima versione sto lavorando alla visualizzazione della situazione dei VHD sia lato VM che lato Host.

L’idea di base è dare evidenza oltre che alle informazioni relative al VHD (Nome, Tipo, eventuale VHD padre, Dimensione, Path e Dimensione del file fisico) anche della della situazione delle spazio disponibile nel volume in cui il file del VHD è memorizzato.

image

Poiché oltre ai meri dati numeri una visione grafica dello spazio disponibile in stile progress è sicuramente più immediata ho valutato alcune possibili soluzioni:

  • Utilizzo di un componente di terze parti (scartato perché mi sembrava esagerato per usarlo solo per questa finalità, inoltre avrei dovuto cercarne uno open source o free e legarmi poi alla sua evoluzione i termini di correzioni di bug o modifiche/aggiunte di funzionalità)
  • Creazione di una DataGridViewColumn custom (scartata perché anche in questo caso avrei creato qualcosa usato solo per una sola finalità)

L’approccio che ho seguito è stato quello di gestire il paint della progress nell’evento CellPainting della DataGridView che permette di semplicemente di sostituire il paint della cella.

Di seguito il codice che si occupa di eseguire il paint delle celle della colonna Drive free space:

Private Sub grdVHD_CellPainting(ByVal sender As Object, ByVal e As System.Windows.Forms.DataGridViewCellPaintingEventArgs) Handles grdVHD.CellPainting

  If e.ColumnIndex = Me.colVHDDriveFreeSpace.Index AndAlso e.RowIndex >= 0 Then
    Dim selected = (e.State And DataGridViewElementStates.Selected) = DataGridViewElementStates.Selected
    Dim focused = Me.grdVHD.CurrentCell.RowIndex = e.RowIndex AndAlso Me.grdVHD.CurrentCell.ColumnIndex = e.ColumnIndex

    ‘Paint background
    e.PaintBackground(e.ClipBounds, selected)

    ‘Paint progress free space
    Dim percentuale = CInt(Me.grdVHD.Rows(e.RowIndex).Cells(e.ColumnIndex).Tag)

    Dim progressX = e.CellBounds.X + 1
    Dim progressY = e.CellBounds.Bottom – 4
    Dim progressWidth = e.CellBounds.Width – 3
    Dim progressHeight = 2

    Dim rect = New System.Drawing.Rectangle( _
      progressX – 1, _
      progressY – 1, _
      progressWidth + 1, _
      progressHeight + 1)
    e.Graphics.DrawRectangle(Pens.Black, rect)

    rect = New System.Drawing.Rectangle( _
      progressX, _
      progressY, _
      progressWidth, _
      progressHeight)
    e.Graphics.FillRectangle(Brushes.Gray, rect)

    rect = New System.Drawing.Rectangle( _
      progressX, progressY, _
      CInt((progressWidth * percentuale) / 100), _
      progressHeight)
    e.Graphics.FillRectangle(Brushes.LightGreen, rect)

    rect = Nothing

    ‘Paint contenuto
    e.PaintContent(e.CellBounds)

    ‘Paint border
    e.Paint(e.ClipBounds, DataGridViewPaintParts.Border)
    If focused Then e.Paint(e.CellBounds, DataGridViewPaintParts.Focus)

    e.Handled = True
  End If
End Sub

image Il concetto di fondo è quello di sfruttare i metodi e.PaintBackground, e.PaintContent e e.Paint per disegnare le parti standard utilizzando i metodi nativi della classe base, utilizzare i metodi offerti da e.Graphics per disegnare gli oggetti grafici aggiuntivi e quindi comunicare alla classe base che della cella è stato eseguito un paint custom mediante l’istruzione e.Handled = True per bloccare il paint nella classe base.