2014-11-25 34 views
5

Non sono un programmatore VBA quindi mi scuso in anticipo se alcuni dei miei termini in questa domanda non sono corretti. Un mio collega ha voluto cancellare la selezione da una casella di riepilogo non appena è stata selezionata. Dopo alcune ricerche su google abbiamo trovato un modo per farlo tramite l'evento Change. Inizialmente abbiamo provato:VBA (Excel) ActiveX ListBox Change Event comportamento ricorsivo evento

Private Sub ListBox1_Change() 
    For i = 0 To ListBox1.ListCount - 1 
     ListBox1.Selected(i) = False 
    Next i 
End Sub 

Tuttavia, sembrava che l'impostazione della proprietà Selected False innesca una Change evento sulla casella di riepilogo e questa diventa effettivamente un ciclo infinito e provoca Excel (2007) il crash. Dato che sapevamo che c'erano anche due voci che abbiamo provato:

Private Sub ListBox1_Change() 
    ListBox1.Selected(0) = False 
    ListBox1.Selected(1) = False 
End Sub 

E funziona! Anche se ci aspetteremmo lo stesso comportamento - per l'impostazione della proprietà selezionata per far sì che l'evento Modifica si inneschi nuovamente e ottenga un ciclo infinito.

Tuttavia sembra che una volta per es. ListBox1.Selected (0) = false l'evento Change viene ri-attivato ma in quel iterazione non retriggera su questa linea - credo perché si sa che questo selezionati proprietà è già stata impostata a False per questo articolo, quindi non sta cambiando nulla.

Ma se questo è il caso allora avremmo aspettiamo anche che il comportamento nella prima soluzione .. così sembra che ci sia qualche differenza nel dire ListBox1.Selected (i) = False contro specificando l'oggetto reale indicizzare direttamente (piuttosto che tramite la variabile i).

Qualcuno conosce il motivo di questo comportamento? Spero che la domanda abbia senso, ho cercato di spiegarlo nel modo migliore possibile.

Grazie Amit

+0

