The DependencyProperty class enables us to register one further, special type of Dependency Property. These properties are like the Extension Methods of XAML, as they enable us to extend existing classes with our own functionality. They are of course, Attached Properties.
We've already seen some examples of them earlier in this book and we'll see further examples later, but in this chapter, we'll cover their registration. We can declare Attached Properties in exactly the same ways that we can create Dependency Properties and have all of the same various options of setting metadata and attaching handlers.
There are several overloads of the RegisterAttached and RegisterAttachedReadOnly methods that mirror the Register and RegisterReadOnly methods in input parameters and functionality. However, instead of declaring a CLR wrapper for our Attached Properties, we are required to declare a pair of getter and setter methods to access and set their values. Let's see another example from the TextBoxProperties class:
public static DependencyProperty IsFocusedProperty = DependencyProperty.RegisterAttached("IsFocused", typeof(bool), typeof(TextBoxProperties), new PropertyMetadata(false, OnIsFocusedChanged)); public static bool GetIsFocused(DependencyObject dependencyObject) { return (bool)dependencyObject.GetValue(IsFocusedProperty); } public static void SetIsFocused(DependencyObject dependencyObject, bool value) { dependencyObject.SetValue(IsFocusedProperty, value); } public static void OnIsFocusedChanged(DependencyObject dependencyObject, DependencyPropertyChangedEventArgs e) { TextBox textBox = dependencyObject as TextBox; if ((bool)e.NewValue && !(bool)e.OldValue && !textBox.IsFocused) textBox.Focus(); }
Here, we have the declaration of a bool Attached Property named IsFocused with a PropertyMetadata element that specifies a default value and a PropertyChangedCallback handler. Like the CLR property wrappers for Dependency Properties, these getter and setter methods will not be called by the WPF Framework. They are typically declared both public and static.
However, there is one situation where we do not need to declare these methods as public. If we want to create a Dependency Property whose value can be inherited by its children, then we should declare it using the RegisterAttached method, even if we don't require an Attached Property. In this situation, we are not required to publicly expose our property getter and setter.
Although we can specify the FrameworkPropertyMetadataOptions.Inherits metadata option upon the declaration of Dependency Properties and their value inheritance might work in some situations, it is not guaranteed in other situations. As Attached Properties are global properties in the property system, we can be assured that their property value inheritance will work in all situations.
Returning to our example, our PropertyChangedCallback handler is a simple affair. It casts the dependencyObject property to the type of control that the property is attached to, in this case, a TextBox. It then verifies that the data bound bool value has been set from false to true and that the control is not already focused. If these conditions are verified, the control is then focused.
This Attached Property can be data bound to a bool property in a View Model like this:
xmlns:Attached="clr-namespace:CompanyName.ApplicationName.Views.Attached" ... <TextBox Attached:TextBoxProperties.IsFocused="{Binding IsFocused}" Text="{Binding User.Name}" />
The attached TextBox control can then be focused from the View Model at any time using this following method:
private void Focus() { IsFocused = false; IsFocused = true; }
Note that we need to ensure that the variable is false before setting it to true, as it is the actual changing of the value that will trigger the control to become focused. Now that we know how to declare our own custom Dependency Properties, let's turn our attention to the rules that govern the way they are set.