Passa ai contenuti principali

Appunti di WPF – Quinta Puntata – XAML, proprietà ed eventi

Prendiamo in esame come vengono impostate le proprietà a livello di XAML in una finestra WPF.

Prendiamo in esame la semplice finestra:

  1. <Window x:Class="SimpleWindow"
  2.     xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
  3.     xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
  4.     Title="Simple Application" Height="350" Width="525">
  5.     <Grid Background="yellow">
  6.         <Grid.RowDefinitions>
  7.             <RowDefinition Height="*" />
  8.             <RowDefinition Height="*" />
  9.             <RowDefinition Height="*" />
  10.         </Grid.RowDefinitions>
  11.         <TextBox x:Name="txtNome" Grid.Row="0" Margin="10">asaasa</TextBox>
  12.         <Button x:Name="btnInvia" Grid.Row="1" Margin="10">Premi</Button>
  13.         <TextBlock x:Name="txtMessaggio" Grid.Row="2" Margin="10" Background="LightGray" ></TextBlock>
  14.     </Grid>
  15. </Window>

La finestra è composta da una griglia di tre righe al cui interno troviamo, rispettivamente un TextBox, un Button e un TextBlock.

Vedremo in dettaglio il funzionamento dei controlli in un tutorial successivo, per ora ci basta sapere che il TextBox e il Button hanno la funzione già nota agli sviluppatori Windows Forms, mentre il controllo TextBlock è una sorta di Label.

La seguente figura mostra come si presenta l’interfaccia:

WPF_05_XAML_ProprietaEventi_Fig1

Analizziamo, quindi, come vengono definite le proprietà dei vari controlli e della finestra all’interno del file XAML.

Possiamo suddividere le proprietà presenti nel codice in tre differenti tipologie che vedremo in dettaglio.

Alcune proprietà vengono impostate utilizzando degli attributi dei tag XML che rappresentano i controlli. Un esempio di questo tipo di proprietà è il background della griglia o del TextBlock:

  1.  
  2. <Grid Background="yellow">
  3. <TextBlock x:Name="txtMessaggio" Background="LightGray" ></TextBlock>
  4.     

Alle spalle di questo semplice modo di definire una proprietà di un controllo WPF c’è la necessità, da parte del framework, di convertire la stringa del valore della proprietà nell’effettivo oggetto della proprietà stessa. Ad esempio nell’impostare lo sfondo della griglia, diciamo che tale sfondo (di tipo Brush) è “Yellow” ma “Yellow” è una stringa e il framework deve sapere come “convertire” la stringa nell’oggetto Brush. Per risolvere questo problema WPF utilizza i Type Converters (le cui basi sono presenti nel framework .NET fin dalla versione 1.0).

Un Type Converter mette a disposizione dei metodi che consentono di convertire un oggetto in un altro e viceversa. In questo caso, essendo i valori delle proprietà indicati con delle stringhe, i metodi trasformano la stringa in un opportuni oggetti.

Il parser XAML di WPF, quando deve impostare una proprietà di un controllo, esegue i seguenti passi:

1. Esamina la dichiarazione della proprietà per verificare se è presente l’attributo TypeConverter che indica, se presente, quale classe si occupa della conversione. Se tale attributo è presente, il framework utilizza l’opportuno TypeConverter per effettuare la conversione ed assegnare la proprietà;

2. Se la proprietà non ha l’attributo TypeConverter, il framework verifica l’eventuale attributo TypeConverter presente nella definizione della classe tipo della proprietà. Se presente, il framework utilizza la classe definita in questo attributo per la conversione e la relativa assegnazione;

3. Se non sono verificati i primi due passi, si ottiene un errore.

Ad esempio, se utilizziamo Reflector per capire come è definita la proprietà Background dell’oggetto TextBlock otteniamo:

  1. Public Property Background As Brush
  2.     Get
  3.         Return DirectCast(MyBase.GetValue(TextBlock.BackgroundProperty), Brush)
  4.     End Get
  5.     Set(ByVal value As Brush)
  6.         MyBase.SetValue(TextBlock.BackgroundProperty, value)
  7.     End Set
  8. End Property

La proprietà non è decorata con il TypeConverterAttribute (passo 1) e si procede alla verifica della classe Brush, la cui definizione è la seguente:

  1. <Localizability(LocalizationCategory.None,
  2. Readability:=Readability.Unreadable),
  3. TypeConverter(GetType(BrushConverter)), ValueSerializer(GetType(BrushValueSerializer))> _
  4. Public MustInherit Class Brush
  5.     Inherits Animatable
  6.     Implements IFormattable, IResource
  7.  
  8. .
  9.  
  10. .
  11.  
  12. End Class

La classe ha l’attributo TypeConverter che indica quale classe utilizzare per convertire i valori delle proprietà (BrushConverter in questo caso).

