Last time, we explored the concept of a user making a choice between various enumeration values. We implemented the choice as a set of radio buttons, where each represents a different value and all are bound to the same property. Radio buttons are one of many controls which allow the user to select from various choices. There is a family of controls, such as ComboBox and ListBox, which inherit their selection capabilities from a common primitive, the Selector control. In this installment, we will write an attached behavior to select an enumeration value from any implementation of Selector.

Scenario

Selectors have two important elements: a collection of child objects, and a currently selected object out of that collection. This mimics a choice between enumeration values quite nicely. For example, a checkout form might offer multiple payment methods, such a credit card or PayPal, and allow the user to choose with a combo box. Here, we can use enumeration matching to synchronize a property with the selected item:

The combo box’s selected value is bound to the PaymentType property and each item is associated with an enumeration value. When the user changes the selection, the attached behavior writes the selected value to the PaymentType property through a two-way binding.

NOTE: The current incarnation of this behavior requires a ComboBoxItem for each value. This is because the items in the selector must have the ItemValue attached property set directly on them. If we place, say, a TextBox directly within the ComboBox, and set the ItemValue property on it, the ComboBox will generate a ComboBoxItem for us, but it won’t have the ItemValue property set. We might be able to check the Content property of each ComboBoxItem for ItemValue, but that is for a future iteration.

EnumSelector

Once again, we define the behavior as a static class:

publicstaticclassEnumSelector

Next, we register the attached properties which govern the behavior. First is the SelectedValue property, which can contain any enumeration value and thus needs to be typed as object. We also create static accessors to facilitate XAML usage:

publicstaticreadonlyDependencyProperty SelectedValueProperty =

DependencyProperty.RegisterAttached(

"SelectedValue",

typeof(object),

typeof(EnumSelector),

newPropertyMetadata(OnSelectedValueChanged));

publicstaticobject GetSelectedValue(Selector selector)

{

return selector.GetValue(SelectedValueProperty);

}

publicstaticvoid SetSelectedValue(Selector selector, object value)

{

selector.SetValue(SelectedValueProperty, value);

}

Then, we register the ItemValue property, which is a simple string we will parse later:

publicstaticreadonlyDependencyProperty ItemValueProperty =

DependencyProperty.RegisterAttached(

"ItemValue",

typeof(string),

typeof(EnumSelector),

newPropertyMetadata(OnItemValueChanged));

publicstaticstring GetItemValue(DependencyObject item)

{

return (string) item.GetValue(ItemValueProperty);

}

publicstaticvoid SetItemValue(DependencyObject item, string value)

{

item.SetValue(ItemValueProperty, value);

}

Behavior

The nice part about the attached behavior framework is that most of the code which goes into behaviors is fairly boilerplate. First, we declare the behavior and tell it how to create instances for individual host objects:

privatestaticreadonlyAttachedBehavior Behavior =

AttachedBehavior.Register(host => newEnumSelectorBehavior(host));

Then, we update it when the SelectedValue dependency property changes:

The behavior implementation is also more involved. The previous enumeration matching behaviors each had one match to perform; this behavior, however, has one match per item. This adds a some complexity but not much.

The class declaration is similar to other behaviors:

privatesealedclassEnumSelectorBehavior : Behavior<Selector>

As is the constructor:

internal EnumSelectorBehavior(DependencyObject host) : base(host)

{}

The first major difference is that we store an index associating items with their enumeration checks:

privatereadonlyIDictionary<object, EnumCheck> _itemEnumChecks =

newDictionary<object, EnumCheck>();

The Attach and Detach overrides manage a handler for the selector’s SelectionChanged event:

protectedoverridevoid Attach(Selector host)

{

host.SelectionChanged += OnSelectionChanged;

}

protectedoverridevoid Detach(Selector host)

{

host.SelectionChanged -= OnSelectionChanged;

_itemEnumChecks.Clear();

}

In the Detach method, we make sure to remove all references to items and their associated enumeration checks.

The Update method goes through all of the items, updates the enumeration check associated with each one, and sets the selector’s SelectedIndex property to the first one which matches:

protectedoverridevoid Update(Selector host)

{

var selectedValue = GetSelectedValue(host);

for(var index = 0; index < host.Items.Count; index++)

{

var item = host.Items[index];

var itemEnumCheck = GetItemEnumCheck(item);

itemEnumCheck.Update(selectedValue, GetItemValue(item));

if(itemEnumCheck.IsMatch)

{

host.SelectedIndex = index;

break;

}

}

}

The GetItemEnumCheck method gets the EnumCheck associated with the specified item. If one doesn’t yet exist, we create and cache it:

privateEnumCheck GetItemEnumCheck(object item)

