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

VB.NET: Convertire un file DOC in RTF e PDF con office interop

In questo post vorrei proporvi del codice per poter convertire un file .doc in un file .rtf oppure .pdf utilizzando le API di interoperabilità di Office.Creeremo una classe, DocConverter, che esporrà le due funzionalità sopra citate.Cominciamo con il prevedere un attributo privato della classe che rappresenterà l’applicazione Word che utilizzeremo per la conversione. Creeremo l’istanza dell’attributo privato all’interno del costruttore della classe:PublicSubNew()
IfNot CreateWordApp() Then
ThrowNew ApplicationException("Assembly di interoperabilità con Office non trovato!")
EndIf
EndSub
Private _wordApp As Word.ApplicationClass
ProtectedFunction CreateWordApp() AsBoolean
Dim retval = True
Try
_wordApp = New Word.ApplicationClass()
_wordApp.Visible = False
Catch ex As System.Exception
_wordApp = Nothing
retval = False
EndTry
Return retval
EndFunction

La conversione del file doc sarà effettuata aprendo il file stesso ed eseguendo un’operazione di SaveAs:

Pr…

Cambiare la lingua di Visual Studio 2008

Oggi ho avuto qualche problema installando Windows Mobile 6 Professional SDK Refresh e Windows Mobile 6 Standard SDK Refresh.Scaricati i file di installazione e installati, ho provato a creare un progetto di tipo Windows Mobile 6.0 e mi sono beccato questo errore:Dopo qualche smanettamento abbiamo scoperto (e ringrazio il mitico Matteo per l’aiuto) che il mio Visual Studio 2008, pur essendo in Inglese (prova ne era il fatto che gli hotfix e la SP installata erano nella lingua di Albione) aveva come lingua impostata quella del sistema operativo (italiano).Ovviamente, non avrebbe mai potuto trovare la cartella 1040 (italiano) visto che l’installazione dell’SDK aveva supposto che la lingua del Visual Studio fosse Inglese (1033).La soluzione del problema è duplice:1) Duplicate la cartella 1033 presente nel percorso evidenziato dall’errore e la rinominate 10402) cambiate la lingua di Visual Studio.Per questa ultima eventualità basta andare nel menù Strumenti/Opzioni:e cambiare il linguaggi…

Tascam DR-05 registratore digitale per tutti

Diverso tempo fa ho acquistato il registratore digitale Tascam DR-05 e, ora, dopo diversi mesi di utilizzo, posso dire la mia a proposito.

Si tratta di un ottimo registratore digitale con microfono stereo che permette di ottenere registrazioni di ottima qualitaà anche in ambienti non perfetti dal punto di vista acustico.

Interessante la possibilità di utilizzare un cavalletto di piccole dimensioni come HAMA Mini treppiede o Mini Cavalletto Universale per tenere il microfono sollevato dal tavolo in fase di registrazione grazie allàattacco universale per macchina fotografica che il microfono ha nella parte inferiore.

Da segnalare anche il menu’ ben fatto e la possibilita’ di utilizzare una scheda SD esterna per memorizzare i file audio. Anche a livello di consumo batterie non e’ niente male. Consiglio l’uso delle alcaline non ricaricabili.

Il mio utilizzo e’ stato prettamente di registrazione di podcast vocali (no musica) ma le recensioni confermano che se la cava egregiamente con la mu…