Definire le proprietà utilizzando gli attributi dei tag è banale quando si tratta di valori semplici ma questo meccanismo non è più utilizzabile nel momento in cui siamo di fronte ad attributi complessi.

Osserviamo, ad esempio, la definizione delle righe della griglia contenitore:

  1. <Grid Background="yellow">
  2.     <Grid.RowDefinitions>
  3.         <RowDefinition Height="*" />
  4.         <RowDefinition Height="*" />
  5.         <RowDefinition Height="*" />
  6.     </Grid.RowDefinitions>
  7.     .
  8.     .
  9.     .
  10. </Grid>

In questo caso la collezione delle righe (RowDefinitions) è composta da tanti oggetti RowDefinition, si tratta in sostanza di un oggetto complesso non rappresentabile facilmente con una stringa.

Per l’assegnazione di proprietà complesse si sfrutta il fatto che, trattandosi XAML di un file XML, possiamo innestare tag XML all’interno di altri tag. La convenzione utilizzata per le proprietà complesse è quella di inserire un tag composto dal nome del tag di cui si sta impostando la proprietà seguito da un “.” E, infine, dal nome della proprietà. All’interno di questo tag definiamo l’oggetto o gli oggetti costituenti il valore della proprietà.

Tanto per chiarire ulteriormente la situazione, supponiamo di voler cambiare lo sfondo del TextBlock e farlo diventare un gradiente con i colori bianco e giallo. In questo caso scriveremo:

  1. <TextBlock x:Name="txtMessaggio" Grid.Row="2" Margin="10">
  2.     <TextBlock.Background>
  3.         <LinearGradientBrush>
  4.             <LinearGradientBrush.GradientStops>
  5.                 <GradientStop Color="white" Offset="0"></GradientStop>
  6.                 <GradientStop Color="Yellow" Offset="0.5"></GradientStop>
  7.                 <GradientStop Color="white" Offset="1"></GradientStop>
  8.             </LinearGradientBrush.GradientStops>
  9.         </LinearGradientBrush>
  10.     </TextBlock.Background>
  11. </TextBlock>

Questa volta la proprietà Background dell’oggetto TextBlock è un oggetto LinearGradientBrush la cui proprietà GradientStops è, a sua volta, composta da 3 oggetti di tipo GradientStop. Graficamente otteniamo:

WPF_05_XAML_ProprietaEventi_Fig2

Grazie a questo modo di impostare le proprietà, possiamo ottenere delle composizioni di oggetti anche complesse mantenendo il file XAML abbastanza comprensibile.

Ultima categoria di proprietà che possiamo trovare all’interno di un file XAML è quella definita come Attached Property. Per capire di cosa si tratta, osserviamo la definizione del bottone della precedente interfaccia grafica:

  1. <Button x:Name="btnInvia" Grid.Row="1"
  2.         Margin="10">Premi</Button>

Tra le proprietà di questo oggetto troviamo l’assegnazione Grid.Row=”1”. La proprietà Grid.Row non esiste nella classe Button, e, infati, la sintassi con il “.” sta ad indicare che tale proprietà non è dell’oggetto a cui la imposta ma è legata contenitore dello stesso gerarchicamente superiore.

In pratica, ogni controllo ha le sue proprietà e, quando viene posizionato all’interno di un contenitore acquisisce altre proprietàm del contenitore stesso.

Le attached properties non sono effettivamente delle proprietà come quelle usuali ma si traducono nella chiamata di opportuni metodi. Il parser dello XAML, quando incontra una attached property, richiama il metodo statico SetNomeProprietà() della classe contenitore. Nell’esempio precedente, quando il parser XAML incontra l’assegnazione Grid.Row=”1”, effettua la chiamata al metodo statico Grid.SetRow(). Se osserviamo (tramite Reflector) come è realizzato il metodo statico di cui sopra, otteniamo:

  1. Public Shared Sub SetRow(ByVal element As UIElement, ByVal value As Integer)
  2.     If (element Is Nothing) Then
  3.         Throw New ArgumentNullException("element")
  4.     End If
  5.     element.SetValue(Grid.RowProperty, value)
  6. End Sub

Quindi, effettivamente, il valore “1” non viene memorizzato all’interno della griglia ma del bottone (in questo caso il parametro Element è il nostro Button) tramite il metodo SetValue() (che, in realtà, è un metodo della classe DependencyObject da cui UIElement indirettamente deriva).

Utilizzando le attached properties ci garantiamo la possibilità di introdurre, in futuro, nuove proprietà in nuovi contenitori senza dover toccare i vecchi controlli.

Gli attributi dei tag XAML possono essere utilizzati per definire eventuali gestori di eventi dei controlli.

Supponiamo di voler definire il gestore di evento per il click del pulsante inserito nella finestra di prova vista in precedenza, scriveremo:

  1.  
  2. <Button x:Name="btnInvia" Grid.Row="1"
  3.         Margin="10" Click="btnInvia_Click">Premi</Button>