{

EnumCheck itemEnumCheck;

if(!_itemEnumChecks.TryGetValue(item, out itemEnumCheck))

{

itemEnumCheck = newEnumCheck();

_itemEnumChecks[item] = itemEnumCheck;

}

return itemEnumCheck;

}

The GetItemValue method is a bit of glue which attempts to convert an item to a DependencyObject and, if successful, accesses its ItemValue attached property:

privatestaticstring GetItemValue(object item)

{

var dependencyObjectItem = item asDependencyObject;

return dependencyObjectItem == null

? null

: EnumSelector.GetItemValue(dependencyObjectItem);

}

These methods implement the updating of the selector based on the SelectedValue and ItemValue attached properties.

The other side of the coin, updating the SelectedValue attached property when the selector changes, is implemented by the OnSelectionChanged event handler. We again use the TryUpdate method, passing it the name of another method, UpdateSelectedValue, which will only be invoked if the host object hasn’t been garbage-collected:

The UpdateSelectedValue method updates the enumeration check associated with the selected item and updates the SelectedValue attached property of the host with the selected item’s value:

privatevoid UpdateSelectedValue(Selector host)

{

var selectedValue = GetSelectedValue(host);

var itemEnumCheck = GetItemEnumCheck(host.SelectedItem);

itemEnumCheck.Update(selectedValue, GetItemValue(host.SelectedItem));

var parsedTargetValue = itemEnumCheck.ParsedTargetValue;

if(selectedValue != null && !selectedValue.Equals(parsedTargetValue))

{

SetSelectedValue(host, parsedTargetValue);

}

}

The if statement prevents an infinite loop where an update to the SelectedValue property raises the SelectionChanged event which updates the SelectedValue property which raises the SelectionChanged event, etc.

Sample Project

It allows you to set the value of the EnumSelector.SelectedValue attached property and see the results when applied to a ListBox. You can also selected different items in the ListBox and see EnumSelector.SelectedValue change, showing the effect of the two-way binding.

Summary

We used enumeration matching to allow the user to select an enumeration value from many. Like radio buttons, selectors naturally represent a choice between mutually exclusive values. The behavior can be applied to anything which implements Selector, offering a large amount of flexibility and extensibility.

What Next?

These are all the attached behaviors I have for now. They cover the various situations I have encountered in my XAML-related career, mostly in an MVVM context. I am sure I will discover more, though, so I will post write-ups when I do.

My personal utilities library, Cloak, contains the framework as well as all the behaviors featured in this series, for both WPF and Silverlight. (There is a lot of other goodness in there as well.) Download it and give them a try!

(If you found this series useful or maybe even enjoyed it, leave a comment. It’s always nice to be reminded I’m not writing in a void.)

Our framework for matching enumeration values has helped us easily manage the Visibility and IsEnabled properties of individual controls. Now, we will write an attached behavior to select a value from a set of radio buttons.

Scenario

Radio buttons naturally represent an enumeration: as a related set of controls, known as a group, each is a value in a mutually exclusive selection. For example, a checkout form might offer multiple payment methods, such a credit card or PayPal, and allow the user to choose with radio buttons. Here, we can use enumeration matching to synchronize a property with the selected radio button:

<RadioButton

Content="Credit card"

local:EnumGroup.Value="{Binding PaymentType, Mode=TwoWay}"

local:EnumGroup.TargetValue="CreditCard"

/>

<RadioButton

Content="PayPal"

local:EnumGroup.Value="{Binding PaymentType, Mode=TwoWay}"

local:EnumGroup.TargetValue="PayPal"

/>

Each radio button is associated with an enumeration value. When the user selects an option, it writes that value to the PaymentType property through a two-way binding.

EnumGroup

If you have read the rest of the series, there isn’t much new here. We define the behavior as a static class:

publicstaticclassEnumGroup

Next, we register the attached properties which govern the behavior. First is the Value property, which can contain any enumeration value and thus needs to be typed as object. We also create static accessors to facilitate XAML usage:

publicstaticreadonlyDependencyProperty ValueProperty =

DependencyProperty.RegisterAttached(

"Value",

typeof(object),

typeof(EnumGroup),

newPropertyMetadata(OnArgumentsChanged));

publicstaticobject GetValue(RadioButton radioButton)

{

return radioButton.GetValue(ValueProperty);

}

publicstaticvoid SetValue(RadioButton radioButton, object value)

{

radioButton.SetValue(ValueProperty, value);

}

Then, we register the TargetValue property, which is a simple string we will parse later:

The behavior class is more interesting than those for other behaviors because it overrides the Attach and Detach methods to manage a handler for the Checked event:

privatesealedclassEnumGroupBehavior : Behavior<RadioButton>

