PDA

View Full Version : [VB.NET] Grafica che scompare


Capello82
19-04-2008, 10:18
Salve a tutti. Ho un problema con la grafica; devo disegnare delle linee (uso "drawline") man mano che invio dei dati sulla seriale. Di per sé ci riesco, ma queste scompaiono se la form viene sovrapposta da altre finstre. In questo caso l'applicazione riprende a disegnare solo le nuove linee, ma quelle vecchie sono ormai state cancellate. Come faccio a mantenerle tutte?

MarcoGG
20-04-2008, 12:56
Questo tuo problema è abbastanza frequente nel passaggio da VB6 a .NET.
In VB6 una Form aveva una particolare proprietà AutoRedraw che permetteva di rendere persistente la grafica.
In .NET non mi risulta esista più tale proprietà ( che era molto comoda, anche se in particolari condizioni portava all'errore... ).

Una possibile soluzione consiste nell'inserire il codice che genera la grafica desiderata, direttamente nell'evento Paint della Form.
Esempio VB .NET 2003 ( 10 righe rosse orizzontali ) :

Private Sub Form1_Paint(ByVal sender As Object, ByVal e As System.Windows.Forms.PaintEventArgs) Handles MyBase.Paint

Dim penna As New System.Drawing.Pen(System.Drawing.Color.Red)
penna.Width = 2

Dim G As System.Drawing.Graphics
G = Me.CreateGraphics()

Dim i As Integer = 0
For i = 1 To 10
G.DrawLine(penna, 0, i * 50, 500, i * 50)
Next

penna.Dispose()
G.Dispose()

End Sub

Da quanto ho capito, se queste linee vengono generate di continuo in base a dati particolari ( Timer, Porte ecc... ), dovrai modificare il tuo codice in modo da forzare l'evento Paint ogni volta che dovrai disegnare una nuova linea...
In pratica devi distinguere e separare i dati in ingresso dalla loro rappresentazione grafica.
Il metodo .Refresh forza l'evento Paint.

Capello82
21-04-2008, 10:51
Dove si trova il metodo refresh? Ho provato a scrivere refresh() e ma la prende come "Public overridable sub refresh". Non so se sia quello che intendi tu.

MarcoGG
21-04-2008, 10:59
Dove si trova il metodo refresh? Ho provato a scrivere refresh() e ma la prende come "Public overridable sub refresh". Non so se sia quello che intendi tu.


No, Refresh è un metodo comune a molti controlli del Framework .NET, non lo puoi usare così da solo.
Esempio :

Form1.ActiveForm.Refresh()

Nome_Form.Refresh()

PictureBox1.Refresh()

ecc...

Capello82
21-04-2008, 11:08
No, Refresh è un metodo comune a molti controlli del Framework .NET, non lo puoi usare così da solo.
Esempio :

Form1.ActiveForm.Refresh()

Nome_Form.Refresh()

PictureBox1.Refresh()

ecc...

Ok grazie ^^ comqune "Form1.ActiveForm.Refresh()" è lo stesso che scrivere solo Refresh().

Comunque, pensandoci bene, il mio problema è un po' più serio. Come accennato sul primo post, io invio sulla seriale dei dati: questi non sono altro che delle coordinate X,Y che io voglio unire con delle linee. Ora, ogni volta che forzo l'evento paint purtroppo tengo in considerazione l'ultima linea. Quello che vedo quindi è solo l'ULTIMA linea disegnata, mentre le altre le perdo. Una possibilità sarebbe di creare array dinamici che vengono riempiti man mano con le coordinate che invio sulla seria e che, alla forzatura dell'evento paint, mi permettono di ridisegnare TUTTE le linee. Però mi chiedevo se ci fosse qualcosa di più semplice e "leggero" che mi mantenga semplicemente un disegno fatto sullo schermo :S

MarcoGG
21-04-2008, 11:25
io invio sulla seriale dei dati: questi non sono altro che delle coordinate X,Y che io voglio unire con delle linee. Ora, ogni volta che forzo l'evento paint purtroppo tengo in considerazione l'ultima linea. Quello che vedo quindi è solo l'ULTIMA linea disegnata, mentre le altre le perdo. Una possibilità sarebbe di creare array dinamici che vengono riempiti man mano con le coordinate che invio sulla seria e che, alla forzatura dell'evento paint, mi permettono di ridisegnare TUTTE le linee. Però mi chiedevo se ci fosse qualcosa di più semplice e "leggero" che mi mantenga semplicemente un disegno fatto sullo schermo :S


Quella dell'Array ( ne basterebbe uno solo, che continene una serie di oggetti Point o se preferisci di variabili personalizzate "Punto"... ) mi sembra una buona idea, e non credo sia troppo pesante, poi dipende dalla mole dei dati per secondo che vuoi rappresentare...
Il metodo più "geniale" era proprio la proprietà AutoRedraw di VB6, che ( non chiedermi perchè ) hanno ben pensato di non implementare più in .NET.

Tempo fa avevo fatto qualcosa di simile in .NET, ma non c'erano linee curve o oblique, solo linee verticali/orizzontali, perciò avevo usato controlli ( pulsanti e/o picturebox con altezza o larghezza=1... ).
In questo modo il problema cancellazione non si presentava... ;)

