100% Free Forever
AI-Powered Learning
Industry Expert Content
Certificates & Badges
Learn At Your Own Pace
.NET

Dependency Properties

Learn why WPF replaces plain CLR properties with DependencyProperty for UI elements, enabling data binding, styling, animation, and property value inheritance.

Data BindingIntermediate11 min readJul 10, 2026
Analogies

Why WPF Needs a Different Kind of Property

A regular CLR auto-property just stores a value in a private backing field. WPF's dependency property system instead stores values in an internal, highly optimized dictionary-like structure owned by DependencyObject, and computes the effective value on demand by evaluating a precedence chain: local value, style triggers, templated parent, style setters, theme, and inherited or default value, in that order. This is what lets a single property like Button.Background be set by a local value, overridden by a Style, and animated, all without conflicting write paths.

🏏

Cricket analogy: It is like determining a player's final playing status through a precedence chain: a specific team announcement (local value) overrides a selection committee's provisional list (style), which overrides the general squad depth chart (default) — only the highest-precedence source wins.

Registering a Dependency Property

csharp
public class Gauge : Control
{
    public static readonly DependencyProperty ValueProperty =
        DependencyProperty.Register(
            name: "Value",
            propertyType: typeof(double),
            ownerType: typeof(Gauge),
            typeMetadata: new FrameworkPropertyMetadata(
                defaultValue: 0.0,
                flags: FrameworkPropertyMetadataOptions.AffectsRender,
                propertyChangedCallback: OnValueChanged),
            validateValueCallback: v => (double)v >= 0.0 && (double)v <= 100.0);

    public double Value
    {
        get => (double)GetValue(ValueProperty);
        set => SetValue(ValueProperty, value);
    }

    private static void OnValueChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
    {
        ((Gauge)d).InvalidateVisual();
    }
}

DependencyProperty.Register creates a static, per-type field that all instances of Gauge share as metadata, while GetValue/SetValue read and write the per-instance effective value from the DependencyObject's internal store. The CLR wrapper property (Value here) is purely a convenience layer — XAML, data binding, styles, and animation all talk to GetValue/SetValue directly and never go through the CLR getter/setter, which is why the wrapper must contain no extra logic beyond the GetValue/SetValue calls, or that logic will silently be bypassed.

🏏

Cricket analogy: It is like the official scorebook (the DependencyObject store) being the single source of truth every system reads from — TV graphics, the stadium screen, and the app all query the scorebook directly, not a commentator's personal notepad (the CLR wrapper) which is just a convenience summary.

Property Value Inheritance vs. Value Precedence

Not every dependency property participates in value inheritance — only those registered with FrameworkPropertyMetadataOptions.Inherits, such as TextElement.FontSize or DataContext, propagate a set value down to descendants automatically. This is a distinct mechanism from the ten-step value precedence chain: inheritance determines the fallback default a descendant sees when no higher-precedence local value, style, or trigger applies to it, while precedence determines which single source wins once a value is being resolved for a given element.

🏏

Cricket analogy: It is like a franchise's default kit color inherited by every age-group team (inheritance) unless a specific U19 team's sponsor overrides it for one tournament (precedence) — two separate mechanisms operating at different levels.

Dependency properties automatically support features you get 'for free' without extra code: data binding, styling (Setters and Triggers), animation via Storyboard, and change notification via the OnPropertyChanged callback wired at registration — none of which work on an ordinary CLR-only property.

  • A dependency property is registered once per owner type via DependencyProperty.Register and stores per-instance values in the DependencyObject's internal store, not in a private field.
  • The CLR wrapper property (get/set) must call only GetValue/SetValue — any extra logic there is bypassed by XAML, binding, styles, and animation.
  • Effective value is resolved through a precedence chain: local value beats triggers, style setters, inherited value, and default value, in that order.
  • Only properties registered with FrameworkPropertyMetadataOptions.Inherits propagate a set value down the visual tree automatically.
  • PropertyChangedCallback fires whenever the effective value changes for any reason, letting the owning class react (e.g., invalidate rendering).
  • ValidateValueCallback rejects invalid values before they are ever stored, independent of where the value came from.
  • Dependency properties are what make data binding, styling, animation, and property inheritance possible in WPF.

Practice what you learned

Was this page helpful?

Topics covered

#NET#WPFWindowsPresentationFoundationStudyNotes#MicrosoftTechnologies#DependencyProperties#Dependency#Properties#WPF#Needs#StudyNotes#SkillVeris