It’s not unreasonable to create a
data-binding converter that is so specialized or so weird that it’s
only good for one particular application. For example, here’s a class called DecimalBitToBrushConverter. This converter includes two public properties
named ZeroBitBrush and OneBitBrush.
Example 1. Silverlight
Project: BinaryClock File: DecimalBitToBrushConverter.cs
using System; using System.Globalization; using System.Windows.Data; using System.Windows.Media;
namespace BinaryClock { public class DecimalBitToBrushConverter : IValueConverter { public Brush ZeroBitBrush { set; get; } public Brush OneBitBrush { set; get; }
public object Convert(object value, Type targetType, object parameter, CultureInfo culture) { int number = (int)value; int bit = Int32.Parse(parameter as string); int digit = number / PowerOfTen(bit / 4) % 10;
return ((digit & (1 << (bit % 4))) == 0) ? ZeroBitBrush : OneBitBrush; }
public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture) { return null; }
int PowerOfTen(int exp) { int value = 1;
for (int i = 0; i < exp; i++) value *= 10;
return value; } } }
|
The Convert method
expects a value argument of type int, and a valid parameter argument. When you set the ConverterParameter
property in XAML to a string, it will come into the Convert method as an
object of type string,
at which point you must convert it manually into the desired type. (To
override that behavior, you’d need to use property-element syntax for
the ConverterParameter
and specify the type using element tags.) This Convert method
expects this string to represent another int, so it passes the string the Int32.Parse.
The value argument
is a number, for example 127. The parameter
argument, when converted to an int, is a bit position, for example, 6. The method
essentially breaks the incoming number into decimal digits, in this
example 1, 2 and 7, and then finds the digit in the specified bit
position. The 7 of 127 corresponds to bit positions of 0 through 3; the 2
of 127 is bit positions 4 through 7; the 1 of 127 is bit positions 8
through 11.
If the bit in that bit position
is 1, Convert returns OneBitBrush; if it’s 0, Convert returns ZeroBitBrush.
I use this converter in a project called BinaryClock. The
converter is referenced in a UserControl
derivative called BinaryNumberRow.
Notice how the two public properties of DecimalBitToBrushConverter
are set right in the Resources
collection, which also includes a Style
for the Ellipse.
Example 2. Silverlight
Project: BinaryClock File: BinaryNumberRow.xaml
<UserControl x:Class="BinaryClock.BinaryNumberRow" xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" xmlns:petzold="clr-namespace:Petzold.Phone.Silverlight;assembly=Petzold.Phone. Silverlight" xmlns:local="clr-namespace:BinaryClock">
<UserControl.Resources> <Style x:Key="ellipseStyle" TargetType="Ellipse"> <Setter Property="Width" Value="48" /> <Setter Property="Height" Value="48" /> <Setter Property="Stroke" Value="{StaticResource PhoneForegroundBrush}" /> <Setter Property="StrokeThickness" Value="2" /> </Style>
<local:DecimalBitToBrushConverter x:Key="converter" ZeroBitBrush="{x:Null}" OneBitBrush="Red" /> </UserControl.Resources>
<petzold:UniformStack Orientation="Horizontal"> <Ellipse Style="{StaticResource ellipseStyle}" Fill="{Binding Converter={StaticResource converter}, ConverterParameter=6}" />
<Ellipse Style="{StaticResource ellipseStyle}" Fill="{Binding Converter={StaticResource converter}, ConverterParameter=5}" />
<Ellipse Style="{StaticResource ellipseStyle}" Fill="{Binding Converter={StaticResource converter}, ConverterParameter=4}" />
<Ellipse Style="{StaticResource ellipseStyle}" Stroke="{x:Null}" />
<Ellipse Style="{StaticResource ellipseStyle}" Fill="{Binding Converter={StaticResource converter}, ConverterParameter=3}" />
<Ellipse Style="{StaticResource ellipseStyle}" Fill="{Binding Converter={StaticResource converter}, ConverterParameter=2}" />
<Ellipse Style="{StaticResource ellipseStyle}" Fill="{Binding Converter={StaticResource converter}, ConverterParameter=1}" />
<Ellipse Style="{StaticResource ellipseStyle}" Fill="{Binding Converter={StaticResource converter}, ConverterParameter=0}" /> </petzold:UniformStack> </UserControl>
|
The body of the BinaryNumberRow visual tree is a horizontal UniformStack containing seven Ellipse elements. Each has a Binding that assigns only the Converter property to the DecimalBitToBrushConverter and a ConverterParameter that ranges from 0 for the
Ellipse on the right to 6 for the Ellipse on the left. None of the bindings include Source
or Path settings! These are obviously
set elsewhere in the DataContext for
the BinaryNumberRow instance.
The MainPage.xaml file of the BinaryClock project instantiates the TwelveHourClock object in its Resources section:
Example 3. Silverlight
Project: BinaryClock File: MainPage.xaml
<phone:PhoneApplicationPage.Resources> <petzold:TwelveHourClock x:Key="clock12" /> </phone:PhoneApplicationPage.Resources>
|
The content area contains a vertically centered StackPanel with three instances of BinaryNumberRow:
Example 4. Silverlight
Project: BinaryClock File: MainPage.xaml
<Grid x:Name="ContentPanel" Grid.Row="1" Margin="12,0,12,0">
<StackPanel DataContext="{StaticResource clock12}" VerticalAlignment="Center">
<local:BinaryNumberRow DataContext="{Binding Hour12}" Margin="0 12" />
<local:BinaryNumberRow DataContext="{Binding Minute}" Margin="0 12" />
<local:BinaryNumberRow DataContext="{Binding Second}" Margin="0 12" /> </StackPanel> </Grid>
|
Notice the DataContext settings: The StackPanel has its DataContext
set to the TwelveHourClock itself.
Each of the BinaryNumberRow controls
has a DataContext set to one of the
properties of TwelveHourClock. This is
why the Binding definitions in BinaryNumberRow only need to contain a Converter and ConverterParameter.
The result, of course, is a binary clock:
The time is, ummm, 12:58:06.
You might wonder if some of the Binding markup
might be reduced even further. Considering that all the individual Ellipse elements have the same Converter setting, might that be moved to the first DataContext setting on the StackPanel? No it can’t. The Converter and ConverterParameter settings must appear together in the same Binding definition.