banryu79
21-04-2008, 12:04
Bhe, al momento giusto potresti salvare l'area client o cmq il contesto in cui stai disegnando le linee come un'immagine in memoria che ripristini successivamente.

Così puoi continuare a disegnare le linee come fai adesso.

Capello82
21-04-2008, 14:17
Bhe, al momento giusto potresti salvare l'area client o cmq il contesto in cui stai disegnando le linee come un'immagine in memoria che ripristini successivamente.

Così puoi continuare a disegnare le linee come fai adesso.


Il fatto è che la grafica mi scompare anche nel caso in cui semplicemente porto in secondo piano l'applicazione (dietro un'altra finestra); quando la riporto in primo piano non c'è più nulla di quanto disegnato fino a quel momento.

Credo che farò con l'array :)

MarcoGG
22-04-2008, 08:23
Il fatto è che la grafica mi scompare anche nel caso in cui semplicemente porto in secondo piano l'applicazione (dietro un'altra finestra); quando la riporto in primo piano non c'è più nulla di quanto disegnato fino a quel momento.

Credo che farò con l'array :)


Quoto. L'unica è ridisegnare in continuazione tutto quanto.
Che poi, molto probabilmente, è proprio quello che faceva implicitamente il vecchio evento VB6 AutoRedraw...

Capello82
22-04-2008, 08:31
Quoto. L'unica è ridisegnare in continuazione tutto quanto.
Che poi, molto probabilmente, è proprio quello che faceva implicitamente il vecchio evento VB6 AutoRedraw...

Bene. Ho fatto con l'array e il problema l'ho risolto ^^

Ora volevo cercare qualche modo per creare un'apposita area di disegno su cui tracciare queste linee, senza rischiare di invadere il resto della form. Altra cosa che ho notato è che l'origine delle coordinate è l'angolo in alto a sinistra; c'è un modo per spostarlo in basso a sinistra e fare in modo che per valori positivi ci si sposti verso destro e verso l'alto?

Il controllo picturebox fa tutto questo o non c'entra nulla? :D

MarcoGG
22-04-2008, 08:56
Bene. Ho fatto con l'array e il problema l'ho risolto ^^

Ora volevo cercare qualche modo per creare un'apposita area di disegno su cui tracciare queste linee, senza rischiare di invadere il resto della form. Altra cosa che ho notato è che l'origine delle coordinate è l'angolo in alto a sinistra; c'è un modo per spostarlo in basso a sinistra e fare in modo che per valori positivi ci si sposti verso destro e verso l'alto?

Il controllo picturebox fa tutto questo o non c'entra nulla? :D


Sono curioso... :) Puoi postare il codice che esegue il disegno ?

Per l'area di disegno userei una PictureBox, o ancora meglio un controllo Panel, che oltretutto presenta barre di scorrimento se il contenuto andasse oltre le sue dimensioni ( nel mio caso erano controlli, non linee, perciò non garantisco che il Panel ti consenta lo scrolling in presenza di sola grafica... ).

Per il discorso Origine / Coordinate devi operare tu la "traslazione" mentre calcoli la Y di ogni punto ( le X restano invariate ), ossia :
>> nuovaY = Contenitore.Height - Y
;)

Capello82
22-04-2008, 09:39
Ecco i frammenti di codice per la grafica:

Dim formGraphics As System.Drawing.Graphics
Dim myPen As New System.Drawing.Pen(System.Drawing.Color.LawnGreen, 2)
Dim rect As System.Drawing.Rectangle
Dim rectf As System.Drawing.RectangleF

....

Sub InviaDati()

....

If penpos = True Then

x1 = XPREV / 2
y1 = YPREV / 2
x2 = XCOORD / 2
y2 = YCOORD / 2
X(z) = XCOORD / 2
Y(z) = YCOORD / 2
formGraphics.DrawLine(myPen, x1, y1, x2, y2)
End If

If penpos = False Then
X(z) = -1
Y(z) = -1
End If

z = z + 1
....

End sub

....

Public Sub Paint1(ByVal sender As Object, ByVal e As System.Windows.Forms.PaintEventArgs) Handles MyBase.Paint
Dim t As Integer
rect.X = 5
rect.Y = 50
rect.Height = 200
rect.Width = 300
rectf.X = 5
rectf.Y = 50
rectf.Height = 200
rectf.Width = 300
formGraphics.DrawRectangle(Pens.Black, rect)
formGraphics.FillRectangle(Brushes.Green, rectf)


If graph_ack = True And z >= 2 Then

For t = 1 To z - 1
If (X(t - 1) > 0 And Y(t - 1) > 0) And (X(t) > 0 And Y(t) > 0) Then
formGraphics.DrawLine(myPen, X(t - 1), Y(t - 1), X(t), Y(t))

End If
Next
End If


End Sub