{

privatereadonlyEnumCheck _enumCheck = newEnumCheck();

internal EnumGroupBehavior(DependencyObject host) : base(host)

{}

protectedoverridevoid Attach(RadioButton host)

{

host.Checked += OnChecked;

}

protectedoverridevoid Detach(RadioButton host)

{

host.Checked -= OnChecked;

}

protectedoverridevoid Update(RadioButton host)

{

_enumCheck.Update(GetValue(host), GetTargetValue(host));

host.IsChecked = _enumCheck.IsMatch;

}

privatevoid OnChecked(object sender, RoutedEventArgs e)

{

TryUpdate(host => SetValue(host, _enumCheck.ParsedTargetValue));

}

}

The Update method, called whenever Value or TargetValue changes (via OnArgumentsChanged), is straightforward. We leverage the EnumCheck class to manage the parsing, caching, and matching. Then, we set the IsChecked property based on whether Value matches TargetValue.

Another interesting aspect of this behavior is the handler for the radio button’s Checked event. This is the first time we have a chance to use the TryUpdate method, which executes the specified lambda expression only if the host has not been garbage-collected.

The Checked event occurs when the radio button becomes the selected value in its group. When that happens, we set the Value property to the associated target value. Voila!

Sample Project

It allows you to set the value of the EnumGroup.Value attached property and see the results when applied to two radio buttons. You can also check the radio buttons and see EnumGroup.Value change, showing the effect of the two-way binding.

As a side note, the EnumCheck class evolved since the last post. It now performs the duties of EnumTargetValue, which no longer exists. It also has a new member, ParsedTargetValue, which we used in the OnChecked method above. It returns the first value from ParsedTargetValues, which may contain multiple values if TargetValue is set to a comma-separated string.

Summary

We used enumeration matching to put another fundamental building block in place, the radio button group. Their mutually exclusive nature is a seamless fit for selecting one value from many. By associating each radio button with an enumeration value, we easily and intuitively described a choice.

Next time, we will use selectors, such as a combo boxes and list boxes, to select enumeration values.

Last time, we created a small framework for matching enumeration values. With this very handy technique in our arsenal, we can start interpreting enumeration properties in various ways.

Scenario

A common situation is the need to enable and disable related sets of controls. For example, we might disable credit card fields if a user chooses the PayPal option on a checkout form (and vice versa). In these cases, we can use enumeration matching to manage the UIElement.IsEnabled property:

<StackPanel>

<local:PayPalOptions

local:EnumIsEnabled.Value="{Binding PaymentType}"

local:EnumIsEnabled.TargetValue="PayPal"

/>

<local:CreditCardOptions

local:EnumIsEnabled.Value="{Binding PaymentType}"

local:EnumIsEnabled.TargetValue="CreditCard"

/>

</StackPanel>

Both of the payment options are governed by the PaymentType property. They are mutually exclusive because only one target value will match at a time.

Like with other enumeration-based behaviors, we can invert the results of the match using the WhenMatched and WhenNotMatched properties:

<local:CreditCardOptions

local:EnumIsEnabled.Value="{Binding PaymentType}"

local:EnumIsEnabled.TargetValue="CreditCard"

local:EnumIsEnabled.WhenMatched="False"

local:EnumIsEnabled.WhenNotMatched="True"

/>

EnumIsEnabled

The naming for this concept is a little fuzzy. My first thought, EnumEnabler, uses a term most commonly associated with addictive behavior. Scratch. EnumEnabled is closer, but it sounds like we are enabling something about enumerations, not setting the IsEnabled property. I ultimately decided the format EnumPropertyName is more discoverable than using an arbitrary term to complete the identifier. Naming suggestions are welcome in the comments.

The process of defining an attached behavior should be very familiar by now. We start by declaring a static class:

publicstaticclassEnumIsEnabled

Next, we register the attached properties which govern the behavior. First is the Value property, which can contain any enumeration value and thus needs to be typed as object. We also create static accessors to facilitate XAML usage:

publicstaticreadonlyDependencyProperty ValueProperty =

DependencyProperty.RegisterAttached(

"Value",

typeof(object),

typeof(EnumIsEnabled),

newPropertyMetadata(OnArgumentsChanged));

publicstaticobject GetValue(UIElement uiElement)

{

return uiElement.GetValue(ValueProperty);

}

publicstaticvoid SetValue(UIElement uiElement, object value)

{

uiElement.SetValue(ValueProperty, value);

}

Next, we register the TargetValue property, which is a simple string we will parse later:

publicstaticreadonlyDependencyProperty TargetValueProperty =

DependencyProperty.RegisterAttached(

"TargetValue",

typeof(string),

typeof(EnumIsEnabled),

newPropertyMetadata(OnArgumentsChanged));

publicstaticstring GetTargetValue(UIElement uiElement)

{

return (string) uiElement.GetValue(TargetValueProperty);

}

publicstaticvoid SetTargetValue(UIElement uiElement, string value)

{

uiElement.SetValue(TargetValueProperty, value);

}

Then, we register the WhenMatched property, giving it a default value of true:

publicstaticreadonlyDependencyProperty WhenMatchedProperty =

DependencyProperty.RegisterAttached(

"WhenMatched",

typeof(bool),

typeof(EnumIsEnabled),

newFrameworkPropertyMetadata(true, OnArgumentsChanged));

publicstaticbool GetWhenMatched(UIElement uiElement)

{

return (bool) uiElement.GetValue(WhenMatchedProperty);

}

publicstaticvoid SetWhenMatched(UIElement uiElement, bool value)

{

uiElement.SetValue(WhenMatchedProperty, value);

}

Finally, we register the WhenNotMatched property, giving it a default value of false:

publicstaticreadonlyDependencyProperty WhenNotMatchedProperty =

DependencyProperty.RegisterAttached(

"WhenNotMatched",

typeof(bool),

typeof(EnumIsEnabled),

newFrameworkPropertyMetadata(false, OnArgumentsChanged));

publicstaticbool GetWhenNotMatched(UIElement uiElement)

{

return (bool) uiElement.GetValue(WhenNotMatchedProperty);

}

publicstaticvoid SetWhenNotMatched(UIElement uiElement, bool value)

{

uiElement.SetValue(WhenNotMatchedProperty, value);

}

Behavior

As in the previous installments in this series, we declare the behavior, telling it how to create instances for individual host objects:

privatestaticreadonlyAttachedBehavior Behavior =

AttachedBehavior.Register(host => newEnumIsEnabledBehavior(host));

Now, we update it whenever any of the above dependency properties changes:

Sample Project

It allows you to set the value of the EnumIsEnabled.Value attached property on three different text blocks. The first shows when Value matches TargetValue. The second shows when Value matches a comma-separated TargetValue. The third uses the WhenMatched and WhenNotMatched attached properties to show when Value does not match TargetValue instead.

Summary

Except for the reference to the IsEnabled property, we didn’t really write anything new for this attached behavior. We only have to write code at all because the Value, TargetValue, WhenMatched, and WhenNotMatched properties must be scoped to a specific class in the XAML. This, along with the fact that WhenMatched and WhenNotMatched can have different types, is why we redeclare the properties on each new behavior.

Next time, we will use a group of radio buttons to allow the user to select enumeration values.

Another convenient behavior is to conditionally show a piece of UI based on the value of an enumeration property. This is especially useful for working with view models in an MVVM context, where enumerations are a common property type.

The input to this behavior is a value and a target value. The host is shown/hidden based on whether they match:

<TextBlock

Text="Access denied"

local:EnumVisibility.Value="{Binding UserType}"

local:EnumVisibility.TargetValue="Standard"

/>

We can specify multiple target values to compare against the Value property:

<TextBlock

Text="Access granted"

local:EnumVisibility.Value="{Binding UserType}"

local:EnumVisibility.TargetValue="Moderator, Administrator"

/>

We can also invert the translation using the WhenMatched and WhenNotMatched properties:

<TextBlock

Text="Access granted"

local:EnumVisibility.Value="{Binding UserType}"

local:EnumVisibility.TargetValue="Standard"

local:EnumVisibility.WhenMatched="Collapsed"

local:EnumVisibility.WhenNotMatched="Visible"

/>

An especially nice use is specifying user-friendly names for enumeration values (a traditionally thorny problem). We bind a bank of controls in the same space to the same property, knowing only one will show up at any given time. This gives us maximum flexibility in representing particular enumeration values:

<Grid>

<TextBlock

Text="Standard"

local:EnumVisibility.Value="{Binding UserType}"

local:EnumVisibility.TargetValue="Standard"

/>

<TextBlock

Text="Moderator"

local:EnumVisibility.Value="{Binding UserType}"

local:EnumVisibility.TargetValue="Moderator"

FontWeight="Bold"

/>

<StackPanel

local:EnumVisibility.Value="{Binding UserType}"

local:EnumVisibility.TargetValue="Administrator"

Orientation="Horizontal">

<TextBlock Text="Administrator" FontWeight="Bold" />

<Image Source="admin.jpg" />

</StackPanel>

</Grid>

This allows us to use bindings, resources, localization, and any other technique to portray user-friendly names.

EnumVisibility

We define the attached behavior as a static class:

publicstaticclassEnumVisibility

Next, we register the attached properties which govern the behavior. First is the Value property, which can contain any enumeration value and thus needs to be typed as object. We also create static accessors to facilitate XAML usage:

publicstaticreadonlyDependencyProperty ValueProperty =

