TheTruster's Box

  • Increase font size
  • Default font size
  • Decrease font size
Home Programmazione Articoli Inserire delle ProgressBar in una ListView con Visual Basic 6

Inserire delle ProgressBar in una ListView con Visual Basic 6

E-mail Stampa PDF

Spesso capita che un programmatore si trovi a prendere spunto da altre applicazioni per realizzare ciò che gli passa per la testa. A volte è un'esigenza reale, a volte è solo la curiosità di confrontarsi con se stessi, cimentandosi in qualcosa che, probabilmente, sul momento non è utile. Ovviamente non ritengo queste attività "tempo perso", prima di tutto perchè tutto ciò che ci spinge a ragionare vale come bagaglio culturale e secondo perchè c'è la possibilità che, un giorno, si debba realizzare quella determinata cosa, e la si abbia già pronta! Sorridente

La mia "ispirazione" in questo caso, è stata la ListView di e-Mule. Come molti sapranno, in una delle sue colonne è presente una barra di avanzamento che sta ad indicare lo stato di avanzamento del download di un file. Incuriosito, ho deciso di provare a realizzare una cosa del genere con una ListView standard di Visual Basic 6.


La barra di avanzamento che ho realizzato non è della stessa complessità di quella di e-Mule, nel senso che non tiene conto di un avanzamento frammentato, ma mi ritengo soddisfatto del risultato e della sua funzionalità.

Il principio che sta alla base è quello di realizzare la barra di avanzamento in un'immagine bitmap, assegnandola successivamente al SubItem del controllo ListView.

Per prima cosa, cominciamo la realizzazione di questo progetto aggiungendo i Microsoft Windows Common Controls 6.0 ai componenti. Possiamo farlo andando su Progetto -> Componenti e selezionandoli dalla lista dei Controlli.

Poi prepariamo il Form che farà da base come in figura, rispettando la nomenclatura dei controlli:



Come accennavo prima, abbiamo bisogno che la nostra barra sia formata da una serie di bitmap, ognua delle quali rappresenterà uno stadio di avanzamento, ma nella ListView facente parte dei Common Controls non è possibile inserire direttamente un'immagine. Essa per essere assegnata ad un Item o SubItem deve risiedere su una ImageList.
La prima cosa da fare, quindi, è popolare l'ImageList con le immagini della barra di avanzamento.

Ovviamente le immagini della ProgressBar dovranno essere proporzionate alla colonna nella quale dovranno essere inserite, per cui definiamo una variabile pubblica nel form che ci consente di variare con poco sforzo e in qualunque momento, la sua collocazione.

Dim pBarCol As Integer

Adesso possiamo creare la routine che disegna materialmente una ProgressBar. Per farlo, abbiamo bisogno del riferimento alla ListView per calcolare altezza e larghezza della barra, del valore di colonna nella quale dovrà essere inserita e, infine, del suo valore.
La routine MakeProgressBar, per spiegarla in breve, si occupa di:

  • definire i colori che determineranno l'aspetto della barra;
  • creare un controllo PictureBox temporaneo nel quale disegnarla;
  • proporzionare il valore passato come argomento alla larghezza del SubItem scelto;
  • disegnare la barra utilizzando gli strumenti grafici di VB6;
  • inserire la barra così creata nell'ImageList;
  • eliminare il controllo PictureBox temporaneo.


Questa è la routine completa.

Sub MakeProgressBar(lw As ListView, colHead As Integer, Value As Integer)
 
Dim v As Long
Dim BarBorder As Long
Dim BarBack As Long
Dim BarNormal As Long
Dim BarComplete As Long
Dim tmpPic As PictureBox
 
BarBorder = RGB(0, 0, 0)
BarBack = RGB(255, 255, 200)
BarNormal = RGB(200, 0, 0)
BarComplete = RGB(0, 200, 0)
 
Set tmpPic = Me.Controls.Add("VB.PictureBox", "tP")
 
