Introduction
I've created a lot of bindings to boolean values that required a converter of some kind. And, I've found that a simple bool to object converter takes care of a lot of my problems. At first, I would create a user control or converter for just about everything; I've seen a few other people go down that same path.
Need to set the background image on a button based on some bool value of the selected item, write a bool to BitmapImage
converter. Need to set a text color, based on the success of an operation, write a bool to Brush
converter. That bool to Brush
converter was originally a bool to SolidColorBrush
converter, but as soon as I wrote that, I thought, that's way too specific. Switching that converter from solid color brush to just brush made me realize that I could have just done a BoolToObjectConverter
and gotten rid of quite a few classes in the code base for the project we were working on. This is where my BoolToContentConverter
comes in.
Sure, you can also do this with triggers, but if your values keep switching back to their default settings, then you'll need to know a bit about the Dependency Property Value Precedence System that WPF uses to choose the select values of Dependency Properties. I haven't looked at that in a while, but I think there were around seven sources for a property value. You could also do this by coding things into the code behind file, but that kind of defeats the purpose of separating your UI from the rest of your code.
Background
For this to be useful, you will need to know:
- How to use converters in your bindings
- How to use bindings
- How to use static resources in XAML
The Code
The code for the BoolToContentConverter
may look a bit bloated, but it's nothing to be scared of. We just have a class that implements the IValueConverter
with three extra properties. These properties will store the content you want to return when given a certain Boolean value.
public class BoolToContentConverter
: IValueConverter
{
public BoolToContentConverter()
{
TrueContent = "True";
FalseContent = "False";
NullContent = "No Value";
}
public object TrueContent { get; set; }
public object FalseContent { get; set; }
public object NullContent { get; set; }
public object Convert(object value, Type targetType,
object parameter, CultureInfo culture)
{
if (value == null)
return NullContent;
bool boolValue = true;
bool isBool = true;
try
{
boolValue = (bool) value;
}
catch
{
isBool = false;
}
if (!isBool)
return NullContent;
return boolValue ? TrueContent : FalseContent;
}
public object ConvertBack(object value, Type targetType,
object parameter, CultureInfo culture)
{
throw new NotImplementedException();
}
}
The BoolToContentConverter
can be broken down into three sections:
- Content properties
- You have three
object
type properties that will house the content that will be returned when given a Boolean value. The true
and false
should be self explanatory, but a beginning programmer might not have much experience with Nullable<T>
objects which usually look something like int?
, double?
, bool?
, etc. Some bool
type properties are actually bool?
type properties, so it's possible they may have a null
value, when binding to these guys. That's the reason why there is a NullContent
property.
- Default values
- I simple chose default values that I thought were fitting and used the constructor to set them.
- Conversion method
- The conversion method can be a bit tricky for two reasons. Someone may have used the converter on a binding source that isn't of either type
bool?
or bool
. And, the source may be null
. This is why we have both a null
check and a try
-catch
statement.
Using the Code
Getting a handy piece of code handed over to you is nice and all, but having good examples is even better. Which is why I'm providing you with two examples on using this. One of the nice things about the BoolToContentConverter
is that you really can place just about anything in those properties.
<local:BoolToContentConverter x:Key="AcceptedImageConverter"
TrueContent="{StaticResource CongratBitmapImage}"
FalseContent="{StaticResource ConsolationBitmapImage}"/>
In XAML, large chunks of code can be turned into reusable resources, this can reduce our memory footprint (if placed in the correct location) and make the location they are being used in more readable (they can also make it less readable and a real pain to track down). Here we have two BitmapImage
resources being set into the content properties. Like I was saying before, you really can put just about anything into those content properties.
<local:BoolToContentConverter x:Key="ActivatedBrushConverter" NullContent="Black">
<local:BoolToContentConverter.TrueContent>
<SolidColorBrush Color="Blue"/>
</local:BoolToContentConverter.TrueContent>
<local:BoolToContentConverter.FalseContent>
<SolidColorBrush Color="Gray"/>
</local:BoolToContentConverter.FalseContent>
</local:BoolToContentConverter>
In this example, we are creating the content that will be used in the in-line XAML code. I used SolidColorBrushs
here because I can create them in a small number of lines, but I could add an ImageBrush
to the NullContent
property. To be honest, SolidColorBrushes
might not be the best example for this because WPF has built in value converters that will convert the string
"Black
", that is being stored in the NullContent
property, into Brushes.Black
.
Points of Interest
One of the interesting things I learned when doing this is how powerful the attached converters on those WPF properties can be. This is meant for beginning level developers, so I'm not going to go into any real detail on the attribute system that's used to attached those converters. What I am going to say, is that the Background
property on WPF controls has brush converter attached to it. This is the reason why I can start out with something like the string
"Black
" in the NullContent
property and end up with SolidColorBrush
when this converter is used in a binding that's targeting a control's Background
property. That brush converter is also the reason why we can write "Black" in the Background
and Foreground
properties of any control and have it use Brushes.Black
.
The other thing I learned was to be careful what you place DependencyObjects
in those content properties (or in any static resource for that matter). The thing about WPF is that everything that shows up in the VisualTree
is a DependencyObject
. DependencyObjects
can have one and only one parent in their VisualTree
placement. Resources allow us to place items in multiple locations in the VisualTree
. The reason we can do this, is because a DependencyObject
will be used to display the resource, or the resource will be used by a DependencyObject
. No matter how many places a resource gets used in the VisualTree
, the VisualTree
will only consider DependencyObjects
as part of the tree.
What I'm trying to get at, is that placing DependencyObjects
in the content properties of the BoolToContentConverte
r will not work out so well if the converter is being used in multiple locations of the VisualTree
. I saw this happen with a System.Windows.Controls.Image
, and the Image
would only show up in the last location that called the converter. This is why we bind the Image
's Source
property to something like a BitmapImage
. I guess you could pull this off having an Image
in two locations in the VisualTree
if only one location was using that Image
it a time. My point is, be careful when using a DependencyObject
as a resource.
History
- Version 1, written by Francisco T. Chavez