DependencyProperty.RegisterAttached(

"Value",

typeof(object),

typeof(EnumVisibility),

newPropertyMetadata(OnArgumentsChanged));

publicstaticobject GetValue(UIElement uiElement)

{

return uiElement.GetValue(ValueProperty);

}

publicstaticvoid SetValue(UIElement uiElement, object value)

{

uiElement.SetValue(ValueProperty, value);

}

Next, we register the TargetValue property, which is a simple string we will parse later:

publicstaticreadonlyDependencyProperty TargetValueProperty =

DependencyProperty.RegisterAttached(

"TargetValue",

typeof(string),

typeof(EnumVisibility),

newPropertyMetadata(OnArgumentsChanged));

publicstaticstring GetTargetValue(UIElement uiElement)

{

return (string) uiElement.GetValue(TargetValueProperty);

}

publicstaticvoid SetTargetValue(UIElement uiElement, string value)

{

uiElement.SetValue(TargetValueProperty, value);

}

Then, we register the WhenMatched property, giving it a default value of Visible:

The IBehavior implementation is similar to the other attached behaviors:

privatesealedclassEnumVisibilityBehavior : Behavior<UIElement>

{

privatereadonlyEnumCheck _enumCheck = newEnumCheck();

internal EnumVisibilityBehavior(DependencyObject host) : base(host)

{}

protectedoverridevoid Update(UIElement host)

{

_enumCheck.Update(GetValue(host), GetTargetValue(host));

host.Visibility = _enumCheck.IsMatch

? GetWhenMatched(host)

: GetWhenNotMatched(host);

}

}

The major difference is this behavior uses an object dedicated to checking the value against the target value. This cleans up the code considerably, as the matching logic is not trivial. It also allows us to reuse it in other enumeration-related behaviors.

Enumeration Checks

An enumeration check requires parsing of the target value(s) to the enumeration type. Rather than perform the parse every time, the EnumCheck type caches the parsed target values and only recalculates them when necessary. This stateful nature is why we update the _enumCheck field and then query its IsMatch property when updating the behavior.

The EnumCheck class has three properties:

publicobject Value { get; privateset; }

publicEnumTargetValue TargetValue { get; privateset; }

publicbool IsMatch { get; privateset; }

The interesting thing here is the TargetValue property: the EnumTargetValue class encapsulates the parsing and comparison of target value strings. We initialize it in the constructor to an empty instance:

public EnumCheck()

{

this.TargetValue = newEnumTargetValue();

}

The Update method, which we call from the behavior’s Update method above, is where we coordinate the match:

publicvoid Update(object value, string targetValue)

{

var valueChanged = value != this.Value;

var targetValueChanged = targetValue != this.TargetValue.Text;

if(valueChanged || targetValueChanged)

{

if(valueChanged)

{

this.Value = value;

}

this.TargetValue.Update(this.Value, targetValue);

this.IsMatch = this.TargetValue.Matches(this.Value);

}

}

We determine if either the value or target has changed and, if so, recalculate the target value and the match result. EnumTargetValue performs the core match logic.

Target Values

Using commas, a target value can actually be composed of multiple enumeration values. We represent this in the EnumTargetValue class by exposing a read-only wrapper of a list of parsed values:

privatereadonlyList<object> _parsedValues = newList<object>();

public EnumTargetValue()

{

this.ParsedValues = _parsedValues.AsReadOnly();

}

publicReadOnlyCollection<object> ParsedValues { get; privateset; }

publicstring Text { get; privateset; }

This list is the cache for the enumeration values we parse from the target value string. It is typed as object because we don’t have compile-time knowledge of the type of the bound enumeration – we wait to see the type of the value bound to the EnumVisibility.Value property and use that type to do the parsing.

(Later in this series, we will see why we make the parsed values publicly available.)

We also expose the source text, so we can check to see if it has changed in the beginning of the EnumCheck.Update method above. That method also calls the Update method on EnumTargetValue, which caches the text and repopulates the _parsedValues list:

publicvoid Update(object value, string text)

{

this.Text = text;

ParseValues(value);

}

In order to parse the text, we need the enumeration value against which it will be compared, which we use to determine the type to pass to the Enum.Parse method. By inferring the enumeration type like this, we avoid requiring developers to specify another attached property alongside Value and TargetValue.

The ParseValues method parses each token in a comma-separated string into an enumeration value of the same type as the bound value:

privatevoid ParseValues(object value)

{

_parsedValues.Clear();

if(value != null && value isEnum && !String.IsNullOrEmpty(this.Text))

{

var parsedValues =

from targetValue inthis.Text.Split(‘,’)

let trimmedTargetValue = targetValue.Trim()

where trimmedTargetValue.Length > 0

selectEnum.Parse(value.GetType(), trimmedTargetValue, false);

_parsedValues.AddRange(parsedValues);

}

}