Per interrompere gli eventi di reinnesco, leggere [this] (http://www.jkp-ads.com/Articles/NoEvents00.asp). –

+0

Grazie Cool Blue - in effetti abbiamo risolto il problema del loop usando qualcosa di simile a quanto discusso nel link che hai postato .. ma la mia domanda sopra (probabilmente non molto chiara!) È cercare di capire perché il comportamento differisce nei due casi (il primo dà un ciclo infinito il secondo no) – user555265

+0

Ci sono letteralmente solo 2 elementi disponibili da selezionare nella lista? O vuoi dire che sono stati selezionati solo 2 oggetti? –

risposta

0

allegare il ciclo per l'evento click al posto del l'evento di modifica di fermare i cicli infiniti:

Private Sub ListBox1_Click() 
'Do the stuff you want to do based on the selection 

'Clear selections 
For i = 0 To ListBox1.ListCount - 1 
    ListBox1.Selected(i) = False 
Next i 
End Sub 

non credo che il "perché" di questa domanda ha un facile risposta. Generalmente, la parola è molto lineare e può elaborare solo 1 cosa alla volta. Tuttavia, i processori moderni ottimizzano il codice e gestiscono le cose in tandem. Word si confonde semplicemente in modo abbastanza prevedibile per fornire soluzioni relativamente coerenti per la confusione. Il trucco che hai menzionato sopra (farlo due volte) è un modo popolare per gestire questo problema (ho visto molte raccomandazioni quando ho avuto lo stesso problema con le caselle di riepilogo).

Preferisco migliorare la logica piuttosto che tentare un lavoro approssimativo. Come sopra, piuttosto che disattivare gli eventi o usare il doppio trucco, mi sono semplicemente reso conto che l'evento di cambiamento non era l'evento corretto da usare.

+0

Credo che abbiamo provato con ListBox Click, ma penso che questo sia stato attivato prima che la selezione fosse "fisicamente" selezionata nella casella di riepilogo, quindi la modifica della proprietà Selected non ha avuto effetto – user555265

0

avrei dovuto fare più ricerca su questo, ma so che nel corso di un ciclo For...Next, una volta che il codice colpisce la linea Next si aggiunge 1 alla variabile (ammesso che non è stato definito un Step) e poi corre un test logico per determinare se la variabile si trova ancora tra le condizioni For blah inclusive. Poiché ognuno di questi funziona in modo indipendente, consente al codice di verificare l'evento _Change().Alcune soluzioni alternative per impedire un ciclo infinito: Change() Event Infinite Loops

MODIFICA: ecco alcune informazioni aggiuntive su cosa causa il trigger ListBox.Change Documentation. Si segnala che

Inoltre, una modifica programmatica a ListBox.ListIndex attiva anche l'evento di modifica.

che potrebbe essere ciò che viene controllato tra i cicli.

0

Hai ragione sull'utilizzo dell'evento Change, ma è in qualche modo complicato in ListBox. Stai ovviamente attivando l'evento Cambia ogni volta che deselezioni un elemento, perché sei modificando il codice del ListBox.

La risposta da guitarthrower va nella buona direzione, ma non credo che Application.EnableEvents sarebbe fare il lavoro anche in questo caso non ha alcun effetto sugli oggetti ActiveX.

Quindi, vorrei provare questo due alternative che cercano di emulare il lavoro di Application.EnableEvents dalla programmazione normale:

  • Alternativa 1: soluzione generale per l'evento Change. Tuttavia, è un po 'rischioso se la variabile NoExecute non viene resettata per nessun motivo (la funzione si chiude a causa di un errore).

    Private NoExecute As Boolean 
    
    Private Sub ListBox1_Change() 
    
        If NoExecute Then Exit Sub 
    
        NoExecute = True 
    
        For i = 0 To ListBox1.ListCount - 1 
         ListBox1.Selected(i) = False 
        Next 
    
        NoExecute = False 
    
    End Sub 
    
  • Alternativa 2: migliore soluzione in questo caso, anche se non così bella come quella precedente.

    Private Sub ListBox1_Change() 
        For i = 0 To ListBox1.ListCount - 1 
         If ListBox1.Selected(i) Then 
          ListBox1.Selected(i) = False 
         End If 
        Next 
    End Sub   
    

Let me know wether ha funzionato.

Questo sito descrive Alternativa 1 in profondità: http://www.cpearson.com/excel/SuppressChangeInForms.htm

+0

Grazie per le informazioni - in effetti abbiamo implementato la prima soluzione come soluzione, la mia domanda era più perché c'è una differenza di comportamento per i due casi che ho descritto – user555265

0

Per fermare la follia infinita, si può fare questo (duro ancora migliore domanda è: perché si dovrebbe fare questo comunque, perché dopo questo valore può essere selezionata o in realtà può essere, ma è subito cambiato di nuovo a false) -

Private Sub ListBox1_Change() 
    For i = 0 To ListBox1.ListCount - 1 
     if ListBox1.Selected(i) = true then ListBox1.Selected(i) = false 
    Next i 
End Sub 

Ora, la ragione per la ciclo infinito non è visibile e si può solo immaginare, ma prima lascia corretta l'ipotesi che il blocco di codice senza la per la prossima loop non causerebbe lo stesso problema. Effettivamente fa la stessa cosa del primo e rimane bloccato in un ciclo infinito, se cambi il valore di ListBox1.Selected (1) o successivo ..

Perché questo accade è l'evento di cambiamento verrà attivato correttamente dopo che il primo valore è stato modificato e poiché la prima cella è stata modificata, l'evento change viene attivato e l'esecuzione del codice viene interrotta per quell'istanza e ricomincia. Ora entrambi i codici funzionano senza causare un loop se solo il primo elemento viene modificato e il motivo per cui ciò accade è probabile che il compilatore entri a far parte dei suoi automatismi. Non posso dirvi il vero motivo dietro gli avvenimenti, ma ciò che probabilmente accade è che il compilatore prevede quando solo il primo valore è cambiato e ottimizza il codice per fare solo l'unica modifica e nient'altro e quando altri valori cambiano sta cercando di riscrivi ogni valore e il primo valore ogni volta che viene attivato il ciclo infinito.

Ecco come è, per comprendere appieno cosa sta succedendo sotto il cofano, è meglio contattare MS.

0

Solo per riferimento futuro: Se si desidera solo per selezione disabilitazione in un controllo ListBox è possibile utilizzare

ListBox.Enabled = False 

o

ListBox.Locked = True 

o entrambi, ovviamente.

È possibile trovare dettagli sulla differenza di comportamento su MSDN.
Fondamentalmente si suppone di essere come questo (direttamente dal l'articolo di MSDN linkato sopra):

  • Se Enabled e Locked sono entrambi vero, il controllo può essere attivato e ricevere appare normalmente (non grigio) in forma . L'utente può copiare, ma non modificare, i dati nel controllo.
  • Se Enabled è Vero e Locked è False, il controllo può essere attivato e ricevere appare normalmente in forma. L'utente può copiare e modificare i dati nel controllo.
  • Se Enabled è False e Locked è vero, il controllo non può ricevere l'attivazione e non è selezionabile nel modulo. L'utente non può né copiare né modificare i dati nel controllo.
  • Se Enabled e Locked sono entrambi False, il controllo non può ricevere lo stato attivo e viene disattivato nel modulo. L'utente non può né copiare né modificare i dati nel controllo.

Nota che non sono sicuro del comportamento in Office 2007 poiché ho provato il tuo esempio con Office 2013 e ho ottenuto lo stesso identico comportamento per entrambi i frammenti di codice. E entrambi non impediscono la selezione ma piuttosto "nulla" (beh, vengono chiamati due volte ma la modifica non influisce sulla selezione visualizzata).

E durante i miei esperimenti ho anche notato che l'evento ListBox_Change viene chiamato prima dell'evento ListBox_Click.

1

Sono un anno in ritardo alla festa ma spero che questo possa aiutare gli altri. Ho riscontrato problemi con Listbox1_Click() loop infinito anziché change(). Tuttavia, penso che questa possa essere una soluzione praticabile per entrambi.

Ogni volta che ho chiamato Listbox1.Selected (i) = True, sarebbe scattare come un Click() o un Change(). Nella mia routine click(), ci sono alcuni indici che fanno si che l'intero elenco si ripopuli da solo con un nuovo elenco e riseleziona stesso. Ciò causa il ciclo infinito quando viene riselezionato.Mi ci è voluto un giorno per risolvere i problemi, ma alla fine la soluzione non era quella di utilizzare l'evento click(); invece, ho usato l'evento MouseDown() con un piccolo calcolo. Questo elimina l'uso di click(). Ho notato che sto usando questo in una singola lista di selezione e non in una lista di selezione multipla. È possibile utilizzare un'istruzione Se con un valore booleano per applicarlo a selezione multipla. In bocca al lupo!

Private Sub ListBox1_MouseDown(ByVal Button As Integer, ByVal Shift As 

Integer, ByVal x As Single, ByVal Y As Single) 

    On Error Resume Next 'You can comment this out for trouble shooting 
    If Button = 1 And UBound(ListBox1.List) <> -1 Then 
     ListBox1.Selected(((Y/9.75) - 0.5) + ListBox1.TopIndex) = True 
     MsgBox "left Click" 
     'You can use Button = 2 for right click 
     'Do some other stuff including listbox1.select(1234) 

    End If 
End Sub