Introduzione In questo terzo gradino della nostra scalata verso WPF, proseguiamo da dove avevamo terminato la scorsa puntata, ovvero avevamo colorato la Window del nostro programma di Rosso porpora usando un pennello (Brush) a noi virtualmente sconosciuto, proveremo ad usarne le versioni più semplici in questo articolo. Prima di parlare di brushes però ho indicato un litigio e quindi vado a spiegare le motivazioni, il suo svolgimento e le conclusioni. Un match fra un'Icona tradizionale ed un'Icona WPF Una delle cose che faccio sempre nei miei programmi, anche quelli didattici, è quella di cambiare l'icona della form da quella standard di Visual Studio ad un'icona personalizzata. Nelle Windows Forms, questo si ottiene inserendo l'icona desiderata nelle Resources ed assegnandola ad ogni form. In WPF quindi pretendevo di fare lo stesso, solo che... non è proprio così che funziona. Infatti, se in una Window WPF provate a scrivere il seguente codice: this.Icon = Properties.Resources.btn_001; Me.Icon = My.Resources.btn_001 Ottenete il seguente errore di compilazione: Error 1 Cannot implicitly convert type 'System.Drawing.Bitmap' to 'System.Windows.Media.ImageSource' Ovviamente la domanda è, ma come si converte un Bitmap in una ImageSource? Tentando un cast nulla di fatto, ma cercando su internet ho trovato varie soluzioni che andrò a mostrare: Generare l'ImageSource da un Bitmap memorizzato nel file Resources.resx public static BitmapSource BitmapSourceFromResource(System.Drawing.Image pImg)
{
System.IO.MemoryStream memStream = new System.IO.MemoryStream();
//Salva l'immagine come Png sul MemoryStream
pImg.Save(memStream, System.Drawing.Imaging.ImageFormat.Png);
//Genera un decoder per il tipo di stream
System.Windows.Media.Imaging.PngBitmapDecoder decoder =
new System.Windows.Media.Imaging.PngBitmapDecoder(memStream,
System.Windows.Media.Imaging.BitmapCreateOptions.PreservePixelFormat,
System.Windows.Media.Imaging.BitmapCacheOption.Default);
//Torna il primo (e unico) frame generato dall'immagine via Decoder.return decoder.Frames[0];
} Public Shared Function BitmapSourceFromResource(ByVal pImg As _System.Drawing.Image) As BitmapSource
Dim memStream As New System.IO.MemoryStream()
'Salva l'immagine come Png sul MemoryStream
pImg.Save(memStream, System.Drawing.Imaging.ImageFormat.Png)
'Genera un decoder per il tipo di stream Dim decoder As New System.Windows.Media.Imaging.PngBitmapDecoder _
(memStream, System.Windows.Media.Imaging.BitmapCreateOptions.PreservePixelFormat, _
System.Windows.Media.Imaging.BitmapCacheOption.[Default])
'Torna il primo (e unico) frame generato dall'immagine via Decoder. Return decoder.Frames(0)
End FunctionQuesto è il metodo per la conversione di un immagine, in questo caso in formato Png, ma potrebbe essere un Jpg o un Gif o un Bmp ecc., esistono i decoder per i vari tipi di immagine. Come possiamo vedere non si tratta di un'operazione proprio semplice, infatti le operazioni eseguite sono le seguenti: - Salvare l'immagine su un memory stream;
- Generare un PngBitmapDecoder a partire dallo stream;
- Restituire il Frame 0 prodotto dal decoder;
Scomodiamo quindi la libreria Media, nonché i Codec per ottenere un'icona, perché mai dobbiamo complicarci la vita così? Generare l'ImageSource da un Icona memorizzata nel file Resources.resx public static BitmapSource BitmapSourceFromResource(System.Drawing.Icon pIco)
{
System.IO.MemoryStream iconStream = new System.IO.MemoryStream();
pIco.Save(iconStream);
return( IconBitmapDecoder.Create(iconStream, BitmapCreateOptions.PreservePixelFormat,
System.Windows.Media.Imaging.BitmapCacheOption.Default)).Frames[0];
} Public Shared Function BitmapSourceFromResource(ByVal pIco _
As System.Drawing.Icon) As BitmapSource
Dim iconStream As New System.IO.MemoryStream()
pIco.Save(iconStream)
Return (IconBitmapDecoder.Create(iconStream, _
BitmapCreateOptions.PreservePixelFormat, _
System.Windows.Media.Imaging.BitmapCacheOption.[Default])).Frames(0)
End FunctionQuesto metodo converte un'icona invece di convertire un bitmap. Sono 3 righe di codice, eppure su di esso ho perso 3 ore di tempo perché una volta generata l'icona, ricevevo un'eccezione quando la form veniva visualizzata, mi diceva che il formato dell'icona era errato. Non capivo per quale motivo, fino a che, per caso, ho usato un'icona diversa da quella che avevo utilizzato fino a quel momento ed ho scoperto che non c'era un errore nel codice ma era l'icona che era errata. Ho indagato sulle differenze fra l'icona che funzionava e quella che invece dava eccezione, ed ho scoperto che il problema è dovuto al fatto che in un raptus di ottimizzazione, quando ho generato il file .ico ho indicato al programma di utilizzare la compressione png per le icone in formato Windows Vista. Questa compressione non è gestita da parte di WPF o meglio, da parte delle Windows. La conversione, simile a quella per il bitmap precedente viene effettuata con le seguenti azioni: - Generare un Memory Stream;
- Salvare l'icona sul Memory stream;
- Generare un Frame dal Decoder esattamente come per la Bitmap.
Ma questi due metodi sono in realtà un ripiego, che ho cercato perché non riuscivo a comprendere come riuscire a generare un'immagine nel modo che sembrava più semplice. Generazione di Una ImageSource utilizzando una Risorsa Embedded ed un URI Infatti, il primo metodo per generare una bitmap che ho trovato guardando il costruttore della ImageSource, ovvero il BitmapFrame, richiedeva un solo parametro, di tipo URI. this.Icon = BitmapFrame.Create( Uri ); Se cercate su Wikipedia, troverete varie definizioni, la prima dice che le URI sono le Vergini del paradiso islamico, la seconda che URI era il dio della caccia dei Longobardi, probabilmente l'Herne dei Sassoni e il Pan dei Greci, gli Uri erano anche un tipo di Bovini estintisi nel primo Medioevo. Infine, L'immancabile acronimo URI: Unique Resource Identifier, Identificatore Univoco di Risorsa, comprensibile, ma che cos'è? Non bastava un semplice Path? Il nome della risorsa?  Che diamine, il Framework 2.0 ci aveva finalmente dato il Properties.Resources, o My.Resources e adesso mi cambiano tutto? Non bastasse ciò, quando sono andata a cercare di capire come è fatto un URI, mi sono imbattuta in una definizione: Pack URIs in Windows Presentation Foundation, che non ha fatto che aumentare la mia confusione mentale, la definizione era associata a questo esempio: pack://application:,,/ResourceFile.xaml, una cosa interessante, ma le spiegazioni trovate non riuscivano a farmi comprendere di cosa si trattasse, forse perché io non sto utilizzando dei files XAML ma sto scrivendo codice, quindi sono andata a cercare aiuto da Charles Petzold ovvero ho cercato URI sull'indice analitico del suo primo libro dedicato a WPF e per fortuna la sua spiegazione era un po' più comprensibile. Appurato che un'URI non è una delle Vergini del paradiso islamico, e non è neppure la divinità Longobarda, le Pack URI sono degli identificatori univoci che permettono di identificare una Risorsa agglomerata in un progetto ovvero una risorsa Embedded. Bisogna solo scoprire che cos'è una risorsa Embedded per un progetto WPF. Visto l'esempio che ho riportato qui sopra, pensavo fosse stato modificato il nome del file risorse, da un .resx a un .xaml, ma non è così. Con Risorsa Embedded, in un progetto WPF si intende un qualsiasi file, nel mio caso un file grafico di tipo JPG, PNG o ICO, che viene aggregato utilizzando Add>Existing File> ad un progetto e la cui property BuildAction è indicata come Resource. Questo fa in modo che il compilatore lo inserisca nell'exe o nella dll compilati e che siano accessibili tramite la famosa Pack URI. La Pack URI, è generata da un path simile ad un URL nella forma: pack://application:,,/NomeDelFile. Per inciso, se facciamo una sottocartella Images per inserire tutte le immagini, scriviamo pack://application:,,/Images/NomeDelFile. Probabilmente gli esperti di WPF sorrideranno per la mia scoperta, ma dal mio punto di vista, si tratta di un primo chiodo importante nella scalata alla parete nord del WPF. Scoperto come generare una risorsa embedded, e aggiunta la mia icona al progetto, il codice da scrivere diviene il seguente: this.Icon = BitmapFrame.Create(
new Uri("pack://application:,,/btn_dnw_001.ico")); Me.Icon = BitmapFrame.Create( _
New Uri("pack://application:,,/btn_dnw_001.ico"))Direi che torniamo alla ragionevolezza, anche se mi chiedo "Perchè quella cosa davanti al nome del file di risorsa?" non sarebbe stato più semplice usare solo il nome del file, ma al momento non mi metto fretta, sono certa che lo scopriremo e sarà un altro chiodo di sostegno per le nostre corde da arrampicata. Terminata questa prima parte estemporanea, passiamo al vero soggetto dell'articolo, ovvero i pennelli di WPF. Le classi di tipo Brush Pennello (Brush) è decisamente un nome riduttivo per queste classi, infatti per chi come me a scuola faceva i disegni con gli acquerelli, oppure con i carioca, il concetto di pennello è semplice, si tratta di un attrezzo che è in grado di spargere un colore su un foglio, in questo caso, i pennelli mono colore sono solo uno dei tipi di pennello che possono essere forniti dalla classe Brush, nella forma dei SolidBrush, ma già i pennelli monocolore non sono così banali, perché oltre al colore, hanno anche una trasparenza che pemette loro di stendere il colore in modo un pochino più complesso di quanto facessimo noi con gli acquerelli. Vediamo dove si trovano i pennelli nell'albero degli oggetti di WPF. Object DispatcherObject (abstract) DependencyObject Freezable (abstract) Animatable (abstract) Brush (abstract) GradientBrush (abstract) LinearGradientBrush RadialGradientBrush SolidColorBrush TileBrush(abstract) DrawingBrush ImageBrush VisualBrush Ho collegato le pagine di MSDN relative alle classi che è opportuno leggere se intendete lavorare con WPF, ci mostrano alcune delle cose che possono fare i nostri pennelli, e già guardando il DrawingBrush possiamo vedere come il pennello è in grado di tracciare delle forme, o ripetere delle forme per generare dei pattern utilizzando degli oggetti di tipo Drawing. L'ImageBrush è invece un pennello in grado di disegnare usando come base un immagine che può riprodurre o ripetere su una superficie. Il VisualBrush è invece in grado di utilizzare un oggetto Visual per riprodurlo, oppure disegnarlo. In questo caso il Visual è ancora immerso nella nebbia che avvolge la maggior parte del WPF, pertanto vi invito a leggerne la descrizione di MSDN per saperne di più. Per questo primo tour limitiamoci ai tre pennelli più semplici, Solidbrush, LinearGradientBrush, RadialGradientBrush. La classe WinSolid Il SolidBrush fornisce un pennello in grado di riempire una superficie con un colore, il Framework fornisce di default una collezione di SolidBrush che rappresentano i colori nominati come White, Red, Green eccetera. Pertanto se ricordate la nostra puntata precedente, abbiamo colorato il background di una form utilizzando la seguente riga di codice: this.Background = Brushes.White; Me.Background = Brushes White La classe statica Brushes ci permette di utilizzare direttamente i colori più comuni, se non erro sono quelli chiamati colori Web fra cui sono Lime, Plum, Chocolate ed altri colori esotici. Questa classe però ha a che vedere con i frigoriferi e vediamo come. Se riguardate l'albero dell'ereditarietà delle classi Brush, noterete come c'è una classe Freezable, ovvero Congelabile. Se proviamo a eseguire il seguente codice: SolidColorBrush myBrush = Brushes.White;
myBrush.Color = Color.FromRgb( 125, 0, 45); SolidColorBrush myBrush = Brushes.White
myBrush.Color = Color.FromRgb( 125, 0, 45) Otteniamo la seguente eccezione: Impossibile impostare una proprietà per l'oggetto '#FFFFFFFF' perché è in stato di sola lettura. Questo può darci un indizio relativo a cosa sono gli oggetti Freezable. L'appartenenza dell'oggetto ai discendenti della classe Freezable, permette loro di avere due stati, NonCongelato e Congelato, nel momento in cui un oggetto viene Congelato, non è più possibile modificarlo in alcun modo, mentre quando non è congelato, l'oggetto può essere modificato. Questo lo rende utile nei casi in cui sia necessario lavorare con più thread contemporaneamente, vi invito a leggere la Panoramica sugli oggetti congelabili di WPF che potrà probabilmente evitarci più di qualche mal di testa in merito. Siccome vogliamo provare a modificare il colore del nostro pennello, ci basta modificare la sua generazione per ottenere un pennello scongelato e quindi malleabile. La prima classe di test ci serve per vedere come è fatto un SolidColorBrush, questo tipo di pennello non ha molte caratteristiche per giocare salvo il proprio colore e la possibilità di divenire trasparente. Pertanto, anche per andare a curiosare in alcune delle cose che dovremo studiare nelle prossime puntate di questa serie, introdurremo alcuni oggetti che ci permetteranno di vedere alcune delle potenzialità di WPF. using System;
using System.Collections.Generic;
using System.Text;
using System.Windows;
using System.Windows.Media;
using System.ComponentModel;
using System.Windows.Media.Animation;
using System.Windows.Input;
using System.Windows.Media.Imaging; namespace Dnw
{
class WinSolid: 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.Animation
Imports System.Windows.Input
Imports System.Windows.Media.Imaging
Namespace Dnw
Class WinSolid
Inherits Window
Private Const ID_PennelloAnimato As String = "PennelloAnimato"End ClassEnd NamespaceLa struttura base della classe è simile alle Window che abbiamo già implementato, le cose nuove che appaiono sono alcuni Namespaces, System.Windows.Media e System.Window.Media.Imaging sono i namespace in cui abbiamo trovato come creare l'icona della form. System.Windows.Input fornisce gli oggetti per utilizzare gli eventi del mouse, System.Windows.Media.Animation contiene gli oggetti necessari ad animare le property del nostro pennello. Utilizziamo inoltre una costante per definire un Nome per il pennello animato. public WinSolid()
{
this.Icon = BitmapFrame.Create(new Uri("pack://application:,,/btn_dnw_001.ico"));
this.WindowStyle = WindowStyle.ThreeDBorderWindow;
this.ResizeMode = System.Windows.ResizeMode.NoResize;
//this.Background = Brushes.White;this.Background = new SolidColorBrush(Colors.White);
BuildAnAnimation();
} 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
'this.Background = Brushes.White; Me.Background = New SolidColorBrush(Colors.White)
BuildAnAnimation()
End SubNel costruttore, predisponiamo la finestra e dipingiamo di bianco il background, per poi chiamare il metodo che costruisce l'animazione. private void BuildAnAnimation()
{
// Creiamo un Namescope per usare gli storyboards
NameScope.SetNameScope(this, new NameScope());
SolidColorBrush myAnimatedBrush = new SolidColorBrush();
myAnimatedBrush.Color = Colors.Orange;
Background = myAnimatedBrush;
// Registriamo il pennello animato per poterlo// usare con gli storyboardthis.RegisterName(ID_PennelloAnimato, myAnimatedBrush);
// Animiamo il pennello per farlo cambiare colore al mouse enter
ColorAnimation mouseEnterColorAnimation = new ColorAnimation();
mouseEnterColorAnimation.To = Colors.Lime;
mouseEnterColorAnimation.Duration = TimeSpan.FromSeconds(3);
Storyboard.SetTargetName(mouseEnterColorAnimation, ID_PennelloAnimato);
Storyboard.SetTargetProperty(
mouseEnterColorAnimation, new PropertyPath(SolidColorBrush.ColorProperty));
Storyboard mouseEnterStoryboard = new Storyboard();
mouseEnterStoryboard.Children.Add(mouseEnterColorAnimation);
this.MouseEnter += delegate(object sender, MouseEventArgs e)
{
mouseEnterStoryboard.Begin(this);
};
//// Animiamo il pennello quando il mouse lascia il rettangolo
ColorAnimation mouseLeaveColorAnimation = new ColorAnimation();
mouseLeaveColorAnimation.To = Colors.Indigo;
mouseLeaveColorAnimation.Duration = TimeSpan.FromSeconds(3);
Storyboard.SetTargetName(mouseLeaveColorAnimation, ID_PennelloAnimato);
Storyboard.SetTargetProperty(
mouseLeaveColorAnimation, new PropertyPath(SolidColorBrush.ColorProperty));
Storyboard mouseLeaveStoryboard = new Storyboard();
mouseLeaveStoryboard.Children.Add(mouseLeaveColorAnimation);
this.MouseLeave += delegate(object sender, MouseEventArgs e)
{
mouseLeaveStoryboard.Begin(this);
};
//// Animiamo l'opacità del pennello quando premiamo il bottone sinistro//
DoubleAnimation opacityAnimation = new DoubleAnimation();
opacityAnimation.To = 0.0;
opacityAnimation.Duration = TimeSpan.FromSeconds(0.5);
opacityAnimation.AutoReverse = true;
Storyboard.SetTargetName(opacityAnimation, ID_PennelloAnimato);
Storyboard.SetTargetProperty(
opacityAnimation, new PropertyPath(SolidColorBrush.OpacityProperty));
Storyboard mouseLeftButtonDownStoryboard = new Storyboard();
mouseLeftButtonDownStoryboard.Children.Add(opacityAnimation);
this.MouseLeftButtonDown += delegate(object sender, MouseButtonEventArgs e)
{
mouseLeftButtonDownStoryboard.Begin(this);
};
} Private Sub BuildAnAnimation()
' Creiamo un Namescope per usare gli storyboards
NameScope.SetNameScope(Me, New NameScope())
Dim myAnimatedBrush As New SolidColorBrush()
myAnimatedBrush.Color = Colors.Orange
Background = myAnimatedBrush
' Registriamo il pennello animato per poterlo ' usare con gli storyboard Me.RegisterName(ID_PennelloAnimato, myAnimatedBrush)
' Animiamo il pennello per farlo cambiare colore al mouse enter Dim mouseEnterColorAnimation As New ColorAnimation()
mouseEnterColorAnimation.[To] = Colors.Lime
mouseEnterColorAnimation.Duration = TimeSpan.FromSeconds(3)
Storyboard.SetTargetName(mouseEnterColorAnimation, ID_PennelloAnimato)
Storyboard.SetTargetProperty(mouseEnterColorAnimation, _ New PropertyPath(SolidColorBrush.ColorProperty))
Dim mouseEnterStoryboard As New Storyboard()
mouseEnterStoryboard.Children.Add(mouseEnterColorAnimation)
AddHandler Me.MouseEnter, AddressOf ConvertedAnonymousMethod1
' ' Animiamo il pennello quando il mouse lascia il rettangolo Dim mouseLeaveColorAnimation As New ColorAnimation()
mouseLeaveColorAnimation.[To] = Colors.Indigo
mouseLeaveColorAnimation.Duration = TimeSpan.FromSeconds(3)
Storyboard.SetTargetName(mouseLeaveColorAnimation, ID_PennelloAnimato)
Storyboard.SetTargetProperty(mouseLeaveColorAnimation, _ New PropertyPath(SolidColorBrush.ColorProperty))
Dim mouseLeaveStoryboard As New Storyboard()
mouseLeaveStoryboard.Children.Add(mouseLeaveColorAnimation)
AddHandler Me.MouseLeave, AddressOf ConvertedAnonymousMethod1
' ' Animiamo l'opacità del pennello quando premiamo il bottone sinistro ' Dim opacityAnimation As New DoubleAnimation()
opacityAnimation.[To] = 0
opacityAnimation.Duration = TimeSpan.FromSeconds(0.5)
opacityAnimation.AutoReverse = True
Storyboard.SetTargetName(opacityAnimation, ID_PennelloAnimato)
Storyboard.SetTargetProperty(opacityAnimation, _ New PropertyPath(SolidColorBrush.OpacityProperty))
Dim mouseLeftButtonDownStoryboard As New Storyboard()
mouseLeftButtonDownStoryboard.Children.Add(opacityAnimation)
AddHandler Me.MouseLeftButtonDown, AddressOf ConvertedAnonymousMethod1
End SubQuesto metodo è il cuore di tutta la nostra classe, iniziamo generando un NameScope, la prima classe nuova, per sapere cos'è un Namescope, vi invito a leggere WPF Namescopes su MSDN, se (spero) ho compreso correttamente quanto letto, il Namescope è un oggetto che permette di rendere univoci i nomi di tutti gli oggetti all'interno di una Window o una Pagina XAML, ogni oggetto radice (root) dell'albero degli elementi di WPF da origine ad un Namescope, che permette di trovare oggetti al suo interno utilizzando un nome univoco e che controlla che non sia possibile generare due oggetti con lo stesso nome all'interno di uno stesso scopo (di qui il suo nome, Scopo dei nomi), sono certa che ci imbatteremo spesso nel Namescope in futuro. Infatti è collegato all'uso degli Storyboards, che servono per realizzare animazioni. Generato il Namescope, creiamo un pennello arancione, e gli assegnamo un Nome univoco all'interno del Namescope. Proseguiamo, generando una ColorAnimation, il secondo oggetto nuovo, color animation anima una Property di tipo color per passare da un colore ad un altro utilizzando l'interpolazione lineare per un periodo specificato. Un'Animazione, modifica il valore di una property nell'arco di un periodo di tempo. L'animazione può avere una forma minuta, come ad esempio spostare un oggetto di alcuni pixel oppure essere complessa, come ad esempio ingrandire un oggetto di 200 volte facendolo ruotare e cambiare colore. Per creare una animazione in WPF si associa una classe di tipo animazione ad una property di un oggetto. Alla color animation assegnamo un colore di arrivo, la Property To e una durata in secondi 3. A questo punto, entra in gioco un oggetto nuovo, l'oggetto Storyboard, un contenitore per una Timeline (linea temporale) che fornisce informazioni relative agli oggetti ed alle proprietà a cui sono destinate le sue animazioni figlie. Infatti, nel nostro codice indichiamo l'animazione che abbiamo generato ed il Nome assegnato al controllo nel Namescope utilizzando il metodo statico SetTargetName, indichiamo poi la property da animare utilizzando il metodo statico SetTargetProperty in cui utilizziamo un altro oggetto nuovo, PropertyPath, questa classe rappresenta il percorso (path) che descrive una proprietà come Path sottostante ad una property o ad un tipo di dato che la possiede, è utilizzato sia per il data binding, sia per le animazioni. Nel nostro caso, la property path indica all'animazione la property di quale classe è interessata dall'animazione. Per capirne di più vi invito a leggere la traduzione della Panoramica delle Proprietà subordinate (Dependency Properties) ed il loro funzionamento. La PropertyPath che generiamo, indica che utilizzeremo SolidColorBrush.ColorProperty come target dell'animazione. Per completare la generazione dell'animazione, generiamo una classe Storyboard, aggiungiamo alla sua Collection Children l'animazione che abbiamo generato, ed assegnamo all'evento MouseEnter della nostra form un event handler che in C# è generato tramite un metodo anonimo, che esegue il metodo Begin dell'animazione, in VB non so come consegnare all'eventhandler la variabile locale, presumo che sarà necessario implementare un Field a livello di classe per generare correttamente l'event handler. Abbiamo finito, però visto che ci siamo lanciati, generiamo altre due animazioni, una che cambia di nuovo colore al mouse leave, e un'ultima che Utilizza una nuova classe, una DoubleAnimation per animare l'opacità, il risultato di questa animazione non è perfetto perché vista la nostra ignoranza (non conosciamo nulla salvo la finestra) animiamo il Background della stessa e vedrete che l'effetto è un po' banale, però, ripeteremo l'esperimento con un oggetto più consono quanto prima. Se leggete che cos'è una DoubleAnimation, scoprirete che è un'animazione che modifica una property di tipo double (il tipo numerico floating point a doppia precisione) fra due valori utilizzando una interpolazione lineare per una durata specifica. Nel nostro esempio, molto semplice, facciamo passare l'opacità del pennello da 1.0 ( opaco, il valore di default per un SolidBrush) a 0.0 (trasparente). Due valori sono inoltre modificati per la nostra animazione, la property OpacityProperty e la property AutoReverse dell'animazione che viene messa a True, mentre il valore To dell'animazione è 0.0 anziché un colore. Per lanciare il nostro programma, impostiamo nella classe DnwApp del progetto (derivata da Application e identica a quella dei progetti nei precedenti articoli) il seguente codice: protected override void OnStartup(StartupEventArgs e)
{
base.OnStartup(e);
WinSolid win1 = new WinSolid();
win1.Title = "Test Solid brush C#";
win1.Height = 400;
win1.Width = 340;
win1.Show(); } Protected Overloads Overrides Sub OnStartup(ByVal e As StartupEventArgs)
MyBase.OnStartup(e) Dim win1 As New WinSolid()
win1.Title = "Test Solid brush C#"
win1.Height = 400
win1.Width = 340
win1.Show()End Sub Facendo partire il progetto otterremo una form che si apre con sfondo arancione, all'ingresso del mouse diviene verde e all'uscita del mouse diviene Indaco, al doppio click, dal colore che ha diviene nera (perché il suo background diviene trasparente) e poi torna al colore precedente. Non è un progetto che risolve un problema reale, però apre una serie di scatole che contengono delle cose interessanti per proseguire la nostra esplorazione di WPF. 
Form all'apertura dell'applicazione | 
Form al mouse enter | 
Form al mouse leave | 
Form dopo il click |
Mi spiece non poter mostrare un filmato ma ovviamente il codice del progetto sarà pubblicato e quindi potrete provarlo sul vostro PC. Vai alla seconda parte >> |