First, we ensure the bound value is an enumeration and there is target value text to parse. Then, we tokenize the string by commas and perform a query over the resulting token. For each, we trim it, ensure it isn’t empty, and parse it to the enumeration type using a case-sensitive comparison. This gives us a sequence of enumeration values which we then add to the _parsedValues list.

Now that we have the set of parsed values, we have to compare it to the bound value. The EnumCheck.Update method above sets the EnumCheck.IsMatch property by calling the EnumTargetValue.Matches method:

publicbool Matches(object value)

{

return value == null || value.Equals("")

? String.IsNullOrEmpty(this.Text)

: _parsedValues.Contains(value);

}

A null or empty bound value matches a null or empty target value, neatly supporting nullable enumerations without actually having to know the enumeration type. If a value is provided, we determine if it exists in the set of parsed values. The Contains call will ultimately use the GetHashCode and Equals methods on the bound value and each parsed value, shouldering the bulk of the equality-checking work.

Sample Project

It allows you to set the value of the EnumVisibility.Value attached property on three different text blocks. The first shows when Value matches TargetValue. The second shows when Value matches a comma-separated TargetValue. The third uses the WhenMatched and WhenNotMatched attached properties to show when Value does not match TargetValue instead.

Summary

Enumeration matching is useful in many scenarios. The incarnation here, which manages the Visibility property, lays the foundation for enumeration-based behaviors.

Next time, we will use the EnumCheck class to manage the IsEnabled property.

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:

publicstaticclassNullVisibility

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:

publicstaticreadonlyDependencyProperty ValueProperty =

DependencyProperty.RegisterAttached(

"Value",

typeof(object),

typeof(NullVisibility),

newPropertyMetadata(true, OnArgumentsChanged));

publicstaticobject GetValue(UIElement uiElement)

{

return uiElement.GetValue(ValueProperty);

}

publicstaticvoid 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:

Sample Project

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.

Last time, we devised a simpler syntax for defining attached behaviors in WPF and Silverlight. The new system approaches the concept at a higher level, encapsulating many of the rote mechanics common to the various implementations found on the web. It aims to make attached behaviors an everyday option, using an approach similar to many core WPF systems.

Now, we will flesh out the framework behind the API, which models three distinct aspects of attached behaviors:

Existence: Declare an object representing the attached behavior

Lifecycle: Attach and detach behavior instances to/from host objects

Logic: Update host objects based on changes in bound values

See the end of this post for a solution containing the framework and a usage sample.

Existence

Attached behaviors, similar to dependency properties, are represented by static fields:

The registration of the behavior of the BooleanVisibility class from part 1

The Register method creates an instance of AttachedBehavior, which encapsulates the ability to attach the behavior to host objects. The lambda expression indicates how to create instances of the behavior when required.

Each attached behavior needs its own dependency property to store the IBehavior instance on each host. Rather than require behavior writes to register properties, we (carefully) create them programmatically:

privatestaticDependencyProperty RegisterProperty()

{

returnDependencyProperty.RegisterAttached(

GetPropertyName(),

typeof(IBehavior),

typeof(AttachedBehavior));

}

privatestaticstring GetPropertyName()

{

return"_" + Guid.NewGuid().ToString("N");

}

First, we generate a unique name for the property by using the alphanumeric representation of a GUID (specified by the “N” format string). We prefix the property name with an underscore to ensure a legal identifier.

Then, we register an attached property with the unique name, indicating that its type is IBehavior and that AttachedBehavior owns it. Having all properties owned by the same type hides the use of attached properties completely from behavior authors.

The handler for dependency property changes in the BooleanVisibility class of part 1

Update manages the association between the host object and an instance of the behavior. This involves three operations:

Attach – Create a behavior instance and associate it with the host object

Update – Synchronize the behavior with the current state of the host object

Detach – Remove the association when the behavior no longer applies to the host object

In concrete terms, attach and detach refer to setting and clearing the value of the dependency property we generated in the Register call. This discreetly stores the behavior instance directly within the host.

Update is straightforward to implement:

publicvoid Update(DependencyObject host)

{

var behavior = (IBehavior) host.GetValue(_property);

if(behavior == null)

{

TryCreateBehavior(host);

}

else

{

UpdateBehavior(host, behavior);

}

}

privatevoid TryCreateBehavior(DependencyObject host)

{

var behavior = _behaviorFactory(host);

if(behavior.IsApplicable())

{

behavior.Attach();

host.SetValue(_property, behavior);

behavior.Update();

}

}

privatevoid UpdateBehavior(DependencyObject host, IBehavior behavior)