With tmpPic
    .Visible = False
    .AutoRedraw = True
    .ScaleMode = vbPixels
    .BorderStyle = 0
    Set .Font = lw.Font
    .Width = lw.ColumnHeaders(colHead).Width
    .Height = Int(lw.ListItems(1).Height)
    .Cls
    .DrawMode = 13
    tmpPic.Line (0, 0)-(.ScaleWidth - 1, .ScaleHeight - 1), BarBack, BF
 
    v = (Value * (.ScaleWidth - 5)) / 100
    .CurrentX = (.ScaleWidth - .TextWidth(Value & "%")) / 2
    .CurrentY = (.ScaleHeight - .TextHeight(Value & "%")) / 2
    .ForeColor = vbBlack
    tmpPic.Print Value & "%"
 
    If v > 0 Then
        .DrawMode = IIf(Value = 100, 9, 14)
        tmpPic.Line (2, 2)-(2 + v, .ScaleHeight - 3), IIf(Value = 100, BarComplete, BarNormal), BF
    End If
 
    .DrawMode = 13
    tmpPic.Line (0, 0)-(.ScaleWidth - 1, .ScaleHeight - 1), BarBorder, B
    ImageList1.ListImages.Add , "v" & Value, tmpPic.Image
End With
 
Me.Controls.Remove ("tP")
Set tmpPic = Nothing
 
End Sub

Prima ho detto che di ProgressBar ne andranno create una per ogni valore, quindi ci serve una routine che si occupi di generarle, passando alla MakeProgressBar i valori sequenzialmente corretti, da 1 a 100.

Sub CreateProgressBars()
Dim k As Integer
ListView1.SmallIcons = Nothing
ImageList1.ListImages.Clear
For k = 0 To 100
    MakeProgressBar ListView1, pBarCol, k
Next
ListView1.SmallIcons = ImageList1
End Sub

Come si può notare, la routine si occupa di eliminare l'assegnazione della ImageList dalla ListView poichè altrimenti non sarebbe possibile popolarla o cancellarla, lancia la generazione delle barre attraverso il ciclo da 1 a 100 e riassegna la ImageList alla ListView.
Vedremo in seguito che questa routine tornerà utile anche quando sarà necessario aggiornare la visualizzazione del controllo al variare della dimensione della colonna che ospita la ProgressBar.

Arrivati a questo punto abbiamo popolato la nostra ImageList con le immagini rappresentanti tutti i valori di avanzamento necessari. Non rimane che assegnare quella opportuna, in base al valore da rappresentare, al SubItem di nostro interesse.
Per rendere le cose più semplici ho predisposto 2 routines: SetValue e GetValue.

La SetValue controlla, innanzitutto, se il valore passato rispetta il range di 1 a 100 consentito, quindi assegna al SubItem scelto l'immagine corrispondente al valore da rappresentare, tramite la proprietà ReportIcon.
In questa routine, per tenere traccia del valore attualmente rappresentato, viene conservato, nella proprietà Tag del SubItem, anche il valore numerico passato come argomento.

Sub SetValue(itm As ListItem, Value As Integer)
If Value < 0 Then Value = 0: Exit Sub
If Value > 100 Then Value = 100: Exit Sub
itm.ListSubItems(pBarCol - 1).ReportIcon = "v" & Value
itm.ListSubItems(pBarCol - 1).Tag = Value
End Sub

Per ottenere il valore attualmente impostato in un determinato SubItem basta usare la Function GetValue, passando l'Item di riferimento come argomento:

Function GetValue(itm As ListItem) As Integer
GetValue = itm.ListSubItems(pBarCol - 1).Tag
End Function

Procediamo, adesso, al monitoraggio della larghezza delle colonne della ListView, poichè in corrispondenza di una variazione della loro larghezza sarà necessario adeguare la dimensione delle ProgressBar.
Purtroppo la ListView non fornisce nessun feedback riguardante l'evento di espansione o riduzione delle colonne, per cui possiamo ricorrere ad un semplice Timer, il quale si occuperebbe di controllare ciclicamente che le dimensioni della colonna contenente le ProgressBar non sia variata. Non è un'operazione molto impegnativa, poichè il "lavoro" di ridimensionamento verrebbe eseguito solo nel caso in cui ci sia una variazione di dimensioni.
Questa è la routine di evento del Timer nominato tResize:

Private Sub tResize_Timer()
Static WidthMonitor As Double
 
If WidthMonitor <> ListView1.ColumnHeaders(pBarCol).Width Then
    CreateProgressBars
    WidthMonitor = ListView1.ColumnHeaders(pBarCol).Width
End If
DoEvents
End Sub

Come dicevo, attraverso una variabile Static (WidthMonitor) si controlla che la dimensione rilevata non sia differente rispetto al controllo precedente, nel qual caso le ProgressBar verrebbero rigenerate (sulla base della nuova dimensione) attraverso l'invocazione della CreateProgressBar.

