I was asked this morning how to implement something similar to the conditional formatting feature of Excel in Silverlight and thought I would throw together a quick and dirty sample.
The basic idea is that we get a number, lets say a grade in a course, and a grade higher then 60 is passing and we want to turn the background of the text box green, otherwise if the grade is less then 60 lets turn the background red.
The first step is to create a control to be the source of our grade, probably in the real application this would be coming from the database and bounded to the view, however to make things simpler I am just going to bind to the value property of a slider control. So now that I have a source of my number, lets bind it to a normal text block so we can see the slider work.
<TextBlock Name="textBlock1" Text="{Binding ElementName=sldGrade, Path=Value}" />
<Slider Name="sldGrade" Maximum="100" />
Ok, so now lets implement the ValueConverter. What is a ValueConverter you ask. . . MSDN says a ValueConverter “Provides a way to apply custom logic to a binding.” (http://msdn.microsoft.com/en-us/library/system.windows.data.ivalueconverter.aspx) Great that is exactly what we want to do. Provide some logic that can convert our number to a Brush we can apply to the background color of our TextBox.
public class GradeValueConverter : IValueConverter
{
public object Convert(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture)
{
double grade = 0;
Brush brush = new SolidColorBrush(Colors.Green);
if (double.TryParse(value.ToString(), out grade) && grade < 60)
brush = new SolidColorBrush(Colors.Red);
brush.Opacity = .6;
return brush;
}
public object ConvertBack(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture)
{
return null;
}
}
Nothing really earth shattering there. We don’t need to implement the ConvertBack method since we are only going to set the background color. The convert method takes in the value, convert it to a double, then we just test the value and return a brush. Great, now lets try to use it, to do that we need to make some changes to our XAML. First lets add our namespace to our control:
<UserControl x:Class="ColorStyles.MainPage"
xmlns:conv="clr-namespace:ColorStyles"
Now lets add a resource for our value converter.
<UserControl.Resources>
<conv:GradeValueConverter x:Name="gradeValueConverter"/>
</UserControl.Resources>
Great now we are ready to setup our text box.
<TextBox Name="txtGrade" Text="{Binding Path=Value, Mode=TwoWay, ElementName=sldGrade}"
Background="{Binding Converter={StaticResource gradeValueConverter}, RelativeSource={RelativeSource Self}, Path=Text}" Grid.Row="1" />
Now when we run this guy and push the slider to the right, wammo the background color changes. Nice! But lets kick it up a bit, lets use a gradient.
public class GradientGradeValueConverter : IValueConverter
{
public object Convert(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture)
{
double grade = 0;
GradientBrush gb = parameter as GradientBrush;
if (double.TryParse(value.ToString(), out grade) && gb != null)
{
gb.GradientStops[1].Offset = grade / 100;
}
return gb;
}
public object ConvertBack(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture)
{
return null;
}
}
In this converter we are passing in the brush as a parameter, this brush has 3 GradientStops (Green, Black, Red) and by moving the offset of the middle gradient stop we give it the desired effect. Here is what the brush looks like in XAML.
<UserControl.Resources>
<conv:GradientGradeValueConverter x:Name="gradeValueConverter2"/>
<LinearGradientBrush x:Name="gradeBrush" Opacity="0.6">
<GradientStop Color="Green" Offset="0" />
<GradientStop Color="Black" Offset="0.5" />
<GradientStop Color="Red" Offset="1" />
</LinearGradientBrush>
</UserControl.Resources>
The binding to the text box is a bit more complex since we now need to send in the brush as a parameter:
<TextBox Name="txtGrade2" Text="{Binding Path=Value, Mode=TwoWay, ElementName=sldGrade}"
Background="{Binding Converter={StaticResource gradeValueConverter2}, RelativeSource={RelativeSource Self}, Path=Text,ConverterParameter={StaticResource gradeBrush} }" Grid.Row="2" />
and just like that we can build an affect similar to the one we get in Excel. . .
I got some inspiration for my post from Jay’s post here:
http://www.analysts.com/Community/Blogs/archive/2009/05/15/silverlight-value-converters.aspx