Il layout è uno degli aspetti più importanti ed innovativi che possiamo trovare in WPF.
La disposizione del controlli in un’applicazione Windows Forms avviene tramite l’impostazione delle coordinate fisiche di posizionamento del controllo stesso all’interno della finestra o di un controllo contenitore.
Questo modo di disporre gli oggetti è troppo rigido e non consente quasi mai un ridimensionamento efficace dell’interfaccia grafica per adattarsi alle differenti risoluzioni grafiche disponibili sul mercato.
In realtà esistono , nello sviluppo per Windows Forms, dei meccanismi leggermente più sofisticati per gestire il posizionamento dei controlli come, ad esempio, il docking o l’anchoring e controlli contenitori quali TableLayoutPanel o FlowLayoutPanel che ci consentono di rendere l’interfaccia grafica leggermente meno rigida ai cambiamenti di risoluzione.
La filosofia che sta dietro il layout in WPF, invece, è completamente differente e si basa sul medesimo concetto di layout che troviamo nel mondo web, cioè un layout di tipo flow, che non deve dipendere dalle coordinate fisiche.
I cardini alla base del layout in WPF sono i seguenti:
· Gli elementi dell’interfaccia non debbono avere una dimensione stabilita ma debbono riempire tutto lo spazio a loro disposizione (il contenitore);
· Gli elementi dell’interfaccia non debbono essere posizionati indicando le loro coordinate fisiche;
· Gli oggetti contenitori condividono il proprio spazio a disposizione tra tutti i controlli figli;
· Gli oggetti contenitori possono essere inseriti uno dentro l’altro.
Il processo di disposizione dei controlli in un contenitore WPF si suddivide in due fasi:
· Fase di misurazione : in questa fase, il contenitore itera sui suoi figli (oggetti in esso contenuti) richiedendo, ad ognuno di essi, la dimensione “preferita” (cioè la dimensione che il controllo assumerebbe, a causa del proprio contenuto, se non ci fossero impedimenti);
· Fase di posizionamento : in questa fase il contenitore, in base alla propria dimensione e ai dati ricavati nella precedente fase, posiziona correttamente gli oggetti in esso contenuti.
Tutti i controlli contenitori in WPF derivano dalla classe astratta Panel contenuta nel namespace System.Windows.Controls. La gerarchia delle classi è la seguente:
Unico contenitore (anche se, in realtà non è esattamente un contenitore) a non derivare da Panel è l’InkCanvas la cui funzione è quella di permettere l’utilizzo del pennino per l’input dei dati.
Analizziamo, ora, in dettaglio i singoli contenitori messi a disposizione dal framework WPF.
StackPanel
Lo StackPanel è il più semplice contenitore che abbiamo a disposizione. Permette di disporre i controlli al suo interno su un unica riga o colonna. Per default, lo StackPanel dispone i controlli in colonna dall’alto verso il basso facendo loro occupare tutta la larghezza del pannello. Possiamo cambiare l’orientamento dei controlli all’interno dello StackPanel (in colonna o in riga) agendo sulla proprietà Orientation.
Un esempio di utilizzo dello StackPanel è il seguente:
- <Window x:Class="StackPanelWindow"
- xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
- xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
- Title="StackPanel" Height="200" Width="200">
- <StackPanel Margin="10" Background="yellow" Name="pnlStackPanel">
- <Button Margin="5">Bottone 1</Button>
- <Button Margin="5" HorizontalAlignment="Right" VerticalAlignment="Top" >Bottone 2</Button>
- <Button Margin="5" HorizontalAlignment="Left" VerticalAlignment="Bottom" >Bottone 3</Button>
- </StackPanel>
- </Window>
Il risultato grafico dello XAML precedente è rappresentato dalle seguenti immagini nelle quali si è impostato il valore della proprietà Orientation a Vertical e a Horizontal rispettivamente.
Osserviamo che la distanza tra lo StackPanel e il bordo della finestra è stato impostato tramite la proprietà Margin così come lo spazio di ogni singolo bottone rispetto a ciò che lo circonda. I bottoni sono allineati, all’interno dello StackPanel, utilizzando le proprietà HorizontalAlignment e VerticalAlignment.
La seguente figura mostra come agiscono i diversi margini dei controlli:
Grid
La griglia è il contenitore più potente tra quelli messi a disposizione da WPF e con esso è praticamente possibile realizzare qualsiasi layout.
In un controllo Grid possiamo definire una collezione di righe e una collezione di colonne. Per posizionare i controlli all’interno delle celle della griglia è necessario utilizzare le attached property Row e Column della griglia stessa. Un controllo può anche occupare più righe o più colonne. Il seguente esempio propone una griglia con tre controlli (button, textblock e textbox) alcuni dei quali si estendono su più celle:
- <Window x:Class="GridWindow"
- xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
- xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
- Title="Grid" Height="300" Width="300">
- <Grid ShowGridLines="True" >
- <Grid.RowDefinitions>
- <RowDefinition Height="30"></RowDefinition>
- <RowDefinition Height="Auto"></RowDefinition>
- <RowDefinition Height="*"></RowDefinition>
- </Grid.RowDefinitions>
- <Grid.ColumnDefinitions>
- <ColumnDefinition Width="80"></ColumnDefinition>
- <ColumnDefinition Width="Auto"></ColumnDefinition>
- <ColumnDefinition Width="*"></ColumnDefinition>
- </Grid.ColumnDefinitions>
- <Button Grid.Row="0" Grid.Column="0"
- Grid.RowSpan="2" Margin="5">Bottone</Button>
- <TextBlock Grid.Row="1" Grid.Column="1"
- Height="100" Margin="5"
- Background="BlanchedAlmond">Text Block</TextBlock>
- <TextBox Grid.Row="2" Grid.Column="1"
- Grid.ColumnSpan="2" Margin="5"
- Background="BurlyWood" TextWrapping="Wrap"></TextBox>
- </Grid>
- </Window>
Il risultato dello XAML è il seguente:
Per fare si che un controllo occupi, ad esempio, due colonne utilizziamo l’attached property Grid.ColumnSpan=”2”.
WrapPanel
Il contenitore WrapPanel dispone i controlli figli su una riga da destra verso sinistra (o su una colonna dall’alto verso il basso, in base alla proprietà Orientation) andando a capo quando un controllo non entra interamente nella riga (o nella colonna). Nel momento in cui si ridimensiona il WrapPanel i controlli vengono riposizionati dinamicamente.
Il seguente pezzo di XAML mostra un esempio di utilizzo del WrapPanel:
- <Window x:Class="WrapPanelWindow"
- xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
- xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
- Title="WrapPanel" Height="150" Width="250">
- <WrapPanel Margin="10" Background="yellow" Name="pnlWrapPanel">
- <Button Margin="5">Bottone 1</Button>
- <Button Margin="5" Width="80" Height="30">Bottone 2</Button>
- <Button Margin="5" Width="100" Height="40">Bottone 3</Button>
- </WrapPanel>
- </Window>
Se impostiamo l’Orientation pari a Horizontal otteniamo:
mentre se impostiamo l’Orientation uguale a Vertical otteniamo:
Per quanto riguarda la gestione dei margini, vale l’analogo discorso fatto per lo StackPanel.
DockPanel
Il DockPanel dispone i controlli in esso contenuti ridimensionandoli ed agganciandoli ad uno dei bordi. Il bordo a cui agganciare un determinato controllo è impostato tramite l’attached property Dock. L’ordine con cui vengono definiti i controlli contenuti è importante per il layout definitivo. Il DockPanel ridimensiona il controllo in base allo spazio a disposizione, così se mettiamo prima il controllo agganciato in alto e, poi, quello a sinistra, avremo che il controllo in alto avrà una dimensione maggiore dell’altro.
Un esempio di utilizzo del WrapPanel è il seguente XAML:
- <Window x:Class="DockPanelWindow"
- xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
- xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
- Title="DockPanel" Height="300" Width="300">
- <DockPanel LastChildFill="false" >
- <Button DockPanel.Dock="Left">Sinistra</Button>
- <Button DockPanel.Dock="Top">Alto</Button>
- <Button DockPanel.Dock="Right">Destra</Button>
- <Button DockPanel.Dock="Bottom">Basso</Button>
- <Button>No Dock</Button>
- </DockPanel>
- </Window>
il quale genera il seguente layout:
Per capire il meccanismo di docking dei controlli, scambiamo il primo bottone del precedente XAMl con il secondo e vediamo che il layout cambia radicalmente:
Il DockPanel ha un duplice comportamento per quanto riguarda i controlli senza la proprietà Dock impostata: impostando la proprietà LastChildFill a True i controlli senza Dock occupano tutto lo spazio rimanente del contenitore, mentre impostando LastChildFill a False i controlli senza Dock mantengono il consueto comportamento.
UniformGrid
La UniformGrid è una griglia in cui possiamo decidere il numero di colonne e di righe ma non le dimensione specifiche delle stesse. I controlli vengono inseriti all’interno delle celle in base al loro ordine di definizione.
Un esempio della UniformGrid è il seguente:
- <Window x:Class="UniformGridWindow"
- xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
- xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
- Title="UniformGrid" Height="216" Width="300">
- <UniformGrid Rows="3" Columns="2">
- <Button>Bottone 0,0</Button>
- <Button>Bottone 0,1</Button>
- <Button>Bottone 1,0</Button>
- <Button>Bottone 1,1</Button>
- <Button>Bottone 2,0</Button>
- <Button>Bottone 2,1</Button>
- </UniformGrid >
- </Window>
Nella versione RC di Visual Studio 2010, il controllo UniformGrid non è presente nella finestra degli strumenti ma è possibile utilizzarlo scrivendo i tag opportuni a mano oppure inserendolo nella toolbox tramite l’opzione di Visual Studio.
Canvas
Il contenitore Canvas consente di disporre i controlli al proprio interno in base a delle coordinate esatte (e, in questo senso, va controcorrente rispetto alla filosofia del layout WPF).
Un esempio di Canvas è il seguente:
- <Window x:Class="CanvasWindow"
- xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
- xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
- Title="CanvasWindow" Height="300" Width="300">
- <Canvas>
- <Button Canvas.Top="20" Canvas.Left="50" Width="100">50,20</Button>
- <Button Canvas.Top="140" Canvas.Left="200" Width="50">140,200</Button>
- <Button Canvas.Top="180" Canvas.Left="100" Width="150" Height="50">180,100</Button>
- </Canvas>
- </Window>
InkCanvas
L’InkCanvas non è un vero e proprio contenitore ma permette di fornire all’utente le funzionalità di input dei dati tramite un pennino (ad esempio nei dispositivi touch).
Tra le sue caratteristiche c’è quella di permettere all’utente di disegnare con il pennino o il mouse oppure di reagire semplicemente ai gesti che l’utente esegue al di sopra di esso.
In questo tutorial non entreremo nei dettagli trattandosi di un controllo particolare che richiede maggiore attenzione.
Commenti