XAML is not a real programming
language. It doesn’t include anything like if statements. XAML isn’t capable of making
decisions.
But that doesn’t mean we can’t
try.
As you may have noticed, the Clock class used the straight Hour property from DateTime, which is a 24-hour clock value. You might
instead want a 12-hour clock and display the text “AM” or “PM” to
indicate the morning or afternoon.
Normally you can do that by
formatting the time (if the Clock class
actually provided a DateTime object indicating the time) but suppose you want to
be very flexible about how you display the AM and PM information—perhaps
you’d prefer to display the text “in the morning” or “in the
afternoon”—and you want to do it in XAML.
Here’s a new class named TwelveHourClock that derives from Clock.
Example 1. Silverlight
Project: Petzold.Phone.Silverlight File: TwelveHourClock.cs
using System; using System.ComponentModel;
namespace Petzold.Phone.Silverlight { public class TwelveHourClock : Clock { int hour12; bool isam, ispm;
public int Hour12 { protected set { if (value != hour12) { hour12 = value; OnPropertyChanged(new PropertyChangedEventArgs("Hour12")); } } get { return hour12; } }
public bool IsAm { protected set { if (value != isam) { isam = value; OnPropertyChanged(new PropertyChangedEventArgs("IsAm")); } } get { return isam; } }
public bool IsPm { protected set { if (value != ispm) { ispm = value; OnPropertyChanged(new PropertyChangedEventArgs("IsPm")); } } get { return ispm; } }
protected override void OnPropertyChanged(PropertyChangedEventArgs args) { if (args.PropertyName == "Hour") { Hour12 = (Hour - 1) % 12 + 1; IsAm = Hour < 12; IsPm = !IsAm; }
base.OnPropertyChanged(args); } } }
|
The TwelveHourClock class defines three new properties, all
triggering PropertyChanged events.
These are Hour12 and two Boolean
properties, IsAm and IsPm. The override of OnPropertyChanged checks if the property
being changed is Hour and, if so, calculates new values for these three
properties, which themselves cause calls to OnPropertyChanged.
Because isAm is simply the logical
negation of isPM,
you may wonder why both properties are required. Because XAML itself can’t
perform a logical negation, having both properties available becomes
extremely convenient.
Let’s instantiate the TwelveHourClock
class in a Resources
collection and give it a key of “clock12”:
<phone:PhoneApplicationPage.Resources>
<petzold:TwelveHourClock x:Key="clock12" />
</phone:PhoneApplicationPage.Resources>
If you’d like XAML to
display some text
along the lines of “It’s after 9 in the morning,” you might begin like
this:
<Grid x:Name="ContentPanel" Grid.Row="1" Margin="12,0,12,0">
<StackPanel DataContext="{StaticResource clock12}"
Orientation="Horizontal"
HorizontalAlignment="Center"
VerticalAlignment="Center">
<TextBlock Text="It's after " />
<TextBlock Text="{Binding Hour}" />
<TextBlock Text=" in the morning." />
<TextBlock Text=" in the afternoon." />
</StackPanel>
</Grid>
This XAML has separate text
strings for morning and afternoon, but at any time only one of them
should be displayed depending on whether IsAm
or IsPm is true.
How is such a thing even possible?
Another converter is required,
and this is also a converter that you’ll use quite often. It’s called a BooleanToVisibilityConverter
and it assumes that the source value is a Boolean and the target is a
property of type Visibility:
Example 2. Silverlight
Project: Petzold.Phone.Silverlight File: BooleanToVisibilityConverter.cs
using System; using System.Globalization; using System.Windows; using System.Windows.Data;
namespace Petzold.Phone.Silverlight { public class BooleanToVisibilityConverter : IValueConverter { public object Convert(object value, Type targetType, object parameter, CultureInfo culture) { return (bool)value ? Visibility.Visible : Visibility.Collapsed; }
public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture) { return (Visibility)value == Visibility.Visible; } } }
|
Add that class to the Resources collection:
<phone:PhoneApplicationPage.Resources>
<petzold:TwelveHourClock x:Key="clock12" />
<petzold:BooleanToVisibilityConverter x:Key="booleanToVisibility" />
</phone:PhoneApplicationPage.Resources>
Now bind the Visibility properties of the final two TextBlock elements to the IsAm and IsPm
properties using the BooleanToVisibilityConverter. Here’s the markup from the project AmOrPm:
Example 3. Silverlight
Project: AmOrPm File: MainPage.xaml (excerpt)
<Grid x:Name="ContentPanel" Grid.Row="1" Margin="12,0,12,0"> <StackPanel DataContext="{StaticResource clock12}" Orientation="Horizontal" HorizontalAlignment="Center" VerticalAlignment="Center"> <TextBlock Text="It's after " /> <TextBlock Text="{Binding Hour}" /> <TextBlock Text=" in the morning." Visibility="{Binding IsAm, Converter={StaticResource booleanToVisibility}}" /> <TextBlock Text=" in the afternoon." Visibility="{Binding IsPm, Converter={StaticResource booleanToVisibility}}"/> </StackPanel> </Grid>
|
And it works: