What are Dependency Properties in WPF #
Dependency Properties are one of the most important concepts in Windows Presentation Foundation (WPF). They are the foundation behind many of WPF’s powerful features, including data binding, styles, animations, templates, default values, and property value inheritance.
If you are coming from traditional .NET properties, Dependency Properties may initially look complicated. However, once you understand how they work and why WPF uses them, they become much easier to use.
In this tutorial, we will cover:
- What are WPF Dependency Properties?
- Why use WPF Dependency Properties?
- How to register a Dependency Property
- Dependency Property metadata
- Property changed callbacks
- A complete practical example
1. What Are WPF Dependency Properties? #
A normal C# property usually stores its value in a private field:
public class Person
{
private string _name;
public string Name
{
get => _name;
set => _name = value;
}
}
The value belongs directly to the object.
A WPF Dependency Property (DP) works differently. Instead of storing the property value directly in a normal field, WPF’s Dependency Property System manages the value.
A Dependency Property is registered with WPF’s property system and identified by a DependencyProperty object.
For example:
public static readonly DependencyProperty TitleProperty =
DependencyProperty.Register(
nameof(Title),
typeof(string),
typeof(MyControl));
The CLR property then provides a convenient wrapper:
public string Title
{
get => (string)GetValue(TitleProperty);
set => SetValue(TitleProperty, value);
}
So there are actually two related parts:
- The
DependencyPropertyfield registered with WPF. - The CLR property wrapper used by C# code.
2. Why Does WPF Need Dependency Properties? #
You might wonder:
Why doesn’t WPF simply use normal C# properties?
Normal properties work perfectly well for ordinary application classes. However, WPF controls require capabilities that normal properties do not provide efficiently.
In WPF, Dependency Properties enable features such as:
- Data binding
- Styles
- Control templates
- Animations
- Property value inheritance
- Default values
- Property change notifications
- Validation
- Property value precedence
- Design-time support
For example, you can bind a WPF control property:
<TextBox Text="{Binding UserName}" />
The Text property is a Dependency Property.
You can also style it:
<Style TargetType="Button">
<Setter Property="Background" Value="Blue"/>
</Style>
Background is also a Dependency Property.
3.Dependency Properties and Data Binding #
One of the biggest advantages of WPF Dependency Properties is their integration with WPF’s binding system.
Consider:
<TextBox Text="{Binding UserName}" />
The WPF property system manages the Text property and allows the binding engine to participate in determining its value.
This is one reason Dependency Properties are heavily used by WPF controls.
4. Dependency Properties and Styles #
Dependency Properties allow properties to be set through styles.
For example:
<Style TargetType="Button">
<Setter Property="FontSize" Value="18"/>
<Setter Property="Padding" Value="10"/>
</Style>
Every button using this style can receive those property values without explicitly setting them.
Normal CLR properties don’t provide this level of integration with WPF’s styling system.
5. Dependency Properties and Animations #
Dependency Properties also support WPF animations.
For example:
<DoubleAnimation
Storyboard.TargetProperty="Opacity"
To="0"
Duration="0:0:1"/>
The property system allows WPF to temporarily provide animated values without permanently changing the underlying value.
This is another major difference between normal CLR properties and Dependency Properties.
6. When Should You Use a Dependency Property? #
You generally create a Dependency Property when developing a:
- Custom WPF control
- Custom WPF UserControl
- Reusable WPF component
- Custom control library
For example, suppose you create a custom control called:
public class CustomerCard : Control
{
}
You might want it to expose:
public string CustomerName
If CustomerName needs to support WPF binding, styling, animation, templates, or other WPF property-system features, it should generally be a Dependency Property.
For an ordinary business model such as:
public class Customer
{
public string Name { get; set; }
}
you normally do not need a Dependency Property.
Use ordinary CLR properties for business/domain models.
7. Registering a Dependency Property #
The most common way to create a Dependency Property is by using:
DependencyProperty.Register()
The basic syntax is:
public static readonly DependencyProperty PropertyNameProperty =
DependencyProperty.Register(
nameof(PropertyName),
typeof(PropertyType),
typeof(OwnerType));
Let’s break this down.
7.1 DependencyProperty.Register #
The Register method tells WPF:
Register this property with the WPF Dependency Property system.
Example:
public static readonly DependencyProperty TitleProperty =
DependencyProperty.Register(
nameof(Title),
typeof(string),
typeof(MyControl));
Here:
nameof(Title)
is the name of the property.
typeof(string)
specifies the property’s data type.
typeof(MyControl)
specifies the class that owns the property.
8. Creating the CLR Wrapper #
After registering the Dependency Property, create a normal-looking CLR property.
public string Title
{
get => (string)GetValue(TitleProperty);
set => SetValue(TitleProperty, value);
}
The complete example becomes:
public class MyControl : Control
{
public static readonly DependencyProperty TitleProperty =
DependencyProperty.Register(
nameof(Title),
typeof(string),
typeof(MyControl));
public string Title
{
get => (string)GetValue(TitleProperty);
set => SetValue(TitleProperty, value);
}
}
Notice that we don’t use a private field:
private string _title;
Instead, WPF manages the value through:
GetValue()
and:
SetValue()
9. Using the Dependency Property in XAML #
Once the Dependency Property has been created, it can be used from XAML.
For example:
<local:MyControl
Title="Customer Information" />
You can also bind it:
<local:MyControl
Title="{Binding CustomerName}" />
And you can potentially style it:
<Style TargetType="{x:Type local:MyControl}">
<Setter Property="Title" Value="Default Title"/>
</Style>
This is where Dependency Properties become particularly powerful.
10. Dependency Property Metadata #
When registering a Dependency Property, you can provide metadata.
Metadata describes characteristics and behavior associated with the property.
For example:
public static readonly DependencyProperty TitleProperty =
DependencyProperty.Register(
nameof(Title),
typeof(string),
typeof(MyControl),
new PropertyMetadata("Default Title"));
Here:
new PropertyMetadata("Default Title")
specifies a default value.
Now, if no value is explicitly assigned, the property returns:
Default Title
11. Default Property Value #
Consider:
public static readonly DependencyProperty AgeProperty =
DependencyProperty.Register(
nameof(Age),
typeof(int),
typeof(PersonControl),
new PropertyMetadata(18));
The default value is:
18
Therefore:
int age = Age;
will return 18 if no other value has been assigned.
This is useful for custom controls because you can define sensible defaults.
12. Property Metadata and Property Changed Callback #
Metadata can also specify a callback that executes when the property value changes.
For example:
new PropertyMetadata(
"Default Title",
OnTitleChanged)
The complete registration looks like:
public static readonly DependencyProperty TitleProperty =
DependencyProperty.Register(
nameof(Title),
typeof(string),
typeof(MyControl),
new PropertyMetadata(
"Default Title",
OnTitleChanged));
Now create the callback:
private static void OnTitleChanged(
DependencyObject d,
DependencyPropertyChangedEventArgs e)
{
var control = (MyControl)d;
// Property changed logic
}
13. Understanding the Property Changed Callback #
The callback receives two important parameters:
DependencyObject d
and:
DependencyPropertyChangedEventArgs e
The d parameter represents the object whose property changed.
For example:
var control = (MyControl)d;
The e parameter contains information about the change.
You can access the old value:
e.OldValue
and the new value:
e.NewValue
For example:
private static void OnTitleChanged(
DependencyObject d,
DependencyPropertyChangedEventArgs e)
{
var control = (MyControl)d;
string oldTitle = (string)e.OldValue;
string newTitle = (string)e.NewValue;
}
14. Complete Dependency Property Example #
Let’s create a simple custom control with a Title property.
public class CustomerCard : Control
{
public static readonly DependencyProperty TitleProperty =
DependencyProperty.Register(
nameof(Title),
typeof(string),
typeof(CustomerCard),
new PropertyMetadata(
"Customer",
OnTitleChanged));
public string Title
{
get => (string)GetValue(TitleProperty);
set => SetValue(TitleProperty, value);
}
private static void OnTitleChanged(
DependencyObject d,
DependencyPropertyChangedEventArgs e)
{
var control = (CustomerCard)d;
string oldValue = (string)e.OldValue;
string newValue = (string)e.NewValue;
// React to the property change here
}
}
Now you can use the control in XAML:
<local:CustomerCard
Title="John Smith"/>
Or bind it:
<local:CustomerCard
Title="{Binding CustomerName}"/>
15. Practical Example: Changing a Visual Property #
Suppose a custom control has an IsActive Dependency Property.
When it changes, we want to update some internal behavior.
public static readonly DependencyProperty IsActiveProperty =
DependencyProperty.Register(
nameof(IsActive),
typeof(bool),
typeof(CustomerCard),
new PropertyMetadata(false, OnIsActiveChanged));
public bool IsActive
{
get => (bool)GetValue(IsActiveProperty);
set => SetValue(IsActiveProperty, value);
}
private static void OnIsActiveChanged(
DependencyObject d,
DependencyPropertyChangedEventArgs e)
{
var control = (CustomerCard)d;
bool isActive = (bool)e.NewValue;
if (isActive)
{
// Activate control
}
else
{
// Deactivate control
}
}
This pattern is extremely common when creating custom WPF controls.
16. Why Is the Dependency Property Field Static? #
You will notice this pattern:
public static readonly DependencyProperty TitleProperty
The field is:
static
because the property definition belongs to the type, not to an individual instance.
For example, if you have 100 instances of:
CustomerCard
you don’t need 100 copies of the metadata describing what Title is.
The property registration is shared.
Each control instance can still have its own value.
17. Why Is It readonly? #
The field is normally declared:
public static readonly DependencyProperty
because after registration, the property identifier should not be replaced.
You don’t normally do:
TitleProperty = SomeOtherProperty;
The readonly keyword prevents reassignment after initialization.
18. Dependency Property vs CLR Property #
Here is the basic difference.
| Feature | CLR Property | Dependency Property |
|---|---|---|
| Private backing field | Usually | Not required |
| WPF binding integration | Limited | Yes |
| Styling | No | Yes |
| Animation | No | Yes |
| Property metadata | No | Yes |
| Default value | Manually implemented | Built in |
| Property system | No | Yes |
| Best for custom controls | Sometimes | Yes |
| Best for domain models | Yes | No |
A good rule is:
Use CLR properties for your business/domain objects and Dependency Properties for WPF controls and reusable UI components.
19. A More Advanced Registration Example #
You can provide several pieces of metadata when registering a Dependency Property.
For example:
public static readonly DependencyProperty FontSizeProperty =
DependencyProperty.Register(
nameof(FontSize),
typeof(double),
typeof(MyControl),
new FrameworkPropertyMetadata(
14.0,
FrameworkPropertyMetadataOptions.AffectsMeasure,
OnFontSizeChanged));
The important parts are:
14.0
The default value.
FrameworkPropertyMetadataOptions.AffectsMeasure
Tells WPF that changing this property can affect the control’s measurement.
OnFontSizeChanged
The callback invoked when the property changes.
This becomes especially useful when building advanced custom controls.
20. Common Mistakes #
Mistake 1: Using a backing field #
Don’t do this for a Dependency Property:
private string _title;
public string Title
{
get => _title;
set => _title = value;
}
Instead:
public string Title
{
get => (string)GetValue(TitleProperty);
set => SetValue(TitleProperty, value);
}
Mistake 2: Forgetting static readonly #
The registration should normally look like:
public static readonly DependencyProperty TitleProperty
not:
public DependencyProperty TitleProperty
Mistake 3: Incorrect owner type #
Make sure the owner type matches the class where the property is registered:
typeof(CustomerCard)
when the property belongs to:
CustomerCard
Mistake 4: Putting business logic in the CLR setter #
A common mistake is:
public string Title
{
get => (string)GetValue(TitleProperty);
set
{
SetValue(TitleProperty, value);
DoSomething();
}
}
This is problematic because WPF can change Dependency Property values through mechanisms other than directly calling the CLR setter, such as binding, styles, animations, and templates.
If you need to react to a change, use the property changed callback:
private static void OnTitleChanged(
DependencyObject d,
DependencyPropertyChangedEventArgs e)
{
// React to change
}
21. Dependency Property Registration Pattern to Remember #
For most custom properties, remember this pattern:
public static readonly DependencyProperty MyPropertyProperty =
DependencyProperty.Register(
nameof(MyProperty),
typeof(string),
typeof(MyControl),
new PropertyMetadata(
"Default Value",
OnMyPropertyChanged));
public string MyProperty
{
get => (string)GetValue(MyPropertyProperty);
set => SetValue(MyPropertyProperty, value);
}
private static void OnMyPropertyChanged(
DependencyObject d,
DependencyPropertyChangedEventArgs e)
{
var control = (MyControl)d;
// Handle property change
}
Once you understand this pattern, you can create most basic Dependency Properties in WPF.
22. Summary #
Dependency Properties are a core part of the WPF property system.
They are different from ordinary CLR properties because WPF can manage their values and integrate them with features such as:
- Data binding
- Styles
- Templates
- Animations
- Default values
- Property inheritance
- Change notifications
- Property metadata
The basic process is:
1. Register the Dependency Property
↓
2. Define the CLR wrapper
↓
3. Add metadata when required
↓
4. Add a property changed callback when needed
↓
5. Use the property from XAML, binding, styles, etc.
The most important pattern to remember is:
public static readonly DependencyProperty TitleProperty =
DependencyProperty.Register(
nameof(Title),
typeof(string),
typeof(MyControl),
new PropertyMetadata(
"Default",
OnTitleChanged));
public string Title
{
get => (string)GetValue(TitleProperty);
set => SetValue(TitleProperty, value);
}
private static void OnTitleChanged(
DependencyObject d,
DependencyPropertyChangedEventArgs e)
{
// React to changes
}
Understanding Dependency Properties is essential before moving into more advanced WPF topics such as custom controls, control templates, styles, triggers, animations, routed events, and advanced data binding.