{

if(behavior.IsApplicable())

{

behavior.Update();

}

else

{

host.ClearValue(_property);

behavior.Detach();

}

}

Before attaching or updating the behavior, we call IsApplicable to ensure it is relevant. This is the time to check things like whether dependency properties have values or whether a control is visible. For example, we may not want to attach a behavior to a combo box if it has no items.

Implementers of IBehavior supply the core logic for each of the operations:

publicinterfaceIBehavior

{

bool IsApplicable();

void Attach();

void Update();

void Detach();

}

A behavior is assumed to have access to the host object, as shown in the Register call where we pass the host to the constructor of BooleanVisibilityBehavior.

Logic

To minimize friction, we should allow behavior authors to work with a strongly-typed host object. We can enable this by allowing them to specify the host type as a type parameter and maintaining the untyped weak reference ourselves:

The GetHost method is the bridge between the weak reference and the typed API. We keep it private because derived classes won’t need it; instead, we pass them the host as a parameter in the method for each operation:

protectedvirtualbool IsApplicable(THost host)

{

returntrue;

}

protectedvirtualvoid Attach(THost host)

{}

protectedvirtualvoid Detach(THost host)

{}

protectedabstractvoid Update(THost host);

This is the core API for authoring a new behavior. Only the Update method is abstract; a behavior might not define specific logic for applicability or attaching/detaching, but it exists to respond to changes in the host.

AttachedBehavior calls them from the IBehavior methods, which ensure the host hasn’t been garbage-collected:

publicbool IsApplicable()

{

var host = GetHost();

return host != null && IsApplicable(host);

}

publicvoid Attach()

{

var host = GetHost();

if(host != null)

{

Attach(host);

}

}

publicvoid Detach()

{

var host = GetHost();

if(host != null)

{

Detach(host);

}

}

publicvoid Update()

{

var host = GetHost();

if(host != null)

{

Update(host);

}

}

Revisiting BooleanVisibilityBehavior

The AttachedBehavior and Behavior<> types implement most of the mechanics of an attached behavior. They factor away the nitty gritty and leave behavior authors with a straightforward implementation:

We declare the host type in the generic parameter to Behavior<>. We accept the host instance in the constructor and simply pass it through to the base class. When it comes time to update the host, it is cast to the generic type and passed to the Update method. We use the accessors for the Value, WhenTrue, and WhenFalse attached properties to determine the host’s new visibility.

(In part 1 of this series, we also listened to changes in the Visibility property and updated the Value property to match. This turned out to be more complex than expected, and so I have left that out of this iteration of the framework. The Attach/Detach methods, normally used to manage event handlers, weren’t necessary in this example.

In the majority of cases, these behaviors will manipulate a control property based on attached properties; two-way binding, a valuable but less applicable scenario, will be covered in a future post.)

Sample Project

It contains all of the code above and allows you to set the value of the BooleanVisibility.Value attached property on two different text blocks. The first shows when Value is true. The second uses the WhenTrue and WhenFalse attached properties to show when Value is false instead.

Summary

Different attached behaviors have very little that is unique about them. Behavior<> brings these elements to the forefront and, along with AttachedBehavior, takes care of the bookkeeping.

This core system lowers the barrier to entry for new attached behaviors, opening the door for many other ideas. Future posts in this series will cover:

The DependencyObject system is at the core of the WPF and Silverlight architectures. It enables many killer features of UI development, including data binding, animation, and an overall declarative style. It is the fruit of years spent developing UI frameworks, addressing the entire idea on a more fundamental level.

One of the more interesting features is attached properties, which allow external entities to store pieces of data within dependency objects. A grid stores an object’s row and column, a canvas stores an object’s coordinates, and a docking container stores dock position. The objects remain blissfully unaware of the orthogonal features.

A particularly useful form of these properties is the attached behavior, an attached property which adds functionality to the object on which it is set. This is similar in effect to extension methods: behaviors can implement anything which uses an object’s public API. They can respond to events, such as TextChanged and Checked/Unchecked, as well as use additional attached properties to refine functionality.

Each of these also feels heavy. The approach with attached properties has less friction:

<TextBlock

Text="An error occurred."

local:BooleanVisibility.Value="{Binding HasError}"

local:BooleanVisibility.WhenTrue="Collapsed"

local:BooleanVisibility.WhenFalse="Visible"

/>

This neatly addresses WPF’s three-state Visibility enumeration by forcing a choice between Collapsed and Hidden. The Silverlight version works the same way, sans the Hidden option.

BooleanVisibility

Since the API is defined solely in terms of attached properties, we define it as a static class (a nice encapsulation of the feature):

publicstaticclassBooleanVisibility

First, we register the Value property, giving it 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:

publicstaticreadonlyDependencyProperty ValueProperty =

