How to Index Arrays in XAML
May 10, 2006
New York City
Suppose you need to obtain a string for the name of the current month. In C#, it's trivial. You start off with using directives for System and System.Globalization and then index the MonthNames array of DateTimeFormatInfo with the Month property of a DateTime object, remembering to convert the Month from 1-based to 0-based:
-
DateTimeFormatInfo.CurrentInfo.MonthNames[DateTime.Now.Month - 1]
However, doing something similar in XAML isn't quite as straightforward. (For the following examples, the 's' prefix refers to an XML namespace for System and 'g' for System.Globalization.) You can easily use a Binding to display the current month number, like so:
-
<TextBlock Text="{Binding Source={x:Static s:DateTime.Now}, Path=Month}" />
That will display the number 5 for the month of May. You can also specify a numeric index of the MonthNames array. This XAML displays the string "May":
-
<TextBlock Text="{Binding Source={x:Static g:DateTimeFormatInfo.CurrentInfo}, Path=MonthNames[4]}" />
However, merging these two bindings just doesn't work.
It is possible to index an array in XAML, however. The trick is finding a class that indexes arrays, and it's more obvious than it may seem. ListBox, for example, stores an array of items, and setting SelectedIndex selects one of those items, which is then obtainable from SelectedItem. So, if a ListBox contains the MonthNames array, and SelectedIndex is set to the Month property of a DateTime object, then SelectedItem is the month name.
The TodaysDate.xaml file illustrates this technique with several TextBlock elements that format the current date. Here's a listing of the program:
-
<!-- =============================================
TodaysDate.xaml (c) 2006 by Charles Petzold
============================================= -->
<Page xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:s="clr-namespace:System;assembly=mscorlib"
xmlns:g="clr-namespace:System.Globalization;assembly=mscorlib"
WindowTitle="Today's Date"
Title="Today's Date">
<Page.Resources>
<!-- Save today's date as the Tag property of this resource. -->
<FrameworkElement x:Key="datetime" Tag="{x:Static s:DateTime.Now}" />
<!-- Use a transform to subtract 1 from the 1-based Month. -->
<TransformGroup x:Key="xformMonth">
<TranslateTransform X="{Binding Source={StaticResource datetime},
Path=Tag.Month}" />
<TranslateTransform X="-1" />
</TransformGroup>
<!-- Store all the month names in a ListBox. -->
<ListBox x:Key="monthnames"
ItemsSource=
"{Binding Source={x:Static g:DateTimeFormatInfo.CurrentInfo},
Path=MonthNames}"
SelectedIndex=
"{Binding Source={StaticResource xformMonth},
Path=Value.OffsetX}" />
</Page.Resources>
<StackPanel Orientation="Horizontal"
HorizontalAlignment="Center" VerticalAlignment="Center"
TextBlock.FontSize="24pt">
<TextBlock Text="{Binding Source={StaticResource datetime},
Path=Tag.DayOfWeek}" />
<TextBlock Text=", " />
<TextBlock Text="{Binding Source={StaticResource monthnames},
Path=SelectedItem}" />
<TextBlock Text=" " />
<TextBlock Text="{Binding Source={StaticResource datetime},
Path=Tag.Day}" />
<TextBlock Text=", " />
<TextBlock Text="{Binding Source={StaticResource datetime},
Path=Tag.Year}" />
</StackPanel>
</Page>
In the Resources section the Framework element with a key of "datetime" obtains the current DateTime once, to be used throughout the rest of the program. The TransformGroup that follows effectively subtracts 1 from the Month property. The ListBox stores the MonthNames array and the SelectedIndex is the zero-based month.
The series of TextBlock elements display the parts of the date. The DayOfWeek property is an enumeration and can be used directly. The third TextBlock element displays the name of the month with a simple binding to the SelectedItem property of the ListBox.