Attached Behaviors Part 3: NullVisibility
- Attached Behaviors Part 1: BooleanVisibility
- Attached Behaviors Part 2: Framework
- Attached Behaviors Part 3: NullVisibility
- Attached Behaviors Part 4: EnumVisibility
- Attached Behaviors Part 5: EnumIsEnabled
- Attached Behaviors Part 6: EnumGroup
- Attached Behaviors Part 7: EnumSelector
Now that we have established a framework for writing attached behaviors, we can create any number of interesting utilities. In this post we will cover binding of the Visibility property to null and non-null values.
Scenario
A useful technique is to hide a piece of UI when its DataContext is null. This is valuable when a piece of data is optional or only shown after having been lazy-loaded. In an MVVM context, UI can be conditionally shown based on the presence of a view model. The relationship between nullity and visibility is rather meaningful and provides many everyday binding opportunities.
We will encapsulate this concept in a behavior, NullVisibility. Similar to BooleanVisibility from part 1 and part 2, it will manage the translation of a nullable value into a Visibility value using attached properties:
<TextBlock
Text="An order is selected"
local:NullVisibility.Value="{Binding SelectedOrder}"
/>
We can invert the translation using the WhenNull and WhenNotNull properties:
<TextBlock
Text="No order is selected"
local:NullVisibility.Value="{Binding SelectedOrder}"
local:NullVisibility.WhenNull="Visible"
local:NullVisibility.WhenNotNull="Collapsed"
/>
NullVisibility
We define the attached behavior as a static class:
public static class NullVisibility
Next, we register the attached properties which govern the behavior. First is the Value property, which we give a default value of true to match the UIElement.Visibility property’s default value of Visible. We also create static accessors to facilitate XAML usage:
public static readonly DependencyProperty ValueProperty =
DependencyProperty.RegisterAttached(
"Value",
typeof(object),
typeof(NullVisibility),
new PropertyMetadata(true, OnArgumentsChanged));
public static object GetValue(UIElement uiElement)
{
return uiElement.GetValue(ValueProperty);
}
public static void SetValue(UIElement uiElement, object value)
{
uiElement.SetValue(ValueProperty, value);
}
Next, we register the WhenNull property, giving it a default value of Collapsed as the opposite of Value‘s default:
public static readonly DependencyProperty WhenNullProperty =
DependencyProperty.RegisterAttached(
"WhenNull",
typeof(Visibility),
typeof(NullVisibility),
new PropertyMetadata(Visibility.Collapsed, OnArgumentsChanged));
public static Visibility GetWhenNull(UIElement uiElement)
{
return (Visibility) uiElement.GetValue(WhenNullProperty);
}
public static void SetWhenNull(UIElement uiElement, Visibility visibility)
{
uiElement.SetValue(WhenNullProperty, visibility);
}
Finally, we register the WhenNotNull property, giving it a default value of Visible to match Value‘s default:
public static readonly DependencyProperty WhenNotNullProperty =
DependencyProperty.RegisterAttached(
"WhenNotNull",
typeof(Visibility),
typeof(NullVisibility),
new PropertyMetadata(Visibility.Visible, OnArgumentsChanged));
public static Visibility GetWhenNotNull(UIElement uiElement)
{
return (Visibility) uiElement.GetValue(WhenNotNullProperty);
}
public static void SetWhenNotNull(UIElement uiElement, Visibility visibility)
{
uiElement.SetValue(WhenNotNullProperty, visibility);
}
Behavior
Just like with BooleanVisibility, we declare the behavior, telling it how to create instances for individual host objects:
private static readonly AttachedBehavior Behavior =
AttachedBehavior.Register(host => new NullVisibilityBehavior(host));
Now, we update it whenever any of the above dependency properties changes:
private static void OnArgumentsChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
{
Behavior.Update(d);
}
All that plumbing is about managing behavior instances. The real work is done in the IBehavior implementation, which is straightforward for NullVisibility:
private sealed class NullVisibilityBehavior : Behavior<UIElement>
{
internal NullVisibilityBehavior(DependencyObject host) : base(host)
{}
protected override void Update(UIElement host)
{
host.Visibility = GetValue(host) == null
? GetWhenNull(host)
: GetWhenNotNull(host);
}
}
Sample Project
This WPF application shows NullVisibility in action:
Attached Behaviors Part 3 NullVisibility.zip
It allows you to set the value of the BooleanVisibility.Value attached property on two different text blocks. The first shows when Value is not null. The second uses the WhenNull and WhenNotNull attached properties to show when Value is null instead.
Summary
There is not a lot of code behind NullVisibility. It isn’t exactly terse (a tax for working with attached properties), but none of the pieces is complex or onerous. Writing attached behaviors does not have to be a chore.
Next time, we will explore some of these ideas using enumerations instead of Boolean values.