DependencyProperty.RegisterAttached(

"Value",

typeof(bool),

typeof(BooleanVisibility),

newPropertyMetadata(true, OnArgumentChanged));

publicstaticbool GetValue(UIElement uiElement)

{

return (bool) uiElement.GetValue(ValueProperty);

}

publicstaticvoid SetValue(UIElement uiElement, bool value)

{

uiElement.SetValue(ValueProperty, value);

}

Next, we register the WhenTrue property, giving it a default value of Visible to match Value‘s default:

Attaching the Behavior

We create one additional attached property to hold the behavior associated with each object:

privatestaticreadonlyDependencyProperty BehaviorProperty =

DependencyProperty.RegisterAttached(

"Behavior",

typeof(BooleanVisibilityBehavior),

typeof(BooleanVisibility));

The fact that we use a dependency property to associate behavior with objects is an implementation detail, so we can make it private and omit the get/set methods. BooleanVisibilityBehavior, discussed later, is the class which implements the specification.

To attach the behavior, we simply set this property. A change in Value, WhenTrue, or WhenFalse is the cue to attach it if necessary and update its state. Each property registers the OnArgumentChanged method to be called when its value changes:

First, we ensure an instance of the behavior has been attached to the UI element through BehaviorProperty. Then, we update its state, which sets the Visibility property based on the values of Value, WhenTrue, and WhenFalse.

BooleanVisibilityBehavior

The behavior implements roughly the same functionality as BooleanVisibilityConverter:

A key difference is that the behavior must listen for changes in the Visibility property, while the converter is not responsible for coordination of any kind.

The behavior class is an implementation detail as well, so we can also make it private. This is convenient because the Get/Set methods for Value, WhenTrue, and WhenFalse are in scope:

privatesealedclassBooleanVisibilityBehavior

{

privatereadonlyWeakReference _uiElementReference;

internal BooleanVisibilityBehavior(UIElement uiElement)

{

_uiElementReference = newWeakReference(uiElement);

var visibilityDescriptor = DependencyPropertyDescriptor.FromProperty(

UIElement.VisibilityProperty,

uiElement.GetType());

visibilityDescriptor.AddValueChanged(uiElement, OnVisibilityChanged);

}

internalvoid Update()

{

var uiElement = (UIElement) _uiElementReference.Target;

if(uiElement != null)

{

uiElement.Visibility = GetValue(uiElement)

? GetWhenTrue(uiElement)

: GetWhenFalse(uiElement);

}

}

privatevoid OnVisibilityChanged(object sender, EventArgs e)

{

var uiElement = (UIElement) _uiElementReference.Target;

if(uiElement != null)

{

var value = uiElement.Visibility == GetWhenTrue(uiElement);

SetValue(uiElement, value);

}

}

}

We store the UI element in a WeakReference, which ensures we don’t accidentally keep it alive after it goes out of scope. Then, we use the dependency property system to add a handler for changes in the object’s Visibility property.

The Update method, which we call from the OnArgumentsChanged handler, sets the Visibility property based on the values of the argument properties. We must first ensure the UI element has not been garbage-collected.

The OnVisibilityChanged handler is the other binding direction, setting the Value property based on the value of the Visibility property.

Generalizing Attached Behaviors

The above approach is solid, but wordy. There are a lot of aspects to consider and details to get right. Writing a new attached behavior is a high-friction undertaking which can be intimidating enough to deter developers even when the effort is warranted. This concept is screaming for a framework.

Following is a proposed syntax for defining attached behaviors. It encapsulates some of the stickier points, such as the attachment logic and the weak reference. Part 2 of this series will contain the framework implementation.

First, instead of registering an attached property to hold the behavior, we raise the level of abstraction and register an attached behavior:

We tell the base class the expected type of the host and pass the instance through to its constructor; it is responsible for the weak reference. We override Update and perform the logic without worrying about any null-checking, as the base class will ensure the host has not been garbage-collected.

OnVisibilityChanged, though, also needs access to the host. To do so, we use the TryUpdate method, giving it a lambda expression which it will only execute if the host has not been garbage-collected.

That’s it! This is much more straightforward and declarative than the raw approach. The majority of the code is concerned with the behavior itself and not with managing the instance. This style is more accessible in our everyday work, where attached behaviors may simply replace some converters.

Summary

Attached behaviors are a powerful tool in the toolkit of any WPF or Silverlight developer. They neatly encapsulate the relationship between an object and the values which drive its behavior. Their implementation complexity, though, is a large deterrent to their widespread adoption. By simplifying or removing many of the details, this framework lowers the barrier to entry and clears the path for attached behaviors to become a more common technique.

Next time, we will see how to implement the AttachedBehavior and Behavior<> classes.