Newsgroup: it.comp.lang.visual-basic
Titolo: gestione errori

Due domandine facili facili (per chi sa)
Ho costruito una userform ed il modulo relativo con due ComboBox ed
una serie di TextBox con la quale leggo e scrivo dati su di un foglio
Excel.
In caso di errore come faccio in modo che il programma si riposizioni
sul TextBox o sul Combo da cui e' partito l' errore?
Come faccio a chiamare la routine da un pulsante di Excel?
Grazie
Renzo Giurini

 Come faccio a chiamare la routine da un pulsante di Excel?
Se clicchi col destro in design, ti manda nella Sub dell'evento Click del
pulsante.
Puoi arrivarci anche dall'editor di visual basic per application, sotto il
foglio del pulsante.
Da lě puoi chiamare la routine desiderata
Spero di non aver detto tavanate.
Ciao

 Come faccio a chiamare la routine da un pulsante di Excel?
Intendi un pulsante posto direttamente sul foglio piuttosto che sullo
UserForm? Se ho inteso bene cio` che chiedi: scrivi la routine come Public
Function in un modulo in modo da poterla invocare sia dallo UserForm che da
una formula in una cella del foglio. Tale funzione potrebbe restituire un
numero corrispondente all'ordine dei parametri passati in modo da sapere
quale di questi e` il primo che ha un valore non consentito. Cosi`, quando
richiamata da uno UserForm, hai l'informazione per poter impostare il fuoco
(<controllo>.SetFocus) sul controllo corrispondente.
--
Ciao.
Maurizio
--------
?SPQR(C)
 X
--------



Si' intendo proprio la possibilita' di chiamare la Useform con un
pulsante posto sul foglio.
Ma mi interessa anche la possible gestione degli errori
Grazie per l' intervento
Renzo




Ma mi interessa anche la possible gestione degli errori.
SEGUITO
Per essere piu' chiaro, credo di aver capito la procedura  che tu
esponi, solo che sono piu' imbranato di cosi' e non so proprio come
fare.


 Ma mi interessa anche la possible gestione degli errori
Ricapitoliamo.
1. Pulsante sul foglio di lavoro che avvia uno UserForm:
. disegni il pulsante (un "Pulsante di comando" o "CommandButton". Non il
"Pulsante" che trovi nella barra degli strumenti "Moduli") sul foglio di
lavoro e ti ritrovi in "Modalita` progettazione". Da questo momento in poi
devi ragionare come se il foglio di lavoro non fosse piu` tale ma quello che
la guida chiama un "Modulo", in pratica uno "UserForm" che e` anche un
"Foglio di lavoro"... o un "Foglio di lavoro" che si comporta come uno
"UserForm"... (decidi tu;-)
. imposta la proprieta` (clic destro sul pulsante e scegli "Proprieta`")
"TakeFocusOnClick" a False, poi chiudi la finestra "Proprieta`";
. doppio clic sul pulsante e passi alla finestra del codice del foglio di
lavoro, col punto di editazione fra le righe:
    Private Sub CommandButton1_Click()
    End Sub
e scrivi:
    UserForm1.Show
in modo da ottenere:
    Private Sub CommandButton1_Click()
        UserForm1.Show
    End Sub
. lo UserForm cosi` avviato possiedera` un CommandButton nel cui evento clic
ci sara`, oltre al resto ed alla fine, per esempio, qualcosa del genere:
    Private Sub cmdOk()
        <tuo codice>
        Unload Me
    End Sub
2. Gestione degli errori:
Non mi e` chiaro cosa tu intenda. Di quali errori parli? Io avevo inteso ti
riferissi agli errori di immissione dell'utente, ovvero a cio` che
normalmente si chiama validazione dei dati immessi. Ora pero` non capisco se
intendi, invece, l'intercettazione degli errori causati dalle istruzioni del
tuo programma. Puoi chiarire? Magari con un esempio.




Grande funziona perfettamente.Non capisco cosa significhi l'
istruzione "Private Sub cmd=K()" sopra, che non appare nel mio
listato, ma va bene egualmente.
Per il punto 2 qui sotto intendevo gli errori generati nell'
immissione. Se immetto un dato sbagliato mi viene generato, per
esempio,  l'errore "Indice non incluso nell'intervallo". Vorrei che
dopo aver segnalato o no l' errore la routine riprendesse dalla stessa
casella di immissione per digitare il dato corretto.
Grazie e saluti
Renzo


 > 2. Gestione degli errori:
[...]
Benissimo. Chiariamo questo punto. Con il classico esempio della divisione.
Poniamo che sul modulo tu abbia tre etichette, tre caselle di testo e due
pulsanti di comando, in quest'ordine:
    Label1
    TextBox1
    Label2
    TextBox2
    Label3
    TextBox3
    CommandButton1 CommandButton2
Siccome e` scomodo lavorare con nomi cosi` generici ridenominiamo cosi`:
    lblDividendo
    txtDividendo
    lblDivisore
    txtDivisore
    lblRisultato
    txtRisultato
    cmdOk
    cmdAnnulla
