A seguito di un thread apparso sul forum Microsoft per lo sviluppo in Windows Phone vorrei riportare una implementazione di un converter che ci consente di cambiare il background di un controllo nello XAML in base al tema attivo.
Per cominciare “rubiamo” il tip del Genio Del Male riguardante il modo con cui determinare il tema attivo (link) e lo convertiamo in VB.NET. In particolare creiamo un metodo shared (ma andrebbe bene anche una proprietà shared) nella classe App della nostra applicazione:
- Public Shared Function GetCurrentTheme() As Theme
- Dim bgc = App.Current.Resources("PhoneBackgroundColor").ToString()
- If bgc = "#FF000000" Then
- Return Theme.Dark
- Else
- Return Theme.Light
- End If
- End Function
L’enumerazione Theme è definita nel seguente modo:
- Public Enum Theme
- Dark
- Light
- End Enum
A questo punto possiamo creare il nostro conveter:
- Imports System.Windows.Data
- Imports System.Windows.Media
- Public Class ThemeColorConverter
- Implements IValueConverter
- Private Function GetColorFromName(ByVal strColor As String) As Color?
- Dim retColor As Color? = Nothing
- Try
- Dim propColor = GetType(Colors).GetProperty(strColor)
- If propColor IsNot Nothing Then
- Dim value = propColor.GetValue(Nothing, Nothing)
- If value IsNot Nothing Then
- retColor = CType(value, Color)
- End If
- End If
- Catch ex As Exception
- retColor = Nothing
- End Try
- Return retColor
- End Function
- Private Function GetColorFromRGB(ByVal strColor As String) As Color?
- Dim retColor As Color? = Nothing
- strColor = strColor.Replace("#", "")
- Dim a As Byte = 255
- Dim r As Byte = 255
- Dim g As Byte = 255
- Dim b As Byte = 255
- Dim start = 0
- If strColor.Length = 6 Or strColor.Length = 8 Then
- If strColor.Length = 8 Then
- a = Byte.Parse(strColor.Substring(0, 2), System.Globalization.NumberStyles.HexNumber)
- start = 2
- End If
- r = Byte.Parse(strColor.Substring(start, 2), System.Globalization.NumberStyles.HexNumber)
- g = Byte.Parse(strColor.Substring(start + 2, 2), System.Globalization.NumberStyles.HexNumber)
- b = Byte.Parse(strColor.Substring(start + 4, 2), System.Globalization.NumberStyles.HexNumber)
- retColor = Color.FromArgb(a, r, g, b)
- End If
- Return retColor
- End Function
- Private Function GetColor(ByVal strColor As String) As Color
- Dim retColor As Color
- Dim tmpColor = GetColorFromName(strColor)
- If Not tmpColor.HasValue Then
- tmpColor = GetColorFromRGB(strColor)
- If tmpColor.HasValue Then
- retColor = tmpColor.Value
- End If
- Else
- retColor = tmpColor.Value
- End If
- Return retColor
- End Function
- Public Function Convert(ByVal value As Object,
- ByVal targetType As System.Type,
- ByVal parameter As Object,
- ByVal culture As System.Globalization.CultureInfo) As Object Implements System.Windows.Data.IValueConverter.Convert
- If value IsNot Nothing Then
- Dim brush As SolidColorBrush = Nothing
- Dim values = value.ToString().Split("|"c)
- If values.Count = 2 Then
- If App.GetCurrentTheme() = Theme.Light Then
- brush = New SolidColorBrush(GetColor(values(0)))
- Else
- brush = New SolidColorBrush(GetColor(values(1)))
- End If
- End If
- Return brush
- Else
- Return Nothing
- End If
- End Function
- Public Function ConvertBack(ByVal value As Object,
- ByVal targetType As System.Type,
- ByVal parameter As Object,
- ByVal culture As System.Globalization.CultureInfo) As Object Implements System.Windows.Data.IValueConverter.ConvertBack
- Throw New NotImplementedException()
- End Function
- End Class
La funzione GetColor ci consente di ricavare un oggetto Color a partire da una stringa nei seguenti formati #AARRGGBB, #RRGGBB o “nome colore” (con il nome che può essere uno dei colori possibili della classe Colors).
Il metodo Convert del nostro converter recupera l’argomento value che per convenzione dovrà essere una stringa con il seguente formato:
colore tema light|colore tema dark
Il metodo splitta la stringa Value per recuperare i due colori e, in base al risultato del metodo GetCurrentTheme (visto in precedenza), recupera il colore utilizzando la GetColor e crea un SolidBrush che restituisce al chiamante.
Il converter così scritto si può utilizzare direttamente in binding all’interno dello XAML.
Per fare questo è sufficiente referenziare il namespace opportuno:
- xmlns:my="clr-namespace:WP7ThemeBackground"
Inserire il converter nelle risorse dell’applicazione o della pagina:
- <phone:PhoneApplicationPage.Resources >
- <my:ThemeColorConverter x:Key="TCC"></my:ThemeColorConverter>
- </phone:PhoneApplicationPage.Resources>
E, infine, mettere il convertitore in binding con il background del controllo desiderato:
- <Grid x:Name="LayoutRoot" Background="{Binding Converter={StaticResource TCC}, Source=Red|#AA00FF00}">
In questo esempio, il colore per il tema light è il rosso (Red) mentre quello per il tema dark è il colore #AA00FF00 (un verde con della trasparenza).
Lo XAML completo dell’esempio è il seguente:
- <phone:PhoneApplicationPage
- x:Class="WP7ThemeBackground.MainPage"
- 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:my="clr-namespace:WP7ThemeBackground"
- mc:Ignorable="d" d:DesignWidth="480" d:DesignHeight="768"
- FontFamily="{StaticResource PhoneFontFamilyNormal}"
- FontSize="{StaticResource PhoneFontSizeNormal}"
- Foreground="{StaticResource PhoneForegroundBrush}"
- SupportedOrientations="Portrait" Orientation="Portrait"
- shell:SystemTray.IsVisible="True" >
- <phone:PhoneApplicationPage.Resources >
- <my:ThemeColorConverter x:Key="TCC"></my:ThemeColorConverter>
- </phone:PhoneApplicationPage.Resources>
- <!--LayoutRoot is the root grid where all page content is placed-->
- <Grid x:Name="LayoutRoot" Background="{Binding Converter={StaticResource TCC}, Source=Red|#AA00FF00}">
- <Grid.RowDefinitions>
- <RowDefinition Height="Auto"/>
- <RowDefinition Height="*"/>
- </Grid.RowDefinitions>
- <!--TitlePanel contains the name of the application and page title-->
- <StackPanel x:Name="TitlePanel" Grid.Row="0" Margin="12,17,0,28">
- <TextBlock x:Name="ApplicationTitle" Text="MY APPLICATION" Style="{StaticResource PhoneTextNormalStyle}"/>
- <TextBlock x:Name="PageTitle" Text="page name" Margin="9,-7,0,0" Style="{StaticResource PhoneTextTitle1Style}"/>
- </StackPanel>
- <!--ContentPanel - place additional content here-->
- <Grid x:Name="ContentPanel" Grid.Row="1" Margin="12,0,12,0"></Grid>
- </Grid>
- </phone:PhoneApplicationPage>
Commenti