|
La classe WinLinearGradient
Questa seconda classe di test, esplora il secondo tipo di base fra i pennelli, che permette di costruire un gradiente lineare con due o più colori.
using System;
using System.Collections.Generic;
using System.Text;
using System.Windows;
using System.Windows.Media;
using System.ComponentModel;
using System.Windows.Media.Imaging;
using System.Windows.Media.Animation;
using System.Windows.Input;
namespace Dnw
{
class WinLinearGradient: Window
{
private const string ID_PennelloAnimato = "PennelloAnimato";
}
}
Imports System
Imports System.Collections.Generic
Imports System.Text
Imports System.Windows
Imports System.Windows.Media
Imports System.ComponentModel
Imports System.Windows.Media.Imaging
Imports System.Windows.Media.Animation
Imports System.Windows.Input
Namespace Dnw
Class WinLinearGradient
Inherits Window
Private Const ID_PennelloAnimato As String = "PennelloAnimato"
End Class
End Namespace
La struttura base è la stessa della precedente classe, al suo interno troverete anche i due metodi per generare l'icona della form da una risorsa di tipo bitmap oppure da un'icona di cui abbiamo parlato all'inizio di questo articolo.
public WinLinearGradient()
{
//this.Icon = BitmapSourceFromResource(Properties.Resources.btn_dnw_001);
this.Icon = BitmapSourceFromResource((System.Drawing.Image)Properties.Resources.btn_dnw_032_004);
this.WindowStyle = WindowStyle.ThreeDBorderWindow;
this.ResizeMode = System.Windows.ResizeMode.NoResize;
BuildAnimation();
}
nel costruttore, abbiamo il test delle due funzioni per l'icona, poi, visto che ci siamo lanciati verso le potenzialità delle animazioni di WPF proviamo ad animare anche questo tipo di pennello per dare una particolare forma al nostro Background usando ancora una volta un metodo BuildAnimation che illustreremo immediatamente.
private void BuildAnimation()
{
// Creiamo un Namescope per usare gli storyboards
NameScope.SetNameScope(this, new NameScope());
GradientStopCollection colorStop;
colorStop = new GradientStopCollection();
colorStop.Add(new GradientStop(Colors.DarkGreen, 0.0));
colorStop.Add(new GradientStop(Colors.White, 0.50));
colorStop.Add(new GradientStop(Colors.Red, 1.0));
LinearGradientBrush myAnimatedBrush = new LinearGradientBrush(colorStop,0);
Background = myAnimatedBrush;
// Registriamo il pennello animato per poterlo
// usare con gli storyboard
this.RegisterName(ID_PennelloAnimato, myAnimatedBrush);
//
// Animiamo lo startpoint e l'endpoint del LinearGradientBrush
//
PointAnimation gradientAni = new PointAnimation();
gradientAni.From = new Point(0,0);
gradientAni.To = new Point(1,1);
gradientAni.Duration = TimeSpan.FromSeconds(3);
gradientAni.AutoReverse = true;
Storyboard.SetTargetName(gradientAni, ID_PennelloAnimato);
Storyboard.SetTargetProperty(
gradientAni, new PropertyPath(LinearGradientBrush.EndPointProperty));
Storyboard mouseLeftButtonDownStoryboard = new Storyboard();
mouseLeftButtonDownStoryboard.Children.Add(gradientAni);
Storyboard.SetTargetName(gradientAni, ID_PennelloAnimato);
Storyboard.SetTargetProperty(
gradientAni, new PropertyPath(LinearGradientBrush.StartPointProperty));
mouseLeftButtonDownStoryboard.Children.Add(gradientAni);
this.MouseLeftButtonDown += delegate(object sender, MouseButtonEventArgs e)
{
mouseLeftButtonDownStoryboard.Begin(this);
};
}
Private Sub BuildAnimation()
' Creiamo un Namescope per usare gli storyboards
NameScope.SetNameScope(Me, New NameScope())
Dim colorStop As GradientStopCollection
colorStop = New GradientStopCollection()
colorStop.Add(New GradientStop(Colors.DarkGreen, 0))
colorStop.Add(New GradientStop(Colors.White, 0.5))
colorStop.Add(New GradientStop(Colors.Red, 1))
Dim myAnimatedBrush As New LinearGradientBrush(colorStop, 0)
Background = myAnimatedBrush
' Registriamo il pennello animato per poterlo
' usare con gli storyboard
Me.RegisterName(ID_PennelloAnimato, myAnimatedBrush)
'
' Animiamo lo startpoint e l'endpoint del LinearGradientBrush
'
Dim gradientAni As New PointAnimation()
gradientAni.From = New Point(0, 0)
gradientAni.[To] = New Point(1, 1)
gradientAni.Duration = TimeSpan.FromSeconds(3)
gradientAni.AutoReverse = True
Storyboard.SetTargetName(gradientAni, ID_PennelloAnimato)
Storyboard.SetTargetProperty(gradientAni, New PropertyPath(LinearGradientBrush.EndPointProperty))
Dim mouseLeftButtonDownStoryboard As New Storyboard()
mouseLeftButtonDownStoryboard.Children.Add(gradientAni)
Storyboard.SetTargetName(gradientAni, ID_PennelloAnimato)
Storyboard.SetTargetProperty(gradientAni, New PropertyPath(LinearGradientBrush.StartPointProperty))
mouseLeftButtonDownStoryboard.Children.Add(gradientAni)
AddHandler Me.MouseLeftButtonDown, AddressOf ConvertedAnonymousMethod1
End Sub
Un LinearGradientBrush, genera il proprio gradiente fra due linee parallele che ne segnano inizio e fine, la colorazione è perpendicolare alle linee di inizio e fine del gradiente. A differenza di un SolidColorBrush, ha bisogno di più colori per la sua costruzione, pertanto, il suo costruttore accetta una collezione di oggetti di tipo GradientStop, un GradientStop, descrive la posizione ed il colore di un punto di transizione in un gradiente. Ogni gradiente può contenere un numero arbitrario di Gradient Stop. Nel nostro caso, facciamo un gradiente patriottico, e costruiamo tre gradient stop, uno verde, che parte dal punto 0 del gradiente, uno bianco che parte dal punto 0.5 e uno rosso che parte, o meglio arriva ad 1.0. Quando facciamo partire la finestra, modificando il DnwApp con queste 5 righe di codice:
WinLinearGradient win2 = new WinLinearGradient();
win2.Title = "Test Linear gradient brush C#";
win2.Height = 400;
win2.Width = 340;
win2.Show();
Dim win2 As New WinLinearGradient()
win2.Title = "Test Linear gradient brush C#"
win2.Height = 400
win2.Width = 340
win2.Show()
Con questo codice, aggiungiamo una seconda finestra alla precedente che quando si apre è fatta così:
L'animazione che abbiamo inserito all'interno del metodo BuildAnimation di questa finestra è una variazione derivata dalle animazioni precedenti.
Il linear gradient brush, viene disegnato fra due punti lungo due righe parallele. la sua posizione rispetto all'asse della form può ruotare di 360° e per farlo utilizza i due punti di inizio e fine del nostro gradiente, che sono due Point, esattamente le proprietà StartPoint ed Endpoint, ciascuno dei 2 punti permette di percorrere le linee del rettangolo della form partendo da 0.0 angolo in alto a sinistra a 1.1 angolo in basso a destra.
La prima cosa che facciamo è creare un gradiente lineare patriottico, usando tre gradient stop verde, bianco e rosso posizionati a 0, 0.5, 1 lungo la linea ideale che compone il gradiente. Assegnamo il gradiente al Background e otteniamo la finestra che vediamo qui sopra.
Per animare il nostro gradiente, vogliamo provare a spostare le due proprietà StartPoint ed EndPoint, che non sono Ne un Color da animare con una Color Animation, ne un Double da animare con una DoubleAnimation, però a naso, proviamo a vedere se esiste una PointAnimation e guarda caso c'è anche lei. Questo tipo di animazione serve per spostare due valori (quindi X e Y del punto) da un Valore ad un altro usando le property From e To oppure tramite un terzo valore chiamato By, visto che abbiamo appena iniziato l'esplorazione di WPF ci limiteremo alle cose semplici, quindi iniziamo animando l'EndPoint in modo che From sia 0.0 e To 1.1, però, visto che stiamo facendo esperimenti aggiungiamo un'po' di brio e proviamo ad assegnare l'animazione anche allo StartPoint del gradiente.
Agganciamo l'animazione ad uno Storyboard generato per lavorare sul Mouse Click del bottone sinistro, e, visto che lo Storyboard possiede una collezione di Children animations, aggiungiamo a quella collezione entrambe le animazioni ed invochiamo il metodo Begin all'interno del metodo anonimo di gestione del Click del bottone sinistro del Mouse.
Facendo partire l'applicazione, otterremo due form, quella con il solid brush e la seconda, con il gradiente patriottico.
Se facciamo click sul Background della finestra con il gradiente, otteniamo una animazione che ruota il Gradiente e poi lo riporta indietro alla forma iniziale. Ho preso tre screenshot, per mostrare l'effetto:
Il gradiente da orizzontale diviene verticale e poi ritorna indietro, il Ritorna indietro è gestito dalla property Autoreverse dell'animazione che abbiamo attivato.
Credo si possa notare come anche con le nostre scarse conoscenze, WPF è piuttosto portato per la grafica e l'animazione.
La classe WinRadialGradientBrush
Questa terza finestra che disegnamo, utilizza il terzo fra i gradienti di base, il RadialGradientBrush, che ci permette di creare un gradiente circolare, anche in questo caso, utilizzando il GradientStop per definire il colore e la sua posizione.
using System;
using System.Collections.Generic;
using System.Text;
using System.Windows;
using System.Windows.Media;
using System.ComponentModel;
using System.Windows.Media.Imaging;
using System.Windows.Media.Animation;
using System.Windows.Input;
namespace Dnw
{
class WinRadialGradient: Window
{
private const string ID_PennelloAnimato = "PennelloAnimato";
}
}
Imports System
Imports System.Collections.Generic
Imports System.Text
Imports System.Windows
Imports System.Windows.Media
Imports System.ComponentModel
Imports System.Windows.Media.Imaging
Imports System.Windows.Media.Animation
Imports System.Windows.Input
Namespace Dnw
Class WinRadialGradient
Private Const ID_PennelloAnimato As String = "PennelloAnimato"
End Class
End Namespace
Anche la struttura di questa terza classe è identica alle precedenti.
public WinRadialGradient()
{
this.Icon = BitmapFrame.Create(new Uri("pack://application:,,/btn_dnw_001.ico"));
this.WindowStyle = WindowStyle.ThreeDBorderWindow;
this.ResizeMode = System.Windows.ResizeMode.NoResize;
BuildAnimation();
}
Public Sub New()
Me.Icon = BitmapFrame.Create(New Uri("pack://application:,,/btn_dnw_001.ico"))
Me.WindowStyle = WindowStyle.ThreeDBorderWindow
Me.ResizeMode = System.Windows.ResizeMode.NoResize
BuildAnimation()
End Sub
Anche qui, nel costruttore configuriamo la finestra e poi chiamiamo la costruzione dell'animazione.
///
/// Costruisce l'animazione...
///
private void BuildAnimation()
{
// Creiamo un Namescope per usare gli storyboards
NameScope.SetNameScope(this, new NameScope());
///
/// Composizione del gradiente
///
GradientStopCollection colorStop;
colorStop = new GradientStopCollection();
colorStop.Add(new GradientStop(Colors.Purple, 0.0));
colorStop.Add(new GradientStop(Colors.Red, 0.17));
colorStop.Add(new GradientStop(Colors.Orange, 0.33));
colorStop.Add(new GradientStop(Colors.Yellow, 0.5));
colorStop.Add(new GradientStop(Colors.Green, 0.67));
colorStop.Add(new GradientStop(Colors.Blue, 0.84));
colorStop.Add(new GradientStop(Colors.Indigo, 1.0));
RadialGradientBrush myAnimatedBrush = new RadialGradientBrush(colorStop);
Background = myAnimatedBrush;
// Registriamo il pennello animato per poterlo
// usare con gli storyboard
this.RegisterName(ID_PennelloAnimato, myAnimatedBrush);
//
// Animiamo lo startpoint e l'endpoint del LinearGradientBrush
//
DoubleAnimation gradientAni = new DoubleAnimation();
gradientAni.From = -1.0;
gradientAni.To = 1.0;
gradientAni.Duration = TimeSpan.FromSeconds(3);
gradientAni.AutoReverse = true;
Storyboard.SetTargetName(gradientAni, ID_PennelloAnimato);
Storyboard.SetTargetProperty(
gradientAni, new PropertyPath(RadialGradientBrush.RadiusXProperty));
Storyboard mouseLeftButtonDownStoryboard = new Storyboard();
mouseLeftButtonDownStoryboard.Children.Add(gradientAni);
this.MouseLeftButtonDown += delegate(object sender, MouseButtonEventArgs e)
{
mouseLeftButtonDownStoryboard.Begin(this);
};
}
Private Sub BuildAnimation()
'''
''' Costruisce l'animazione...
'''
' Creiamo un Namescope per usare gli storyboards
NameScope.SetNameScope(Me, New NameScope())
'''
''' Composizione del gradiente
'''
Dim colorStop As GradientStopCollection
colorStop = New GradientStopCollection()
colorStop.Add(New GradientStop(Colors.Purple, 0))
colorStop.Add(New GradientStop(Colors.Red, 0.17))
colorStop.Add(New GradientStop(Colors.Orange, 0.33))
colorStop.Add(New GradientStop(Colors.Yellow, 0.5))
colorStop.Add(New GradientStop(Colors.Green, 0.67))
colorStop.Add(New GradientStop(Colors.Blue, 0.84))
colorStop.Add(New GradientStop(Colors.Indigo, 1))
Dim myAnimatedBrush As New RadialGradientBrush(colorStop)
Background = myAnimatedBrush
' Registriamo il pennello animato per poterlo
' usare con gli storyboard
Me.RegisterName(ID_PennelloAnimato, myAnimatedBrush)
'
' Animiamo lo startpoint e l'endpoint del LinearGradientBrush
'
Dim gradientAni As New DoubleAnimation()
gradientAni.From = -1
gradientAni.[To] = 1
gradientAni.Duration = TimeSpan.FromSeconds(3)
gradientAni.AutoReverse = True
Storyboard.SetTargetName(gradientAni, ID_PennelloAnimato)
Storyboard.SetTargetProperty(gradientAni, New PropertyPath(RadialGradientBrush.RadiusXProperty))
Dim mouseLeftButtonDownStoryboard As New Storyboard()
mouseLeftButtonDownStoryboard.Children.Add(gradientAni)
AddHandler Me.MouseLeftButtonDown, AddressOf ConvertedAnonymousMethod1
End Sub
Per animare questa finestra, Creiamo il NameScope, poi generiamo il RadialGradientBrush e stavolta utilizziamo i sette colori dell'arcobaleno distribuiti lungo la circonferenza utilizzando i GradientStop. La finestra iniziale che otterremo sarà la seguente:

Per l'animazione, scegliamo di modificare la property RadiusX del gradiente, ovvero il raggio orizzontale del cerchio, questo valore è un Double (floating point a doppia precisione) pertanto usiamo una DoubleAnimation, anche in questo caso, con autoreverse, andiamo da -1 ad 1 come valori assunti dalla property e osserviamo quello che accade.
Per quanto ancora piuttosto grezzo, l'effetto che otterremo sarà far sembrare che il cerchio ruoti su se stesso sull'asse verticale in un senso e poi tornando indietro.
Conclusioni
In questo articolo abbiamo scoperto le nuove resources embedded, come convertire un icona o una bitmap in un immagine WPF, come iniziare a giocare con le animazioni, magari per poi ottenere qualche effetto simpatico sulle form WPF che svilupperemo, e ovviamente abbiamo aggiunto un paio di chiodi alla nostra scalata verso la vetta.
Il progetto utilizzato per il codice dimostrativo può essere scaricato in Area Risorse.
Per qualsiasi Feedback, Ulteriore domanda, chiarimento, oppure se trovate qualche errore usate direttamente il form dei commenti in calce a questo articolo oppure scrivete all'autrice .
Nota per gli sviluppatori VB
Visual basic, al contrario di C# non possiede la funzionalità cosiddetta dei metodi anonimi, che permette, come potete osservare all'interno del codice, di definire un event handler al volo e di utilizzare al suo interno le variabili locali del metodo in cui è definito.
Per ovviare a questo problema, anche se nel codice di questo articolo non si vede, sono stati generati degli event handler normali e gli oggetti Storyboard sono stati spostati a livello di classe, in modo che gli event handler li possano vedere.
|