Poi, da codice come nella routine seguente, o direttamente nella finestra
delle proprieta`, assegnamo dei valori iniziali alle proprieta` dei
controlli che ci interessa modificare:
Option Explicit
Private Sub UserForm_Initialize( _
)
  Caption = "Divisione"
  With lblDividendo
    .Caption = "Dividendo:"
    .Width = txtDividendo.Width
    .AutoSize = True
    .Accelerator = "D"
  End With
  With lblDivisore
    .Caption = "Divisore:"
    .Width = txtDivisore.Width
    .AutoSize = True
    .Accelerator = "v"
  End With
  With lblRisultato
    .Caption = "Risultato:"
    .Width = txtRisultato.Width
    .AutoSize = True
    .Accelerator = "R"
  End With
  With cmdOk
    .Caption = "Ok"
    .Default = True
  End With
  With cmdAnnulla
    .Caption = "Annulla"
    .Cancel = True
  End With
End Sub
E liquidiamo subito il pulsante Annulla, che deve solo chiudere il modulo.
Private Sub cmdAnnulla_Click( _
)
  Unload Me
End Sub
Passiamo poi al pulsante Ok:
Private Sub cmdOk_Click( _
)
  txtRisultato.Text = txtDividendo.Text / txtDivisore.Text
End Sub
e con F5 avviamo il primo test.
Se non scriviamo nessun valore e premiamo subito "Invio" otterremo
un'interruzione dell'esecuzione col messaggio:
    Errore di run-time '13':
    Tipo non corrispondente
    [Fine] [Debug] [?]
Scegliamo "Debug" e ci troveremo sulla riga evidenziata:
    txtRisultato.Text = txtDividendo.Text / txtDivisore.Text
Qual e` il "tipo non corrispondente"? Il testo di txtRisultato, quello di
txtDividendo o quello di txtDivisore? E "tipo" di che? Tipo di dato,
ovviamente. Passiamo col mouse, senza cliccare, sulle parole evidenziate e
scopriremo che tutte e tre hanno valore "", ovvero stringa nulla. E sappiamo
bene che non e` possibile eseguire un'operazione matematica su dati di tipo
stringa. Avremmo dovuto prima convertire dal tipo stringa ad un tipo
numerico. Poco male perche' lo possiamo fare anche subito. Correggiamo cosi`
l'istruzione in questo modo:
    txtRisultato.Text = Val(txtDividendo.Text) / Val(txtDivisore.Text)
e premiamo F8 per continuare l'esecuzione interrotta dall'errore. Ma ecco un
nuovo errore:
    Errore di run-time '6':
    Overflow
    [Fine] [Debug] [?]
Si`, perche' le due stringhe nulle convertite in valori numerici dalla
funzione "Val" hanno assunto il valore zero e in questo caso la divisione
per zero genera questo errore. In effetti ci saremmo aspettati l'errore 11,
"Divisione per zero"... ma non importa.
Per ora non possiamo farci niente, quindi selezioniamo l'istruzione
seguente:
    End Sub
premiamo Maiusc+F9 per continuare l'esecuzione da questa istruzione,
premiamo F5 per continuare, quindi Esc e chiudiamo lo UserForm concludendo
cosi` l'esecuzione del nostro programma.
Dobbiamo prendere una decisione. Intercettare gli errori o validare i dati?
Ovvero, far gestire l'errore dal nostro programma quando avviene o
verificare, prima di eseguire la divisione, se i dati immessi dall'utente
sono validi?
Il sistema piu` sbrigativo e` sicuramente scrivere una routine di questo
genere:
Private Sub cmdOk_Click( _
)
  On Error GoTo cmdOk_Click_Error
  txtRisultato.Text = txtDividendo.Text / txtDivisore.Text
  Exit Sub
cmdOk_Click_Error:
  With Err
    txtRisultato.Text _
      = "Errore " & Format$(.Number, "0") & ": " _
      & .Description
  End With
  txtDividendo.SetFocus
End Sub
Ma l'informazione che viene fornita all'utente e` di tipo *tecnico*,
interessa piu` a noi che a lui che il numero di errore e`, per esempio, 13 e
vuol dire "Tipo non corrispondente". L'utente potrebbe allarmarsi
inutilmente, valutare male il da farsi e, soprattutto, non gli forniremmo
una informazione fondamentale: cosa deve fare per risolvere la situazione.
Sarebbe opportuno quindi adeguare il messaggio agli scopi su esposti,
indicare all'utente qual e` il primo valore da correggere e magari
posizionare il cursore sul controllo corrispondente, giusto?
Compito per casa!... :-)))


 Compito per casa!... :-)))
