Per modificare la User Interface di una pagina di un’applicazione Silverlight per Windows Phone 7 al variare dell’orientazione del device (Portrait o Landscape) si può bindare le varie proprietà dei controlli al valore della proprietà “Orientation” della pagina stessa.
Ad esempio, supponiamo di avere una semplice pagina con due modalità grafiche, una da visualizzare se l’orientazione è portrait e l’altra da visualizzare se l’orientazione è landscape:
Lo XAML della pagina è il seguente:
<phone:PhoneApplicationPage
x:Class="WindowsPhoneApplication.MainPage"
x:Name="ThisPage"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:phone="clr-namespace:Microsoft.Phone.Controls;assembly=Microsoft.Phone"
xmlns:shell="clr-namespace:Microsoft.Phone.Shell;assembly=Microsoft.Phone"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:converters="clr-namespace:WindowsPhoneApplication.Converters"
mc:Ignorable="d" d:DesignWidth="480" d:DesignHeight="800"
FontFamily="{StaticResource PhoneFontFamilyNormal}"
FontSize="{StaticResource PhoneFontSizeNormal}"
Foreground="{StaticResource PhoneForegroundBrush}"
shell:SystemTray.IsVisible="False"
SupportedOrientations="PortraitOrLandscape" Orientation="Portrait" >
<phone:PhoneApplicationPage.Resources>
<converters:ConvertOrientationToVisible x:Key="ConvertOrientationToVisible" />
</phone:PhoneApplicationPage.Resources>
<!--LayoutRoot is the root grid where all page content is placed-->
<Grid x:Name="LayoutRoot" Background="Transparent">
<!-- Landscape ContentPanel -->
<Grid x:Name="LandscapeContentPanel" Background="#FF2F2FD6"
Visibility="{Binding ElementName=ThisPage, Path=Orientation,
Converter={StaticResource ConvertOrientationToVisible},
ConverterParameter=Landscape}">
<TextBlock x:Name="LandscapeTextBox" Text="Landscape" FontSize="80"
HorizontalAlignment="Center" VerticalAlignment="Center"/>
</Grid>
<!-- Portrait ContentPanel -->
<Grid x:Name="PortraitContentPanel" Background="#FF1EAD34"
Visibility="{Binding ElementName=ThisPage, Path=Orientation,
Converter={StaticResource ConvertOrientationToVisible},
ConverterParameter=portrait}">
<TextBlock x:Name="PortraitTextBox" Text="Portrait" FontSize="60"
HorizontalAlignment="Center" VerticalAlignment="Center"/>
</Grid>
</Grid>
</phone:PhoneApplicationPage>
Le cose da notare, rispetto alla pagina creata dal template di Visual Studio, sono:
La proprietà x:Name="ThisPage" (necessaria per il successivo binding)
Il namespace xmlns:converters="clr-namespace:WindowsPhoneApplication.Converters" che serve per referenziare il converter di cui parleremo più avanti.
Le SupportedOrientations="PortraitOrLandscape" (al posto del solo Portrait)
La pagina contiene due grid semplicissime (ciascuna con una TextBox), una da mostrare in Portrait e una in Landscape, gestendone la visibilità mediante binding alla proprietà Orientation della pagina. Poiché si tratta di un elemento definito nello XAML con la proprietà x:Name (che noi abbiamo impostato a “ThisPage”), possiamo identificarlo come sorgente del binding tramite ElementName=ThisPage e richiedere la proprietà voluta tramite Path=Orientation.
I possibili valori di Orientation sono: Landscape, LandscapeLeft, LandscapeRight, None, Portrait, PortraitDown, PortraitUp.
Per ottenere dal binding il valore necessario (Visibility.Visible o Visibility.Collapsed) occorre utilizzare un converter. Per poter usare lo stesso converter sia per gli oggetti che devono essere visibili in Landscape sia per quelli che invece devono essere visibili in Portrait, occorre inoltre che il converter accetti un parametro che definisca qual’è l’orientazione per la quale vogliamo che venga restituito Visibility.Visible.
Ecco il sorgente del converter (una classe che abbiamo creato nella cartella Converters, appositamente creata nel progetto):
using System;
using System.Windows;
using System.Windows.Data;
using Microsoft.Phone.Controls;
namespace WindowsPhoneApplication.Converters
{
public class ConvertOrientationToVisible : IValueConverter
{
public object Convert(object value, Type targetType,
object parameter,
System.Globalization.CultureInfo culture)
{
if (value.GetType() != typeof(PageOrientation))
{
throw new NotImplementedException("Value must be of type Orientation");
}
else
{
if (targetType.Name != "Visibility")
{
throw new NotImplementedException("Target must be of type Visibility");
}
else
{
if (Visible((PageOrientation)value, (string)parameter))
{
return Visibility.Visible;
}
else
{
return Visibility.Collapsed;
}
}
}
}
bool Visible(PageOrientation pageOrientation, string requiredOrientation)
{
if (requiredOrientation.ToLower() == "portrait")
{
return (pageOrientation == PageOrientation.Portrait ||
pageOrientation == PageOrientation.PortraitUp ||
pageOrientation == PageOrientation.PortraitDown);
}
else
{
return (pageOrientation == PageOrientation.Landscape ||
pageOrientation == PageOrientation.LandscapeLeft ||
pageOrientation == PageOrientation.LandscapeRight);
}
}
public object ConvertBack(object value, Type targetType,
object parameter,
System.Globalization.CultureInfo culture)
{
throw new NotImplementedException();
}
}
}
Il motivo per il quale la verifica dell’orientazione viene fatta su tutti i valori possibili (ad esempio Portrait o PortraitUp o PortraitDown) dipende dal fatto che nel designer di Visual Studio e di Blend viene impostata in modo generico (ad esempio a Portrait), mentre nell’emulatore e nel device viene impostata in modo preciso (ad esempio PortraitUp).
Tornando al codice XAML relativo all’impostazione del binding, per utilizzare tale converter occorre:
Impostare il namespace:
xmlns:converters="clr-namespace:WindowsPhoneApplication.Converters”
Creare la risorsa statica:
<phone:PhoneApplicationPage.Resources>
<converters:ConvertOrientationToVisiblex:Key="ConvertOrientationToVisible" />
</phone:PhoneApplicationPage.Resources>
In questo modo è possibile richiedere la conversione con il converter e il relativo parametro:
Converter={StaticResource ConvertOrientationToVisible},
ConverterParameter=portrait}">
Da notare che, in alternativa, è sempre possibile creare due convertitori (chiamandoli ad esempio ConvertPortraitToVisible e ConvertLandscapeToVisible) in modo da eliminare il parametro e semplificando così sia lo XAML che il sorgente, ottenendo al contempo una certa duplicazione di codice. Nei miei progetti preferisco la seconda scelta (quella dei due convertitori) ma per quest’esempio ho preferito attenermi rigorosamente al principio DRY (Don’t Repeat Yourself).