|
Penne alternative
Nel capitolo precedente abbiamo impiegato penne predefinite. Ora vogliamo cercare qualcosa di più accattivante. Dopo aver inizializzato
un nuovo oggetto Pen, possiamo modificarne le proprietà:
- Brush : associando un pennello a questa proprietà è possibile riempire il tratto della penna mediante tale pennello con
sfumature, disegni, immagini, eccetera...
- CompoundArray : ammettiamo che l'oggetto Pen abbia una larghezza abbondante, ad esempio 10. Tutto il tratto viene riempito unfirmemente
con un colore (a meno che non abbiate modificato la proprietà Brush). Mediante questa proprietà possiamo decidere di rimpiazzare
il blocco cromatico con più strisce colorate di largezza definita. Ecco un esempio:
Dim b As New Bitmap(300, 300)
Dim g As Graphics = Graphics.FromImage(b)
Dim p As New Pen(Color.Black, 14)
p.CompoundArray = New Single() {0, 0.2, 0.4, 0.8, 0.9, 1}
g.SmoothingMode = Drawing2D.SmoothingMode.AntiAlias
g.Clear(Color.White)
g.DrawLine(p, 10, 10, 80, 100)
La penna lascia un tratto di larghezza 14, ma esso non è uniforma. La proprietà CompoundArray è di tipo array di Single.
In questo array vanno specificate delle posizioni percentuali, che indicano l'intervallarsi di strisce e spazi. Nel codice sopra, ho specificato
che da 0% a 20% della larghezza ci deve essere una linea, dal 20% al 40% uno spazio, dal 40% all'80% una linea, dall'80% al 90% uno spazio
e dal 90% al 100% una linea. Il tutto ha prodotto un risultato come quello in figura.
- DashStyle : permette di scegliere un differente tipo di tratteggio tra alcuni predefiniti. Eccone un esempio:

L'ultimo è stato creato modificando la proprietà DashPattern: è anch'essa un'array di Single, ma specifica la lunghezza
in pixel di un tratto e di uno spazio (in figura era 5, 5, 10, 5, ossia 5px di linea, 5 di spazio, 10 di linea e 10 di spazio). Si tratta
di pixel poiché il tratto si estende in lunghezza (quindi non si possono specificare valori relativi)
- DashCap : determina la forma degli estremi dei punti o delle linee che costituiscono una linea tratteggiata. Ecco un esempio, combinato
con la proprietà CompoundArray:

- StartCap / EndCap : specifica la forma geometrica posta all'inizio o alla fine della linea (di tutta la linea). Simile a DashCap, ma
con molte più varianti.
Pennelli alternativi
Oltre a SolidBrush, esistono alcuni altri pennelli con caratteristiche peculiari. Ad esempio:
- HatchBrush : riempie una superficie con una trama specificata.
Qui ci sono tutte le varianti;
- LinearGradientBrush : esegue una sfumature sull'area da riempire. Potete consultare alcuni esempi nella sezione Appunti;
- TextureBrush : riempie un'area specificata con un'immagine, eventualmente ripetendola e/o allungandola. Esempio
Esempio 1: Creare una userbar
Per mostrarvi qualche utilizzo pratico e non proprio basilare alla grafica in .NET voglio mostrare come sia possibile disegnare una userbar
simile a quelle create con Photoshop.
Per creare una userbar si seguono più o meno sempre questi passaggi:
- si prende uno sfondo di dimensioni 350x19 (formato standard), oppure si riempie quest'area con un
gradiente colorato e vi si applica sopra un'altra porzione di immagine;
- si applica sullo sfondo una trama omogenea formata da righe diagonali molto sottili e ravvicinate di colore scuro;
- si crea un effetto lucido sovrapponendo all'immagine così creata una semiellisse di colore bianco, con una trasparenza del 20-30%;
- per ultimare il lavoro, si pone una scritta sulla parte destra della barra, di solito usando il font
Visitor TT2 BRK.
Noi automatizzeremo tutto questo creando una classe apposita:
Namespace Userbars
Class Userbar
Private _BackgroundImage As Image
Private _BackgroundImagePosition As Int32
Private _Text As String
Private _Size As Size = New Size(350, 25)
Public Property Size() As Size
Get
Return _Size
End Get
Set(ByVal value As Size)
If value.Width > 0 And value.Height > 0 Then
_Size = value
Else
_Size = New Size(350, 25)
End If
End Set
End Property
Public Property BackgroundImage() As Image
Get
Return _BackgroundImage
End Get
Set(ByVal value As Image)
If value.Width > Me.Size.Width Or value.Height > Me.Size.Height Then
Throw New ArgumentOutOfRangeException()
Else
_BackgroundImage = value
End If
End Set
End Property
Public Property BackgroundImagePosition() As Int32
Get
Return _BackgroundImagePosition
End Get
Set(ByVal value As Int32)
If value > Me.Size.Width Then
Throw New ArgumentOutOfRangeException()
Else
_BackgroundImagePosition = value
End If
End Set
End Property
Public Property Text() As String
Get
Return _Text
End Get
Set(ByVal value As String)
_Text = value
End Set
End Property
Public Function Create() As Image
Dim Result As New Bitmap(Me.Size.Width + 1, Me.Size.Height + 1)
Dim G As Graphics = Graphics.FromImage(Result)
'Questi valori sono statici poiché costanti. Una
'volta inizializzati saranno sempre uguali e non
'verranno creati ulteriori oggetti ad ogni invocazione
'di Create()
'HatchBrush permette di riempire un'area con una trama
'prefissata. Noi vogliamo disegnare delle sottili righe
'scure, un motivo a cui corrisponde l'enumeratore
'indicato.
'Il colore usato è un Nero con opacità pari
'a 48: dato che il valore massimo è 255, si tratta di
'un nero al 19% di opacità. Il colore di sfondo è
'invece trasparente (come se non ci fosse).
Static LinesBrush As New HatchBrush(HatchStyle.DarkUpwardDiagonal, Color.FromArgb(48, Color.Black), Color.Transparent)
'Il font usato è Visitor TT2 BRK, grandezza 11pt.
'11 va molto bene per le userbar di dimensione
'standard. Se volete qualcosa di più generale,
'il font deve dipendere dall'altezza
Static FontUsed As New Font("Visitor TT2 BRK", 11, FontStyle.Regular)
'Questo pennello servirà per l'effetto lucido. Dato
'che si tratta di un SolidBrush, riempirà l'area con
'un colore omogeneo, in questo caso un bianco
'trasparente
Static TranspBrush As New SolidBrush(Color.FromArgb(70, Color.White))
'Imposta la modalità di smussamento delle linee. Dato
'che questa funzione viene usata solo quando l'utente
'la richiede (quindi non molte volte al secondo), e che
'il risultato deve essere il migliore possibile,
'utilizziamo un algoritmo ad alto rendimento e
'bassa velocità di rendering.
G.SmoothingMode = SmoothingMode.HighQuality
'Imposta la modalità di sovrapposizione. Poiché
'dobbiamo disegnare molte cose le una sopra alle altre, ci
'serve che i nuovi disegni non sovrascrivano quelli
'precedenti, ma vi si applichino sopra rispettandone
'le trasparenze
G.CompositingMode = CompositingMode.SourceOver
'Disegna l'immagine di sfondo
G.DrawImage(Me.BackgroundImage, Me.BackgroundImagePosition, 0)
'Disegna le righe su tutta l'immagine
G.FillRectangle(LinesBrush, 0, 0, Me.Size.Width, Me.Size.Height)
'Applica il velo di bianco trasparente. Notate che
'utilizzo delle coordinate negative per disegnare
'l'ellisse fuori dall'immagine. In questo modo,
'noi vedremo solo la parte di ellisse che rientra
'nell'area effettivamente esistente, ossia solo metà.
'Inoltre ho messo qualche coefficiente per aggiustare
'la larghezza e rendere migliore l'aspetto
G.FillEllipse(TranspBrush, -5, -Me.Size.Height + 3, Me.Size.Width + 10, CInt(Me.Size.Height * 1.5))
'Disegna il contorno della barra
G.DrawRectangle(Pens.Black, 0, 0, Me.Size.Width, Me.Size.Height)
'Calcola la dimensione del testo (in pixel)
Dim TextSize As SizeF = G.MeasureString(Me.Text, FontUsed)
'Quindi disegna la stringa Text in bianco, spostata
'rispetto al margine destro in modo che il testo non
'vada fuori dall'immagine.
G.DrawString(Me.Text, FontUsed, Brushes.White, Me.Size.Width - CInt(TextSize.Width) - 30, Me.Size.Height 3)
'Restituisce il risultato
Return Result
End Function
End Class
End Namespace
Ed ecco un esempio:

Come noterete, la proprietà BackgroundImagePosition ha senso solo se l'immagine è di larghezza inferiore alla userbar, ossia
nel caso in cui lo sfondo debba essere riempito con un gradiente uniforme (LinearGradientBrush). Non ho implementato questa funzionalità
nel codice, ma la lascio come esercizio. Potete usare come riferimento il mio articolo al riguardo nella sezione Appunti.
Esempio 2: Orologio analogico
Ecco uno stupidissimo codice di esempio per disegnare un orogoloio:
Class Form1
Private ClockImage As New Bitmap(230, 230)
Private G As Graphics = Graphics.FromImage(ClockImage)
Private Sub tmrClock_Tick(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles tmrClock.Tick
G.Clear(Me.BackColor)
Dim Time As Date = Date.Now
Static BorderPen As New Pen(Color.Black, 5)
Static HourPen As New Pen(Color.Red, 4) With {.EndCap = LineCap.Triangle}
Static MinutePen As New Pen(Color.Blue, 2) With {.EndCap = LineCap.Triangle}
Static SecondPen As New Pen(Color.Green, 1)
Dim C As Point = New Point(ClockImage.Width / 2, ClockImage.Height / 2)
Dim h, m, s As Single
h = Time.Hour + (Time.Minute / 60)
m = Time.Minute + (Time.Second / 60)
s = Time.Second
G.SmoothingMode = SmoothingMode.AntiAlias
G.FillEllipse(Brushes.White, 5, 5, ClockImage.Width - 10, ClockImage.Height - 10)
G.DrawEllipse(BorderPen, 5, 5, ClockImage.Width - 10, ClockImage.Height - 10)
For I As Int32 = 0 To 11
G.FillEllipse(Brushes.Black, _
C.X + CInt(ClockImage.Width / 2.3 * Math.Cos(Math.PI / 2 - I / 6 * Math.PI)) - 1, _
C.Y - CInt(ClockImage.Width / 2.3 * Math.Sin(Math.PI / 2 - I / 6 * Math.PI)) - 1, _
2, 2)
Next
G.DrawLine(HourPen, C.X, C.Y, _
C.X + CInt(ClockImage.Width / 4 * Math.Cos(Math.PI / 2 - h / 6 * Math.PI)), _
C.Y - CInt(ClockImage.Width / 4 * Math.Sin(Math.PI / 2 - h / 6 * Math.PI)))
G.DrawLine(MinutePen, C.X, C.Y, _
C.X + CInt(ClockImage.Width / 3 * Math.Cos(Math.PI / 2 - m / 30 * Math.PI)), _
C.Y - CInt(ClockImage.Width / 3 * Math.Sin(Math.PI / 2 - m / 30 * Math.PI)))
G.DrawLine(SecondPen, C.X, C.Y, _
C.X + CInt(ClockImage.Width / 2.2 * Math.Cos(Math.PI / 2 - s / 30 * Math.PI)), _
C.Y - CInt(ClockImage.Width / 2.2 * Math.Sin(Math.PI / 2 - s / 30 * Math.PI)))
imgClock.Image = ClockImage
End Sub
End Class
 |
|