PDA

Visualizza versione completa : sfarfallio timer in visual basic


Acqua1
13-06-2003, 17.03.45
il mio è un problema banalissimo
Come si fa ad evitare lo "sfarfallio" dell'immagine nell'utilizzo dei controlli
"timer" ?

io ho una textbox grande quasi tutto un form, questa textbox deve visualizzare all’accadimento di un dato evento, del testo che però viene stampato carattere per carattere, come se un utente stesse digitando quel testo.

In questa fase di stampa “carattere x carattere” avviene lo sfarfallio di cui parlavo.

Per maggior chiarezza le faccio vedere i tentativi che ho fatto sin ora:
la sub normale è questa:

(shell = textbox, testo è una stringa che contiene tutto il testo da stampare)

Private Sub Timer1_Timer()
start = start + 1
Shell.Text = Left (testo, start)
....
End Sub

questa stampa tutta la stringa + un carattere ogni volta.

pensavo che lo sfarfallio potesse dipendere dal fatto che riscriveva ogni volta
tutta la stringa e ho fatto così:

Private Sub Timer1_Timer()
start = start + 1
Shell.Text = Shell + Mid(testo, 1 + start, 1)
....
End Sub

lo sfarfallio della schermata continua a presentarsi

allora ho provato a non usare i timer e ho creato una cosa che in teoria doveva
funzionare ma in pratica no:)

in un modulo ho dichiarato:

Declare Sub Sleep Lib "kernel32" (ByVal dwMilliseconds As Long)

For start = 0 To len(testo)
Shell.Text = Shell + Mid(testo, 1 + start, 1)
Sleep (100)
Next

la funzione non va perché il programma stampa il risultato solo quando ha finito
l'intero ciclo (quindi ti stampa tutta la stringa contenuta in testo, facendoti
inoltre aspettare il tempo di sleep x ogni carattere di testo).

Morale della favola, nn so cosa fare :), anche perché non so le esatte cause che
generano lo sfarfallio. ho provato a cambiare di molto la proprietà Interval
del timer portandola da 1 fino a 1000 ma lo sfarfallio c'era sempre.

LoryOne
14-06-2003, 12.32.46
Lo sfarfallio è veramente uno di quei problemi che danno letteralmente fastidio, soprattutto quando ai nostri programmi vogliamo dare un tocco di classe simulando l' opera di una telescrivente, come il nostro amico Acqua1 vuole fare.

L'utlizzo della proprietà ".Text" del controllo Textbox non sembra essere la più indicata in quanto ad ogni chiamata, il contenuto della casella di testo viene cancellata e riscritta con il nuovo testo impostato.
Tutto ciò avviene in maniera molto rapida, tanto che all'occhio umano l'operazione risulta essere impercettibile.

L'imperfezione, però, risulta essere tanto più visibile con un controllo timer, tanto più l' intervallo di ripetizione dell'evento Timer è ridotto al minimo.
Il controllo TextBox, oltre alla proprietà ".Text" ha un'interessantissima proprietà chiamata ".SelText".
Tale proprietà viene utilizzata per incollare un testo all'interno del controllo senza eseguire un refresh dello stesso.

Utilizzando detta proprietà come da codice sottoriportato, è possibile evitare lo sfarfallio del testo persino con la proprietà ".Interval" del controllo timer pari a 1 millisecondo.

Codice:
------------------------------------------------------------------
Const c = "Quarantaquattro gatti in fila per sei col resto di due"

Dim a As Long

Private Sub Timer1_Timer()
a = a + 1
If a <= Len(c) Then Text1.SelText = Mid$(c, a, 1)
End Sub
------------------------------------------------------------------

Un ultima cosa:
Dai del tu a tutti noi. Sei tra amici

Ciao :)

LoryOne
16-06-2003, 11.01.29
Devo scusarmi con Acqua1 per aver quasi risolto il suo problema.
Ci sono parecchie cose che ho notato grazie al suo post e le elenco brevemente:

1) Il codice postato da me sembra funzionare a dovere solo se i font non sono True Type.
Infatti, se si imposta il Font come Ms Sans Serif, cioè Text1.Font="Ms Sans Serif", lo sfarfallio non sussiste.
Al contrario, impostando il font come "Arial", lo sfarfallio permane anche se ridotto.

Come fare, allora ?
Visto che il problema è sicuramente dovuto al refresh del controllo, sono ricorso in primis alle API, anche se senza successo.
Vi è una API chiamata SendMessage ed una costante chiamata WM_SETREDRAW come messaggio.

ho utilizzato tale API in questo modo
SendMessage Text1.hwnd,WM_SETREDRAW,0,0 per evitare il refresh e SendMessage Text1.hwnd,WM_SETREDRAW,1,0 per ripristinarlo.
Risultato insoddisfacente.

Sono ricorso ad un'altra API e cioè LockWindowUpdate ma con risultato simile.

2) A questo punto mi si sono presentate due strade.
La prima era quella di ricorrere alle tecniche di Hooking e Subclassing sul textbox, mentre la seconda (nella quale riponevo maggiori speranze) era quella che per una cosa così "banale" ci doveva essere sicuramente un metodo più semplice.

Ho rilanciato il codice postato ed ho atteso il termine del ciclo.
Ho notato che eseguendo una cancellazione a ritroso del testo immesso nel controllo, questo mi apportava un fastidioso flickering (Sfarfallio).
Lo stesso non si poteva dire se si aggiungva del testo tenendo premuto ripetutamente un tasto a caso.

E di qui l'illuminazione

Perchè non "spedire" un tasto al controllo ?
SendKeys, ecco la soluzione !!!!

Codice:
------------------------------------------------------------------
Const c = "Quarantaquattro gatti in fila per sei col resto di due"

Dim a As Long

Private Sub Timer1_Timer()
a = a + 1
If a <= Len(c) Then SendKeys mid$(c,a,1)
End Sub
------------------------------------------------------------------

Devo ringraziare Acqua1 per avermi dato l'opportunità di imparare ancora qualcosa.

Ciao :)

LoryOne
16-06-2003, 11.05.38
...Ancora una cosa, credo molto imporatnete.
Prima di utilizzare SendKeys è necessario portare il focus sulla textbox ;)