Ecco, spero di non essermi dimenticato nulla ^^ La sub "InviaDati" viene eseguita ripetutamente per calcolare e inviare coordinate al macchinario (in questo caso macchina CNC) e come vedi anche per disegnare passo passo l'immagine sullo schermo e per riempire gli array (X e Y maiuscoli). L'evento paint è quello di cui abbiamo parlato qui e contiene l'array per ridisegnare l'immagine ottenuta (anche in corso d'opera). La variabile "penpos" serve ad indicare se deve essere disegnata una determinata linea o meno; mi serve perché io comunque devo continuare a riempire l'array. Se non la usasi mi unirebbe anche punti che non voglio unire.

Per quanto riguarda la traslazione che dici tu, anche io ci avevo pensato; sia traslazione che scalatura. Però nel caso in cui l'immagine fosse più grande del dovuto volevo introdurre un sistema di zoom e mi chiedevo se fosse già implementato come controllo oppure bisogna scrivere un codice apposta ^^

MarcoGG
22-04-2008, 10:03
Per quanto riguarda la traslazione che dici tu, anche io ci avevo pensato; sia traslazione che scalatura. Però nel caso in cui l'immagine fosse più grande del dovuto volevo introdurre un sistema di zoom e mi chiedevo se fosse già implementato come controllo oppure bisogna scrivere un codice apposta ^^


Non mi risulta esistano proprietà di zoom in PictureBox, nè in Panel ( almeno fino al .NET 2003, che sto usando in questo periodo, per 2005 o 2008 dovrei vedere... ), comunque definire uno scaling personalizzato non è difficile :
>> ti basta stretchare Y, una cosa del tipo :

SE Y > Pannello.Height >>
Per ogni punto nell'array : Y = Y/2

E il controllo lo fai su ugni nuovo punto che stai per inserire nell'Array...

Capello82
22-04-2008, 10:08
Non mi risulta esistano proprietà di zoom in PictureBox, nè in Panel ( almeno fino al .NET 2003, che sto usando in questo periodo, per 2005 o 2008 dovrei vedere... ), comunque definire uno scaling personalizzato non è difficile :
>> ti basta stretchare Y, una cosa del tipo :

SE Y > Pannello.Height >>
Per ogni punto nell'array : Y = Y/2

E il controllo lo fai su ugni nuovo punto che stai per inserire nell'Array...

Già.. ora mi ingegno un po' per fare una cosa precisa ;)

Per quanto riguarda il "panel" non riesco a vedere le scroll bars O.o non compaiono nelle proprietà...

MarcoGG
22-04-2008, 10:18
Per quanto riguarda il "panel" non riesco a vedere le scroll bars O.o non compaiono nelle proprietà...


Panel / proprietà AutoScroll = True
;)
Funziona sicuramente se un controllo figlio ( pulsante ecc... ) esce dalla dimensione del Panel padre, con la grafica Gdi, come ti ho già detto, è da provare...

Capello82
22-04-2008, 20:22
Panel / proprietà AutoScroll = True
;)
Funziona sicuramente se un controllo figlio ( pulsante ecc... ) esce dalla dimensione del Panel padre, con la grafica Gdi, come ti ho già detto, è da provare...


Ah ok grazie :D io cercavo la proprietà "Scrollbars" come per i controlli di testo. In caso ti faccio sapere ;) Per ora mi stavo arrangiando con un autosize del disegno all'interno della picturebox :)

MarcoGG
23-04-2008, 08:09
Ah ok grazie :D io cercavo la proprietà "Scrollbars" come per i controlli di testo. In caso ti faccio sapere ;) Per ora mi stavo arrangiando con un autosize del disegno all'interno della picturebox :)


Beh, eseguire un semplice autosize senza scrollbars è più semplice da implementare, però ti può portare, dopo qualche tempo, ad avere un grafico troppo scalato e illeggibile...

Capello82
23-04-2008, 09:24
Beh, eseguire un semplice autosize senza scrollbars è più semplice da implementare, però ti può portare, dopo qualche tempo, ad avere un grafico troppo scalato e illeggibile...

Purtroppo il controllo panel non scrolla la grafica :(

MarcoGG
23-04-2008, 13:07
Purtroppo il controllo panel non scrolla la grafica :(


Infatti, come mi aspettavo, ma è un problema al quale si può rimediare.
Potresti inserire una piccola picturebox, dello stesso colore di sfondo del Panel e priva di bordo, e spostarla verso destra impostando il suo .Left via via uguale alla coordinata x del'ultimo punto inserito... ;)

Capello82
23-04-2008, 14:22
Quindi praticamente sposterei la picturebox1 intera usando gli scroll del panel?

Capello82
23-04-2008, 16:25
Bene ^^ Ho fatto in modo di far scrollare la picturebox nel panel. Ora ho un evento scroll sulle barre del panel che mi forza l'evento paint con un refresh ;)

Thx per l'idea :)

MarcoGG
23-04-2008, 21:52
Bene ^^ Ho fatto in modo di far scrollare la picturebox nel panel. Ora ho un evento scroll sulle barre del panel che mi forza l'evento paint con un refresh ;)

Thx per l'idea :)


Eh Già. :cool: