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

Custom Controls and User Controls

Compare WPF's two approaches to building reusable controls — lightweight, composition-based UserControl versus fully templatable, lookless custom Control.

Graphics and AnimationAdvanced10 min readJul 10, 2026
Analogies

Custom Controls and User Controls

WPF offers two distinct patterns for building reusable UI components. A UserControl is built by composition: you author a XAML file with a fixed visual tree (typically laid out in a root Grid or StackPanel) and a code-behind class, and consumers cannot restyle its internal layout — they can only set the exposed DependencyProperties. A custom Control (deriving from Control or a more specific base like ButtonBase) is 'lookless,' meaning it defines behavior and DependencyProperties in a C# class but ships its visual appearance as a separate default ControlTemplate in Themes/Generic.xaml, so any consumer can completely replace its look via ControlTemplate while the control's logic and property contract remain intact.

🏏

Cricket analogy: This is like the difference between a fixed pre-recorded highlights package a broadcaster hands out unchanged versus raw match footage that any regional channel can re-edit with their own graphics package, where UserControl is the fixed package and lookless Control is the re-editable raw footage.

Building a UserControl

A UserControl is the right choice when you are composing existing controls into a reusable unit of behavior — for example a LabeledTextBox that pairs a TextBlock label with a TextBox and a validation icon — because you get simple XAML+code-behind development with x:Name access to child elements and no need to worry about template parts. You expose configurability through standard DependencyProperty declarations (often with a PropertyChangedCallback) on the UserControl's code-behind class, and internally wire the root layout's bindings to those properties using RelativeSource or ElementName bindings back to the UserControl instance itself.

🏏

Cricket analogy: This is like a franchise assembling a fixed opening pair of two specific batsmen into one dependable batting unit rather than inventing a new batting technique, similar to a UserControl composing existing controls like TextBlock and TextBox into one reusable unit.

Building a Lookless Custom Control

Deriving from Control (or a semantically closer base like RangeBase for a slider-like control) is the right choice when you need a reusable, restylable component that ships across multiple apps or themes — for example a RatingControl used by both a light and dark themed app. The class overrides OnApplyTemplate() to look up named 'template parts' via GetTemplateChild (conventionally marked with a [TemplatePart] attribute and an x:Name in the default template that must match exactly), and it defines its complete visual states — Normal, MouseOver, Pressed, Disabled — for the VisualStateManager so that any replacement ControlTemplate can opt into the same state machine.

🏏

Cricket analogy: This is like a national cricket board publishing an official rulebook and required field markings that any local ground can build, so long as the pitch dimensions and boundary rules match the spec, similar to OnApplyTemplate requiring named template parts to match the control's contract.

csharp
[TemplatePart(Name = PartTrack, Type = typeof(FrameworkElement))]
public class RatingControl : Control
{
    private const string PartTrack = "PART_Track";
    private FrameworkElement _track;

    static RatingControl()
    {
        DefaultStyleKeyProperty.OverrideMetadata(
            typeof(RatingControl),
            new FrameworkPropertyMetadata(typeof(RatingControl)));
    }

    public static readonly DependencyProperty ValueProperty =
        DependencyProperty.Register(nameof(Value), typeof(int), typeof(RatingControl),
            new FrameworkPropertyMetadata(0, OnValueChanged));

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

    public override void OnApplyTemplate()
    {
        base.OnApplyTemplate();
        _track = GetTemplateChild(PartTrack) as FrameworkElement;
        VisualStateManager.GoToState(this, "Normal", false);
    }

    private static void OnValueChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
    {
        // update visual state / raise routed event as needed
    }
}

For a custom Control, the default ControlTemplate lives in Themes/Generic.xaml and is discovered automatically via the DefaultStyleKeyProperty override — this is what lets consumers get a working default appearance out of the box while still being free to supply a completely different ControlTemplate through a Style setter.

A frequent mistake is deriving from Control and forgetting to override DefaultStyleKeyProperty metadata in the static constructor, which causes the control to render with no visual content at all because WPF has no default template to apply. Another common pitfall is calling GetTemplateChild with a name that does not exactly match the x:Name in Generic.xaml, which silently returns null instead of throwing.

  • UserControl composes existing controls into a fixed visual tree with code-behind; consumers can only set exposed DependencyProperties, not restyle internals.
  • A lookless custom Control derives from Control (or a more specific base) and separates behavior/properties from appearance, which lives in a replaceable ControlTemplate.
  • Custom Control appearance defaults live in Themes/Generic.xaml, discovered via the DefaultStyleKeyProperty override in the static constructor.
  • OnApplyTemplate() and GetTemplateChild() wire the control's logic to named template parts that any replacement template must supply with matching names.
  • VisualStateManager states (Normal, MouseOver, Pressed, Disabled) form a contract that any replacement ControlTemplate should honor.
  • Choose UserControl for app-specific composition of existing controls; choose custom Control for a reusable, restylable, cross-theme component.
  • Forgetting the DefaultStyleKeyProperty override is a common bug that leaves a custom Control rendering with no visible content.

Practice what you learned

Was this page helpful?

Topics covered

#NET#WPFWindowsPresentationFoundationStudyNotes#MicrosoftTechnologies#CustomControlsAndUserControls#Custom#Controls#User#Building#StudyNotes#SkillVeris