Oops!...
Ti ho assegnato come "compito per casa" proprio quello che avevi chiesto
inizialmente! Scusa, evidentemente mi ero perso. :-(
Dicevo che le possibilita` sono due, (1) intercettare l'errore quando
avviene o (2) prevenirlo "validando" l'immissione dell'utente. E
l'immissione dell'utente puo` essere (2a) validata controllo per controllo,
cioe` man mano che l'utente si sposta da un controllo all'altro oppure (2b)
una volta sola per tutti i controlli prima di lanciare l'elaborazione dei
dati immessi.
Il caso (2a) non lo prendo in considerazione perche' di gestione leggermente
piu` difficile e talvolta inutile. Ti propongo quindi due esempi di (1) e
(2b), sempre relativi al modulo che abbiamo costruito nel mio post
precedente.
Caso (1). Intercettazione degli errori.
Private Sub cmdOk_Click( _
)
  On Error GoTo cmdOk_Click_Error
  Const c_intNOERRORI     As Integer = 0
  Const c_intERRDIVISORE  As Integer = 1
  Const c_intERRDIVIDENDO As Integer = 2
  Const c_intERRDIV0      As Integer = 3
  Dim intImmissioneErrata As Integer
  Dim dblDividendo        As Double
  Dim dblDivisore         As Double
  intImmissioneErrata = c_intNOERRORI
  If IsNumeric(txtDividendo.Text) = True Then
    dblDividendo = CDbl(txtDividendo.Text)
  Else
    intImmissioneErrata = c_intERRDIVISORE
  End If
  If intImmissioneErrata = c_intNOERRORI Then
    If IsNumeric(txtDivisore.Text) = True Then
      dblDivisore = CDbl(txtDivisore.Text)
    Else
      intImmissioneErrata = c_intERRDIVIDENDO
    End If
  End If
  If intImmissioneErrata = c_intNOERRORI Then
    If dblDivisore = 0 _
      Then intImmissioneErrata = c_intERRDIV0
  End If
  Select Case intImmissioneErrata
  Case c_intERRDIVISORE
    txtRisultato.Text = "Valore dividendo non valido."
    txtDividendo.SetFocus
  Case c_intERRDIVIDENDO
    txtRisultato.Text = "Valore divisore non valido."
    txtDivisore.SetFocus
  Case c_intERRDIV0
    txtRisultato.Text = "Divisione per 0 non consentita."
    txtDivisore.SetFocus
  Case Else
    txtRisultato.Text = dblDividendo / dblDivisore
    txtRisultato.SetFocus
  End Select
  Exit Sub
cmdOk_Click_Error:
  With Err
    txtRisultato.Text _
      = "Errore " & Format$(.Number, "0") & ": " _
      & .Description
  End With
  txtRisultato.SetFocus
End Sub
Caso (2b). Validazione dei dati immessi dall'utente.
Private Sub cmdOk_Click( _
)
  On Error Resume Next
  Dim dblDividendo As Double
  Dim dblDivisore  As Double
  dblDividendo = CDbl(txtDividendo.Text)
  If Err.Number = 0 Then
  ElseIf Err.Number = 13 Then
    txtRisultato.Text = "Valore dividendo non valido."
    txtDividendo.SetFocus
    Exit Sub
  Else
    GoTo cmdOk2_Click_Error
  End If
  dblDivisore = CDbl(txtDivisore.Text)
  If Err.Number = 0 Then
  ElseIf Err.Number = 13 Then
    txtRisultato.Text = "Valore divisore non valido."
    txtDivisore.SetFocus
    Exit Sub
  Else
    GoTo cmdOk2_Click_Error
  End If
  txtRisultato.Text = dblDividendo / dblDivisore
  If Err.Number = 0 Then
  ElseIf Err.Number = 11 Then
    txtRisultato.Text = "Divisione per 0 non consentita."
    txtRisultato.SetFocus
    Exit Sub
  Else
    GoTo cmdOk2_Click_Error
  End If
  Exit Sub
cmdOk2_Click_Error:
  With Err
    txtRisultato.Text _
      = "Errore " & Format$(.Number, "0") & ": " _
      & .Description
  End With
  txtRisultato.SetFocus
End Sub


Scusa ma... la fretta! Butta via il post precedente e sostituiscilo con
quanto segue.

Oops!...





  On Error Resume Next
  Dim dblDividendo As Double
  Dim dblDivisore  As Double
  dblDividendo = CDbl(txtDividendo.Text)
  If Err.Number = 0 Then
  ElseIf Err.Number = 13 Then
    txtRisultato.Text = "Valore dividendo non valido."
    txtDividendo.SetFocus
    Exit Sub
  Else
    GoTo cmdOk_Click_Error
  End If
  dblDivisore = CDbl(txtDivisore.Text)
  If Err.Number = 0 Then
  ElseIf Err.Number = 13 Then
    txtRisultato.Text = "Valore divisore non valido."
    txtDivisore.SetFocus
    Exit Sub
  Else
    GoTo cmdOk_Click_Error
  End If
  txtRisultato.Text = dblDividendo / dblDivisore
  If Err.Number = 0 Then
  ElseIf Err.Number = 11 Then
    txtRisultato.Text = "Divisione per 0 non consentita."
    txtRisultato.SetFocus
    Exit Sub
  Else
    GoTo cmdOk_Click_Error
  End If
  Exit Sub

End Sub




  intImmissioneErrata = c_intNOERRORI

  Else
    intImmissioneErrata = c_intERRDIVISORE
  End If

    Else
      intImmissioneErrata = c_intERRDIVIDENDO
    End If
  End If

  End If




  End Select
  Exit Sub

End Sub
--
