La consultazione del forum è libera per tutti.
Per poter porre un quesito è invece necessario essere un utente registrato (clicca qui se non lo sei).
Tutti gli utenti che richiedono un supporto, come da REGOLAMENTO, sono caldamente invitati ad allegare un file di esempio con l'indicazione di quello che si desidera ottenere.
Ciao, oggi vi presento un altro problemino...nel file allegato sono riuscita a realizzare una userform che..BLOCCA EXCEL! Sono diventata brava eh 🤣
Parliamo della userform ReportOperai, dove dovrei filtrare i dati presenti nel foglio rapporti_lavoro filtrandoli con due combobox in modo da ottenere nella listbox l'elenco delle ore lavorate da un particolare operaio in un particolare cantiere. non arrivo a capire dove sia l'errore perchè non mi parte nemmeno il debug, si blocca excel e basta.
Grazie in anticipo per l'aiuto
Ciao @Cyberlady prova a sostituire tutto ciò che hai scritto nella UserForm ReportOperai con queste procedure e vedi se fa quello che vorresti:
Option Explicit
Private Sub UserForm_Initialize()
caricaCantiere
caricaDipendenti
End Sub
Private Sub caricaCantiere()
Dim ws As Worksheet
Dim ur As Long, i As Long
Dim dictCantiere As Object
Dim cantiere As String
Dim arrCantiere() As Variant
Set ws = ThisWorkbook.Worksheets("rapporti_lavoro")
Set dictCantiere = CreateObject("Scripting.Dictionary")
ur = ws.Cells(ws.Rows.Count, "A").End(xlUp).Row
If ur > 1 Then
For i = 2 To ur
cantiere = Trim(ws.Range("A" & i).Value)
If cantiere <> "" And Not dictCantiere.Exists(cantiere) Then
dictCantiere.Add cantiere, cantiere
End If
Next i
If dictCantiere.Count > 0 Then
arrCantiere = dictCantiere.Items
Me.ComboBox1.List = OrdinaArray(arrCantiere)
End If
End If
Set ws = Nothing
Set dictCantiere = Nothing
End Sub
Private Sub caricaDipendenti()
Dim ws As Worksheet
Dim ur As Long, i As Long
Dim dictDipendenti As Object
Dim dipendente As String
Dim arrDipendente() As Variant
Set ws = ThisWorkbook.Worksheets("rapporti_lavoro")
Set dictDipendenti = CreateObject("Scripting.Dictionary")
ur = ws.Cells(ws.Rows.Count, "C").End(xlUp).Row
If ur > 1 Then
For i = 2 To ur
dipendente = Trim(ws.Range("C" & i).Value)
If dipendente <> "" And Not dictDipendenti.Exists(dipendente) Then
dictDipendenti.Add dipendente, dipendente
End If
Next i
If dictDipendenti.Count > 0 Then
arrDipendente = dictDipendenti.Items
Me.ComboBox2.List = OrdinaArray(arrDipendente)
End If
End If
Set ws = Nothing
Set dictDipendenti = Nothing
End Sub
Private Function OrdinaArray(arr As Variant) As Variant
Dim j As Long, k As Long
Dim temp As Variant
For j = LBound(arr) To UBound(arr) - 1
For k = j + 1 To UBound(arr)
If UCase(arr(j)) > UCase(arr(k)) Then
temp = arr(j)
arr(j) = arr(k)
arr(k) = temp
End If
Next k
Next j
OrdinaArray = arr
End Function
Private Sub combobox1_change()
If ComboBox2 <> "" Then Call aggiornalistbox
End Sub
Private Sub combobox2_change()
If ComboBox1 <> "" Then Call aggiornalistbox
End Sub
Private Sub aggiornalistbox()
Dim ws As Worksheet
Dim lastrow As Long, i As Long, r As Integer
Dim cantiere As String, dipendente As String, firstAddress As String
Dim f As Range
Dim dict As Object
Dim arrDataNomeOre As Variant
Set ws = ThisWorkbook.Sheets("rapporti_lavoro")
Set dict = CreateObject("Scripting.Dictionary")
lastrow = ws.Cells(ws.Rows.Count, 1).End(xlUp).Row
cantiere = ComboBox1.Value
dipendente = ComboBox2.Value
i = 0
Set f = ws.Range("A:A").Find(What:=cantiere, LookIn:=xlValues, LookAt:=xlWhole)
If Not f Is Nothing Then
firstAddress = f.Address
Do
If f.Offset(, 2).Value = dipendente Then
arrDataNomeOre = Array(f.Offset(, 1).Value, f.Offset(, 2).Value, f.Offset(, 3).Value)
i = i + 1
dict.Add i, arrDataNomeOre
End If
Set f = ws.Range("A:A").FindNext(f)
Loop While Not f Is Nothing And f.Address <> firstAddress
End If
With ListBox1
.ColumnCount = 3
.Clear
If dict.Count > 0 Then
For i = 0 To dict.Count - 1
.AddItem
For r = 0 To .ColumnCount - 1
.List(.ListCount - 1, r) = dict.Items()(i)(r)
Next r
Next i
Else
MsgBox "Non ci sono dati da mostrare!", vbExclamation, "Attenzione..."
End If
End With
Set ws = Nothing
Set f = Nothing
Set dict = Nothing
End Sub
Se hai bisogno di spiegazioni fammi sapere.
Ciao @Cyberlady ho fatto un piccolo errore nel codice precedente. Ho scambiato le ComboBox. Tieni buono quest'altro codice:
Option Explicit
Private Sub UserForm_Initialize()
caricaCantiere
caricaDipendenti
End Sub
Private Sub caricaCantiere()
Dim ws As Worksheet
Dim ur As Long, i As Long
Dim dictCantiere As Object
Dim cantiere As String
Dim arrCantiere() As Variant
Set ws = ThisWorkbook.Worksheets("rapporti_lavoro")
Set dictCantiere = CreateObject("Scripting.Dictionary")
ur = ws.Cells(ws.Rows.Count, "A").End(xlUp).Row
If ur > 1 Then
For i = 2 To ur
cantiere = Trim(ws.Range("A" & i).Value)
If cantiere <> "" And Not dictCantiere.Exists(cantiere) Then
dictCantiere.Add cantiere, cantiere
End If
Next i
If dictCantiere.Count > 0 Then
arrCantiere = dictCantiere.Items
Me.ComboBox2.List = OrdinaArray(arrCantiere)
End If
End If
Set ws = Nothing
Set dictCantiere = Nothing
End Sub
Private Sub caricaDipendenti()
Dim ws As Worksheet
Dim ur As Long, i As Long
Dim dictDipendenti As Object
Dim dipendente As String
Dim arrDipendente() As Variant
Set ws = ThisWorkbook.Worksheets("rapporti_lavoro")
Set dictDipendenti = CreateObject("Scripting.Dictionary")
ur = ws.Cells(ws.Rows.Count, "C").End(xlUp).Row
If ur > 1 Then
For i = 2 To ur
dipendente = Trim(ws.Range("C" & i).Value)
If dipendente <> "" And Not dictDipendenti.Exists(dipendente) Then
dictDipendenti.Add dipendente, dipendente
End If
Next i
If dictDipendenti.Count > 0 Then
arrDipendente = dictDipendenti.Items
Me.ComboBox1.List = OrdinaArray(arrDipendente)
End If
End If
Set ws = Nothing
Set dictDipendenti = Nothing
End Sub
Private Function OrdinaArray(arr As Variant) As Variant
Dim j As Long, k As Long
Dim temp As Variant
For j = LBound(arr) To UBound(arr) - 1
For k = j + 1 To UBound(arr)
If UCase(arr(j)) > UCase(arr(k)) Then
temp = arr(j)
arr(j) = arr(k)
arr(k) = temp
End If
Next k
Next j
OrdinaArray = arr
End Function
Private Sub combobox1_change()
If ComboBox2 <> "" Then Call aggiornalistbox
End Sub
Private Sub combobox2_change()
If ComboBox1 <> "" Then Call aggiornalistbox
End Sub
Private Sub aggiornalistbox()
Dim ws As Worksheet
Dim lastrow As Long, i As Long, r As Integer
Dim cantiere As String, dipendente As String, firstAddress As String
Dim f As Range
Dim dict As Object
Dim arrDataNomeOre As Variant
Set ws = ThisWorkbook.Sheets("rapporti_lavoro")
Set dict = CreateObject("Scripting.Dictionary")
lastrow = ws.Cells(ws.Rows.Count, 1).End(xlUp).Row
dipendente = ComboBox1.Value
cantiere = ComboBox2.Value
i = 0
Set f = ws.Range("A:A").Find(What:=cantiere, LookIn:=xlValues, LookAt:=xlWhole)
If Not f Is Nothing Then
firstAddress = f.Address
Do
If f.Offset(, 2).Value = dipendente Then
arrDataNomeOre = Array(f.Offset(, 1).Value, f.Offset(, 2).Value, f.Offset(, 3).Value)
i = i + 1
dict.Add i, arrDataNomeOre
End If
Set f = ws.Range("A:A").FindNext(f)
Loop While Not f Is Nothing And f.Address <> firstAddress
End If
With ListBox1
.ColumnCount = 3
.Clear
If dict.Count > 0 Then
For i = 0 To dict.Count - 1
.AddItem
For r = 0 To .ColumnCount - 1
.List(.ListCount - 1, r) = dict.Items()(i)(r) '<--le due () prima di i e r servono a trasformare gli Item del dizionario in array
Next r
Next i
Else
MsgBox "Non ci sono dati da mostrare!", vbExclamation, "Attenzione..."
End If
End With
Set ws = Nothing
Set f = Nothing
Set dict = Nothing
End Sub
Grazie, appena rientro lo provo…ma tu hai scoperto cos’era che faceva bloccare il programma? Vorrei pian piano riuscire a capire dove sbaglio, non solo correggere con un copia e incolla
Ciao. Allora innanzitutto scusami ma mi sono accorto di un altro piccoli errore quindi aspettati una nuova mia risposta dove ti rigiro il codice corretto per la terza volta.
Ti risponderò anche riguardo il problema che riscontri con il tuo codice...ma se ne parla stasera
Ciao @Cyberlady
divido questa mia risposta in due parti. Nella prima darò delle risposte in merito al tuo codice, nella seconda parte parlerò del mio.
Prima parte
Tanto per iniziare il tuo file è troppo "pesante" e rispetto al suo contenuto, e le dimensioni non si spiegano. Da rigo 1543 in poi, anche se non li vedi, ci sono delle celle con all'interno dei dati. Infatti la variabile lastrow, quando si valorizza, assume come valore un numero gigantesco (non ricordo di preciso ma se ho visto bene siamo verso il milione). Con un numero così grande, capisci bene che quei cicli For in cui viene impegnata quella variabile, il processo diventa lentissimo.
Per prima cosa fai questa azione, che sarà utile anche per il mio codice. Dunque, nel foglio "rapporti_lavoro", fai click con il mouse sul numero che identifica il rigo 1543. Come puoi vedere adesso quel rigo è interamente selezionato. A questo punto tieni premuto i pulsanti SHIFT + CTRL e con la freccia direzionale verso il basso, seleziona tutte le righe (a partire quindi dalla 1543) fino all'ultima del foglio. Con le righe selezionate, premi il tasto CANC. Adesso tutti i dati superflui presenti, sono cancellati.
Adesso salva il file e chiudilo. Se nella cartella dove risiede il file premi il tasto F5, noterai che il file da oltre 18 Mb, passa a poco più di 100 Kb.
Se apri di nuovo il file, noterai che non si blocca più come dicevi, ma purtroppo il tuo codice non funziona come dovrebbe. Quindi passiamo ad esaminarlo.
Non so se già altre volte l'ho fatto notare, ma in cima ad ogni modulo (che sia uno Modulo Standard o del Foglio), devi sempre mettere la scritta Option Explicit (dichiarazione esplicita delle variabili). Se lo avessi fatto anche in questa occasione, il compilatore ti avrebbe segnato subito la prima anomalia, ovvero, nell'evento Initialize della UserForm c'è una discrepanza con la variabile lastrow perché nel primo ciclo For (quello che serve a caricare la Combobox1) è dichiarata lastros
Una cosa che però non mi torna è che tu nella UserForm, secondo come descritto dalle Etichette di fianco ad ogni ComboBox, sembra che tu voglia caricare i Dipendenti nella ComboBox1 e i Cantieri nella ComboBox2, ma di fatto con quel doppio ciclo For carichi i Cantieri nella ComboBox1 e le Date nella ComboBox2. Quindi o hai commesso un errore nell'indicare le colonne da dove attingere i dati oppure hai sbagliato ad indicare le Etichette.
Tralasciando per ora questo problema, personalmente io preferisco utilizzare una Dictionary piuttosto che una Collection per caricare in modo univoco i dati (cosa che a quanto sembra tu voglia fare). La Dictionary ha il proprio metodo .Exists che valuta la presenza o meno del dato (che nel gergo viene definito key), mentre la Collection non ha un metodo nativo, infatti tu hai dovuto aggirare il problema gestendolo con On Error Resume Next.
Passiamo alla Sub aggiornalistbox(). Qui onestamente non ho capito cosa pensavi di fare scrivendo così la macro. In altre parole, attraverso un ciclo For che scorre fino all'ultima riga compilata in colonna "A", stai cercando di caricare in ListBox gli elementi presenti in colonna "B", "C" e "D" ma solo se le variabili filtrocategoria e filtrosottocategoria sono entrambe vuote. Queste due variabili si valorizzano del contenuto delle due ComboBox.
Io invece da quello che mi è sembrato di capire, tu vorresti scegliere il Cantiere e il Dipendente attraverso le due ComboBox, in base alle scelte, mostrare in ListBox le date, il nominativo e le ore svolte in quella data. Giusto? (Spero di si perché la mia macro quello fa 😀)
Seconda parte
Come accennato, la mia macro fa proprio quanto su descritto. Nelle varie procedure ti ho commentato ogni rigo, in modo che tu possa capire il funzionamento:
Option Explicit '<--dichiarazione esplicita delle varibili _
(è anche possibile attivarla in modo permanente andando in: _
STRUMENTI, OPZIONI, spunta su DICHIARAZIONI DI VARIABILI OBBLIGATORIA
Private Sub UserForm_Initialize()
'utilizzo di 2 SUB per caricare le ComboBox.
'utile perché può essere necessario in seguito richiamare il loro caricamento
caricaCantiere
caricaDipendenti
End Sub
Private Sub caricaCantiere()
'procedura che carica la ComboBox2 (Cantieri)
Dim ws As Worksheet
Dim ur As Long, i As Long
Dim dictCantiere As Object
Dim cantiere As String
Dim arrCantiere() As Variant
'imposto il foglio
Set ws = ThisWorkbook.Worksheets("rapporti_lavoro")
'imposto un Dizionario per la raccolta dei valori (Cantieri)
Set dictCantiere = CreateObject("Scripting.Dictionary")
'calcolo l'ultimo rigo compilato in colonna "A" (dove risiedono i nomi dei Cantieri)
ur = ws.Cells(ws.Rows.Count, "A").End(xlUp).Row
'se l'ultimo rigo è superiore ad 1, allora esistono dati, quindi carico i dati
If ur > 1 Then
'con un ciclo, scorro da rigo 2 fino all'ultimo rigo
For i = 2 To ur
'in una variabile prelevo il nome del cantiere scritto nella cella iterata.
'utilizzo la funzione TRIM per eliminare eventuali spazi prima e dopo
cantiere = Trim(ws.Range("A" & i).Value)
'se il cantiere esiste (ovvero la cella iterata non è vuota) e non è ancora stato scritto nel Dizionario (ecco il metodo .Exists)...
If cantiere <> "" And Not dictCantiere.Exists(cantiere) Then
'allora lo scrivo nel Dizionario. La chiave (key) sarà il nome del cantiere, così come anche il valore (item)
dictCantiere.Add cantiere, cantiere
End If
Next i
'se il Dizionario contiene valori...
If dictCantiere.Count > 0 Then
'allora trasferisco i valori del Dizionario in un Array
'è necessario perché voglio ordinare l'elenco dei valori
arrCantiere = dictCantiere.Items
'utilizzo una Function chiamata OrdinaArray, che non è altro che un BobbleSort
'come parametro gli passo l'Array dei valori presi dal Dizionario
'una volta che il BobbleSort avrà ordinato i valori, essi verranno utilizzati per caricare la ComboBox2 (Cantieri)
Me.ComboBox2.List = OrdinaArray(arrCantiere) '<--qui il codice salta nella Function OrdinaArray che è scritta più giù
End If
End If
'libero la memoria dagli oggetti creati
Set ws = Nothing
Set dictCantiere = Nothing
End Sub
Private Sub caricaDipendenti()
'procedura che carica la ComboBox1 (Dipendenti)
'stessa identica procedure della ComboBox2, ma si opera sulla colonna "C"
Dim ws As Worksheet
Dim ur As Long, i As Long
Dim dictDipendenti As Object
Dim dipendente As String
Dim arrDipendente() As Variant
Set ws = ThisWorkbook.Worksheets("rapporti_lavoro")
Set dictDipendenti = CreateObject("Scripting.Dictionary")
ur = ws.Cells(ws.Rows.Count, "C").End(xlUp).Row
If ur > 1 Then
For i = 2 To ur
dipendente = Trim(ws.Range("C" & i).Value)
If dipendente <> "" And Not dictDipendenti.Exists(dipendente) Then
dictDipendenti.Add dipendente, dipendente
End If
Next i
If dictDipendenti.Count > 0 Then
arrDipendente = dictDipendenti.Items
Me.ComboBox1.List = OrdinaArray(arrDipendente)
End If
End If
Set ws = Nothing
Set dictDipendenti = Nothing
End Sub
Private Function OrdinaArray(arr As Variant) As Variant
'funzione che ordina alfabeticamente i valori (BoobleSort)
Dim j As Long, k As Long
Dim temp As Variant
'attraverso un doppio ciclo, scorro a partire dall'indice minimo dall'Array arr (definito come parametro nella Function)
'fino all'indice massimo dell'Array arr - 1 (LBound serve per determinare l'indice minimo, UBound quello massimo)
'ad esempio se arr ha 100 elementi, allora il primo ciclo andrà da 0 a 99
For j = LBound(arr) To UBound(arr) - 1
'secondo ciclo che va dall'indice minimo dell'Array arr + 1 fino all'indice massimo
'qui il ciclo andrà da 1 a 100
For k = j + 1 To UBound(arr)
'siccome nell'Array arr ho tutti i valori (ad esempio ho tutti i Cantieri presi in modo univoco),
'valuto se il nome del primo cantiere e maggiore del secondo cantiere
'ad esempio CASTELFONDO è maggiore di ACCIAIERIE, quindi...
If UCase(arr(j)) > UCase(arr(k)) Then '(UCase serve per valuare i nomi i valutandoli in maiuscolo)
'dato che CASTELFONDO è maggiore di ACCIAIERIE
temp = arr(j) 'trasferisco temporaneamente ACCIAIERIE in un una varibile d'appoggio
arr(j) = arr(k) 'sposto CASTELFONDO in posizione successiva
arr(k) = temp 'sposto ACCIAIERIE in posizione precedente
End If
'faccio questi spostamenti finché non ordino tutto l'Array
Next k
Next j
'a questo punto, terminata l'operazione, la Function OrdinaArray mi ritorna l'Array ordinato
OrdinaArray = arr
End Function
Private Sub combobox1_change()
'al cambiamento della ComboBox1, valuto se la ComboBox2 non è vuota.
'se non lo è allora aggiorno la ListBox
'quindi entrambe le ComboBox devono essere valorizzate
If ComboBox2 <> "" Then Call aggiornalistbox
End Sub
Private Sub combobox2_change()
'al cambiamento della ComboBox2, valuto se la ComboBox1 non è vuota.
'se non lo è allora aggiorno la ListBox
'quindi entrambe le ComboBox devono essere valorizzate
If ComboBox1 <> "" Then Call aggiornalistbox
End Sub
Private Sub aggiornalistbox()
'procedura per popolare la ListBox
Dim ws As Worksheet
Dim lastrow As Long, i As Long, r As Integer
Dim cantiere As String, dipendente As String, firstAddress As String
Dim f As Range
Dim arrDataNomeOre As Variant
'imposto il Foglio
Set ws = ThisWorkbook.Sheets("rapporti_lavoro")
'determino l'ultima riga compilata
lastrow = ws.Cells(ws.Rows.Count, 1).End(xlUp).Row
dipendente = ComboBox1.Value 'il dipendente assume il valore della ComboBox1
cantiere = ComboBox2.Value 'il cantiere assume il valore della ComboBox2
With ListBox1
.ColumnCount = 3 'imposto 3 colonne alla ListBox
.Clear 'cancello i vecchi valori
'con il metodo Find di Range, cerco il Cantiere nella colonna "A"
Set f = ws.Range("A:A").Find(What:=cantiere, LookIn:=xlValues, LookAt:=xlWhole)
'se esiste il Cantiere...
If Not f Is Nothing Then
'prelevo in una variabile l'indirizzo di cella del primo cantiere che ha trovato
'questo servirà quando dovrò cercare gli altri cantieri,
'perché poi dovrà cercare fin quando non ritorna al primo indirizzo di cella (fistAddress)
firstAddress = f.Address
Do
'se rispetto al cantiere trovato, risulta in colonna "C" (Offset(, 2).Value) il dipendente indicato in ComboBox1, allora...
If f.Offset(, 2).Value = dipendente Then
'aggiungi un rigo alla ListBox
.AddItem
'attraverso un ciclo di 3 iterzioni, popola la ListBox in ogni sua colonna
For r = 1 To .ColumnCount '<-- r va da 1 a 3 perché prima ho definito che le colonne della ListBox devono essere 3
'quindi la prima colonna sarà uguale alla DATA presente in colonna "B" (Offset(, 1) ovvero sposta a destra di un passo rispetto al cantiere trovato)
'quindi la seconda colonna sarà uguale al DIPENDENTE presente in colonna "C" (Offset(, 2) ovvero sposta a destra di due passi rispetto al cantiere trovato)
'quindi la terza colonna sarà uguale alle ORE presenti in colonna "D" (Offset(, 3) ovvero sposta a destra di tre passi rispetto al cantiere trovato)
.List(.ListCount - 1, r - 1) = f.Offset(, r).Value
Next r
End If
'una volta popolato il primo rigo della ListBox, passiamo al cercare il prossimo cantiere, utilizzando il FindNext
Set f = ws.Range("A:A").FindNext(f)
'ripeti la ricerca finché la prossima cella dove risiede il prossimo cantiere è diversa dalla cella del primo cantiere
'ad esempio se i cantieri sono in A2, A5, A10 e A20...allora firstAddress sarà A2. Con FindNext il prossimo cantiere trovato sarà in A5,
'poi in A10 ed infine in A20. A quel punto, proseguirebbe cercandolo di nuovo in A2 ma siccome gli diciamo di continuare (Loop) finché
'l'indirizzo di cella dell'ennesimo cantiere è diverso dall'indirizzo di cella del primo cantiere, allora la ricerca si interromperà
Loop While Not f Is Nothing And f.Address <> firstAddress
End If
'se per caso non esistono dati che si incrociano (Dipendente-Cantiere), allora la ListBox non sarà popolata.
'a quel punto mostro un messaggio di dati insesistenti
If .ListCount = 0 Then MsgBox "Non ci sono dati da mostrare!", vbExclamation, "Attenzione..."
End With
'libero la memoria dagli oggetti creati
Set ws = Nothing
Set f = Nothing
End Sub
@alexps81 funziona perfettamente e anche la tua spiegazione è chiarissima, perfino per me che sono una povera autodidatta agli inizi...ho solo fatto una piccola modifica per visualizzare nella listbox anche la prima colonna. Adesso cercherò di applicare il tuo codice anche per le userform successive, così vedo se ho davvero capito. Grazie ancora!
Bene @Cyberlady, visto che utilizzi Office 365, ti propongo questa modifica che già sulla mia versione che è la 2021 funziona. Quindi funzionerà anche sulla tua. Ma se la utilizzi su versioni precedenti alla 2021 non funzionerà.
Di tutte le procedute che ti ho girato, elimina:
- la Function OrdinaArray
- la Sub caricaDipendenti
- la Sub caricaCantiere
rimpiazza queste tre con solo questo codice:
Private Sub caricaCantiere()
'procedura che carica la ComboBox2 (Cantieri)
Dim ws As Worksheet
Dim ur As Long, i As Long
Dim arrUnici As Variant, arrOrdinati As Variant
'imposto il foglio
Set ws = ThisWorkbook.Worksheets("rapporti_lavoro")
'calcolo l'ultimo rigo compilato in colonna "A" (dove risiedono i nomi dei Cantieri)
ur = ws.Cells(ws.Rows.Count, "A").End(xlUp).Row
'se l'ultimo rigo è superiore ad 1, allora esistono dati, quindi carico i dati univoci e ordinati
If ur > 1 Then Me.ComboBox2.List = Application.WorksheetFunction.Sort(Application.WorksheetFunction.Unique(ws.Range("A2:A" & ur)))
'libero la memoria dagli oggetti creati
Set ws = Nothing
End Sub
Private Sub caricaDipendenti()
'procedura che carica la ComboBox1 (Dipendenti)
'stessa identica procedure della ComboBox2, ma si opera sulla colonna "C"
Dim ws As Worksheet
Dim ur As Long, i As Long
Dim arrUnici As Variant, arrOrdinati As Variant
Set ws = ThisWorkbook.Worksheets("rapporti_lavoro")
ur = ws.Cells(ws.Rows.Count, "C").End(xlUp).Row
If ur > 1 Then If ur > 1 Then Me.ComboBox1.List = Application.WorksheetFunction.Sort(Application.WorksheetFunction.Unique(ws.Range("C2:C" & ur)))
Set ws = Nothing
End Sub
In pratica da Office 2021 a Office 2024 (ma non su tutte le Build) e su Office 365 si possono sfruttare le Funzioni Unique (per estrarre valori univoci) e Sort (per ordinare l'elenco).
Fammi sapere se funziona.
Ciao.
@alexps81 funziona alla grande. Tu invece sai scovare il perchè il comando stampa dia un errore che nella userform ReportCantieri non da? Il codice è identico (ho fatto copia e incolla), il calcolo del totale ore nella textbox anche e funziona benissimo, cambia solo la textbox ma in questa userform mi restituisce "totale" come variabile non definita
Private Sub Stampa_Click()
Dim wsProvvisorio As Worksheet
Dim i As Long, j As Long
Dim ultimariga As Long
On Error Resume Next
Set wsProvvisorio = ThisWorkbook.Sheets("ReportOperai")
If wsProvvisorio Is Nothing Then
Set wsProvvisorio = ThisWorkbook.Sheets.Add
wsProvvisorio.Name = "ReportOperai"
End If
On Error GoTo 0
wsProvvisorio.Cells.Clear
For i = 0 To ListBox1.ListCount - 1
For j = 0 To ListBox1.ColumnCount - 1
wsProvvisorio.Cells(i + 1, j + 1).Value = ListBox1.List(i, j)
Next j
Next i
wsProvvisorio.Columns.AutoFit
totale = Val(Me.TextBox1.Value)
ultimariga = wsProvvisorio.Cells(wsProvvisorio.Rows.Count, 1).End(xlUp).Row + 2
wsProvvisorio.Cells(ultimariga, 3).Value = "Totale ore:"
wsProvvisorio.Cells(ultimariga, 4).Value = totale
Application.Dialogs(xlDialogPrinterSetup).Show
ThisWorkbook.Sheets("ReportOperai").PrintOut copies:=1
Application.DisplayAlerts = False
wsProvvisorio.Delete
Application.DisplayAlerts = True
MsgBox "Stampa completata", vbInformation
End Sub
Ciao, sarò lieto di aiutarti appena posso (osa rispondo da cellulare), però credo che dato che si tratta di un altro argomento, se questo è risolto dovresti spuntare come tale e aprirne uno ad hoc.
P.S. nell'ultimo codice che ti ho girato, c'è un refuso: le variabili i As Long, arrUnici As Variant e arrOrdinati As Variant le puoi cancellare in entrambe le Sub. Erano rimaste lì mentre facevo le prove, ma non servono.
