Prima di proseguire oltre occorre sviscerare lo scopo e gli utilizzi della tipologia dei controlli WPF che permettono di sistemare il layout di una pagina.

Nativamente una pagina realizzata con la tecnologia WPF può contenere un solo elemento grafico: se si vogliono disporre più elementi (e noi lo vogliamo: una maschera con solo un bottone servirebbe a poco, non trovate ??) occorre usare un qualsiasi controllo di tipo layout.

Tramite questo tipo di controllo sarà possibile disporre gli altri controlli sulla maschera.

Esistono in WPF svariati tipi di tipi di controlli layout, ognuno dotato delle sue peculiarità: qui esporrò solo quelli che ritengo i più importanti.

StackPanel

Lo StackPanel è un controllo layout che semplicemente dispone gli elementi da lui contenuti uno dopo l’altro: la cosa interessante è che l’orientamento può essere sia verticale che orizzontale.

 

<Window x:Class="Ex033_Layout.WPFStackPanel"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        Title="WPFStackPanel" Height="300" Width="300">
    <StackPanel Orientation="Horizontal">
        <Label Background="Red">Label</Label>
        <TextBox >TextBox</TextBox>
        <CheckBox Background="DarkOrchid">CheckBox</CheckBox>
        <Button Background="Green">Button</Button>
    </StackPanel>
</Window>

Nelle figure a corredo è possibile vedere l’effetto finale per i due orientamenti.

x

 

WrapPanel

Il controllo WrapPanel posiziona gli elementi figli partendo da destra e proseguendo verso sinistra, o viceversa, sino a quando stanno sulla riga: dopo continua sulla linea successiva. Anche in questo caso l’orientamente può essere sia orizzontale che verticale.

<Window x:Class="LayoutDemo.WrapPanelWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
Title="WrapPanelWindow" Height="300" Width="300">
<WrapPanel>
<Button Width="100" Margin="5">Button</Button>
<Button Width="100" Margin="5">Button</Button>
<Button Width="100" Margin="5">Button</Button>
<Button Width="100" Margin="5">Button</Button>
<Button Width="100" Margin="5">Button</Button>
<Button Width="100" Margin="5">Button</Button>
<Button Width="100" Margin="5">Button</Button>
<Button Width="100" Margin="5">Button</Button>
</WrapPanel>
</Window>

Francamente è più facile a vedersi che a spiegarsi: la figura nel seguito dovrebbe dipanare ogni dubbio.

La cosa da notare è che grazie a WPF se per caso si ridimensiona la maschera allora la disposizione dei bottoni automagicamente seguità l'orientamento del wrap panel.

 

 

Grid

Il controllo layout di tipo grid rappresenta, a mio parere, il contenitore più efficace: infatti grazie all’ausilio delle sue caratteristiche è è possibile sistemare i controlli usando riga/colonna, un po' “battaglia navale style”.

<Window x:Class="WPF001_Layout.WindowGrid"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
        xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
        xmlns:local="clr-namespace:WPF001_Layout"
        mc:Ignorable="d"
        Title="WindowGrid" Height="300" Width="300">
    <Grid ShowGridLines="True">
        <Grid.ColumnDefinitions>
            <ColumnDefinition />
            <ColumnDefinition />
        </Grid.ColumnDefinitions>
        <Grid.RowDefinitions>
            <RowDefinition />
            <RowDefinition />
            <RowDefinition />
        </Grid.RowDefinitions>
        <Label Grid.Column="0" Grid.ColumnSpan="2" Grid.Row="0" VerticalAlignment="Center" HorizontalAlignment="Center" Content="=========Title========="/>
        <Label Grid.Column="0" Grid.Row="1" VerticalAlignment="Center" Content="Firstname:" Margin="10" />
        <TextBox Grid.Column="1" Grid.Row="1" Width="100" Height="30" />
        <Label Grid.Column="0" Grid.Row="2" VerticalAlignment="Center" Content="Lastname:" Margin="10" />
        <TextBox Grid.Column="1" Grid.Row="2" Width="100" Height="30" />
    </Grid>
</Window>

Dal codice sopra è intuibile che si è definita una una griglia dotata di due colonne e tre righe (ColumnDefinition e RowDefinition).

Su questa griglia così creata abbiamo quindi posizionato i controlli: per specificare per ognuno di questi la relativa posizione si sono utilizzate le istruzioni Grid.Column e Grid.Row (il conteggio parte da zero).

Un caso a parte riguarda il posizionamento del primo controllo, il quale in realtà occupa due colonne contigue: questa particolarità è definita da Grid.ColumnSpan.

Detto in altri termini la label è posizionata in posizione riga zero e colonna zero, ma poi la stessa può occupare anche la colonna vicino.

Manco a dirlo esiste anche la possibilità di usare Grid.RowSpan.

Penso sia interessante notare che la definizione iniziale relativa alla presenza di righe e colonne (ColumnDefinition e RowDefinition) non è corredata da alcun attributo che identifica una qualsiasi dimensione: infatti se non diversamente specificato le righe colonne così definite avranno sempre dimensioni tali da accogliere tutti i controlli che si vorranno inserire nelle varie celle.

In questo caso i controlli hanno delle dimensioni precise (attributi width e height) e pertanto con queste informazioni il nostro framework disegnerà per noi una maschera correttamente dimensionata.

 

La mia coscienza mi impone di dire qualcosa in più riguardo alla definizione delle colonne e righe.

Nel caso fosse necessario intervenire sulle dimensioni della griglia occorre tenere presente che l'istruzione ColumnDefinition è dotata di una proprietà Width, mentre RowDefinition di una proprietà Height.

E’ possibile definire queste proprietà usando dimensioni in pixels, centimeters, inches, o points, o anche semplicemente impostandole ad Auto (che è il default): in quest’ultimo caso la dimensione delle righe e colonne sarà calcolata dal framework per una disposizione ottimale dei controlli contenuti.

Questi elementi permettono di usare lo “star sizing”: in questo caso la dimensione della riga (o della colonna) è calcolata in modo relativo alla dimensione rispettivamente delle altre righe o colonne presenti nella griglia.

Per esempio diciamo che si vuole che una colonna assuma dimensione doppia rispetto ad un’altra colonna, la cui dimensione è auto. Ecco quindi che la definizione assumerà la seguente forma.

...
<Grid.ColumnDefinitions> <ColumnDefinition Width="*"/> <ColumnDefinition Width="2*"/> </Grid.ColumnDefinitions>
...

Ecco il risultato ottenuto.

 

Semplice e intuitivo ! La prima colonna ha dimensioni Auto, e pertanto si disporrà ad accogliere tutti i controlli necessari. la seconda colonna, invece, assumerà il doppio della dimensione assegnata automaticamente alla precedente.

Ci sarebbe molto ancora da dire su questa parte, ma non desidero appensantire troppo la trattazione: questi pochi elementi forniti sono a mio parere sufficienti per permetterVi di iniziare a creare maschere dall'aspetto grafico coerente con la maggiore parrte delle esigenze.