Per completare il processo dovremmo, ovviamente, definire il gestore di evento nel code-behind:

  1. Private Sub btnInvia_Click(ByVal sender As System.Object, ByVal e As System.Windows.RoutedEventArgs)
  2.  
  3. End Sub

In maniera analoga a quanto avviene nelle Windows Form, quando l’utente effettuerà un click sul bottone, verrà invocato il metodo btnInvia_Click. Osserviamo che, a differenza delle Windows Form, il secondo argomento del gestore di evento è di tipo RoutedEventArgs. L’event routing è il nuovo modello di gestione degli eventi che vedremo in un successivo tutorial.

A differenza delle Windows Form, non è necessario assegnare un nome ad un controllo per definire il gestore di evento tramite l’attributo.

Possiamo definire il gestore di evento di un controllo anche nella maniera canonica con la clausola Handles (come nelle Windows Form). In questo caso il controllo deve necessariamente avere un nome e non serve definire l’attributo nel tag XAML:

  1. Private Sub btnInvia_Click(ByVal sender As System.Object,
  2.                            ByVal e As System.Windows.RoutedEventArgs) Handles btnInvia.Click
  3.  
  4. End Sub

Scarica la versione PDF dell'articolo. Scarica la versione Amazon Kindle dell'articolo.

Commenti

Post popolari in questo blog

MVP Reconnect …… ovvero quando entri nella “famigghia” resti sempre nella “famigghia”!!!

Ma di che “famigghia” stiamo parlando!!!!

Fermi tutti, non si tratta di robe strane o sette segrete o affari malavitosi….stiamo parlando della grande famiglia dei Microsoft MVP.

Per chi non sapesse cosa sono i Microsoft MVP, vi consiglio di fare un giro sul sito ufficiale del programma (link), ma, volendolo spiegare in pochisime parole, si tratta di un riconoscimento che Microsoft da a persone che si distinguono per il loro impegno, aiutando gli altri ad ottenere il massimo grazie alle tecnologie Microsoft. Si tratta di persone, non dipendenti Microsoft, che mettono la loro passione, il loro tempo, la loro buona volontà per la divulgazione e la condivisione della conoscenza. Non necessariamente (come qualcuno erroneamente sostiene, evidentemente non conoscendo le basi del programma) si tratta di professionisti nel termine letterale del termine ma si tratta comunque di un gruppo di persone che sacrifica un pò del suo tempo (e, a volte, vi assicuro neanche pò!!!) per la sua passione.

Pe…

Template di progetto per sviluppare applicazioni WPF con Intel® RealSense™

E’ disponibile, nella gallery di Visual Studio, la prima versione del mio template di progetto per applicazioni WPF scritte in C# che permette di realizzare applicazioni con l’SDK di Intel® RealSense™.Il template si può scaricare direttamente all’interno Visual Studio utilizzando il tool “Extensions and Updates”oppure all’indirizzo https://visualstudiogallery.msdn.microsoft.com/1c36ecfd-8c00-4aee-b20c-a1726ab6424dIl template esegue le seguenti operazioni per voi:Aggiunge la reference all’assembly libpxcclr.cs.dll (nelle due distinte versioni per x86 e x64);Aggiunge lo script di post build per copiare la libreria libpxccpp2c.dll dalla cartella dell’SDK alla cartella bin del vostro progetto.Una volta creato il progetto dovete rimuovere la configurazione di compilazione AnyCPU (che non ha più senso) dalla vostra solution e sarete pronti per sviluppare con Intel® RealSense™.Ovviamente dovete installare l’SDK che potete scaricare all’indirizzo https://software.intel.com/en-us/intel-realsen…

Nuova versione del Band SDK

E’ di ieri l’annuncio del rilascio della nuova versione dell’SDK per il Microsoft Band.
Si tratta della versione 1.3.10417 (la precedente e, prima della serie, era la 1.3.10219 preview).
Maggiori informazioni, download dell’SDK per le tre piattaforme Windows Phone, iOS e Android all’indirizzo http://developer.microsoftband.com/.
Allo stesso indirizzo potrete trovare anche la documentazione.
Nei mesi scorsi mi sono gia’ occupato della precedente versione e questi sono i post che ne parlano:
Microsoft Band SDK Preview - First LookMicrosoft Band SDK Preview - ”Hello Band”Microsoft Band SDK Preview - Accesso ai sensoriMicrosoft Band SDK Preview - TileMicrosoft Band SDK Preview - NotificheMicrosoft Band SDK Preview - Personalizzazione
Gli argomenti trattati e il codice proposto dovrebbe, ad una prima lettura delle nuove funzionalita’ inserite, essere ancora valido e funzionante ma nei prossimi giorni prendero’ in esame tutti gli argomenti dei precedenti post e vedremo cosa cambia e cosa e’ …