Abbiamo adesso tutti gli strumenti per poter utilizzare le ProgressBar nel nostro controllo ListView e quello che dobbiamo fare, per provare il tutto è disporre di un certo numero di Item nella nostra ListView ai quali assegnare le barre.
Sfruttiamo l'evento Load del form, per formattare correttamente la ListView, popolandola con degli Item e dei dati fittizi.

Private Sub Form_Load()
 
Dim itmX As ListItem
Dim k As Integer
 
Randomize
 
Me.ScaleMode = vbPixels
 
pBarCol = 4
 
With ListView1
    .View = lvwReport
    .FullRowSelect = True
    .ColumnHeaders.Add , , "Dummy", 0
    .ColumnHeaders.Add , , "Nome del File"
    .ColumnHeaders.Add , , "Dimensione"
    .ColumnHeaders.Add , , "Avanzamento"
    For k = 1 To 20
        Set itmX = .ListItems.Add(, , "")
        itmX.SubItems(1) = "File n. " & k
        itmX.SubItems(2) = CStr(k + 100) & "Kb"
        itmX.SubItems(3) = ""
        itmX.ListSubItems(pBarCol - 1).Tag = 0
    Next
End With
 
CreateProgressBars
 
For k = 1 To ListView1.ListItems.Count
    SetValue ListView1.ListItems(k), 0
Next k
 
End Sub

Si noterà nel codice, che alla ListView viene aggiunta una colonna iniziale che ho definito "Dummy" e che ha larghezza 0. Questo è, più che altro, un escamotage di natura estetica: quando si assegna un'immagine alla proprietà ReportIcon di un SubItem, lo stesso spazio che essa occupa, ma vuoto, viene aggiunto anche accanto all'Item nella prima colonna. Esteticamente è poco gradevole per cui ho preferito nascondere la prima colonna, lasciandola comunque non popolata.

Se lanciamo adesso il progetto possiamo già notare che la nostra ListView è popolata e con le barre di avanzamento (tutte settate a valore 0) inserite nella terza colonna. Possiamo anche variare la dimensione di quest'ultima, notando che le ProgressBar si ridimensioneranno di conseguenza.
Allo stato attuale il progetto è già funzionante e potrebbe essere usato per rappresentare i valori di avanzamento dei vari "files", poichè basterebbe impostare attraverso la SetValue il corretto valore all'Item interessato, per notare l'avanzamento della barra al valore settato.

Per una prova generale, però, abbiamo inserito sul form (vedi immagine iniziale) un secondo Timer e un CommandButton denominati rispettivamente tProgress e Command1.

Il Command1 serve semplicemente per avviare/stoppare il Timer, mentre all'interno della routine di evento tProgress_Timer() vengono generati dei valori casuali "decidendo" arbitrariamente l'uno o l'altro Item, incrementando il valore della ProgressBar di un tot, anch'esso casuale.

Ecco entrambe le routine di evento:

Private Sub Command1_Click()
tProgress.Enabled = Not tProgress.Enabled
 
If tProgress.Enabled Then
    Command1.Caption = "Stop"
Else
    Command1.Caption = "Start"
End If
 
End Sub

Private Sub tProgress_Timer()
 
Dim r As Integer
 
r = 1 + Int(Rnd(1) * ListView1.ListItems.Count)
 
With ListView1
    SetValue .ListItems(r), GetValue(.ListItems(r)) + (1 + Int(Rnd) * 10)
End With
 
End Sub

Questo è il risultato finale con le barre in avanzamento:


I colori delle barre possono essere variati a piacere, basta intervenire sulle variabili BarBorder, BarBack, BarNormal e BarComplete dichiarate nella routine MakeProgressBar.

Come al solito, potete scaricare il progetto completo dalla sezione Download -> Progetti, oppure cliccando qui: DOWNLOAD

Non esitate a contattarmi per farmi presenti inesattezze o chiedere ulteriori chiarimenti. Sorridente

 

Aggiungi commento

I commenti da parte degli ospiti sono moderati, quindi soggetti ad approvazione da parte dell'Amministratore.
Si prega di aggiungere commenti in tema.
Sono assolutamente vietati messaggi volgari, pubblicitari e/o promozionali.
I commenti ritenuti non conformi saranno rimossi.

Codice di sicurezza
Aggiorna

Sondaggio

Cosa vorresti vedere di più su TheTruster's Box?
 

Utenti on-line

 6 visitatori online

MasterDrive.it



Aggiungi TheTruster's Box ai preferiti!