Custom controls and user controls
UserControl versus templated Control, control parts and states, dependency properties for reusable controls, and when composition beats templating.
Composition or templating
| UserControl | Templated Control | |
|---|---|---|
| Definition | XAML plus code-behind, fixed inner tree | A default style with a ControlTemplate |
| Restyling | Can only override properties | The whole structure can be replaced |
| Default style lookup | None | Themes/Generic.xaml |
| Use it for | Screens and panels inside your app | A control that ships in a library |
| Testability | Bound to its own DataContext | Cleaner - the template binds to the control |
If the control is application-specific, start with a UserControl. Moving to a templated control later is a rewrite, so make that jump only when consumers genuinely need to replace the visual structure.
A templated control with parts and states
[TemplatePart(Name = PartFill, Type = typeof(Border))]
public class ProgressPill : Control
{
private const string PartFill = "PART_Fill";
private Border _fill;
static ProgressPill()
{
DefaultStyleKeyProperty.OverrideMetadata(
typeof(ProgressPill), new FrameworkPropertyMetadata(typeof(ProgressPill)));
}
public static readonly DependencyProperty ValueProperty =
DependencyProperty.Register(nameof(Value), typeof(double), typeof(ProgressPill),
new FrameworkPropertyMetadata(0d, FrameworkPropertyMetadataOptions.AffectsRender));
public double Value
{
get => (double)GetValue(ValueProperty);
set => SetValue(ValueProperty, value);
}
public override void OnApplyTemplate()
{
base.OnApplyTemplate();
_fill = GetTemplateChild(PartFill) as Border; // may be null if the template omits it
}
}<!-- Themes/Generic.xaml: the default look for a templated control -->
<Style TargetType="{x:Type local:ProgressPill}">
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="{x:Type local:ProgressPill}">
<Border Background="#E6E6E6" CornerRadius="9" Height="18">
<Border x:Name="PART_Fill" Background="#2F6FED" CornerRadius="9"
HorizontalAlignment="Left"
Width="{Binding Value, RelativeSource={RelativeSource TemplatedParent}}" />
</Border>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>- Name parts with a
PART_prefix and declare them with[TemplatePart]- that is the documented contract. OnApplyTemplatecan run more than once, so detach from old parts before attaching to new ones.- Never assume the part exists; a consumer may supply a template without it. Null-check and degrade.
- Prefer visual states (
VisualStateManager) over triggers inside the template when the control must expose states such as Disabled or Busy.
User controls done well
public partial class SearchBox : UserControl
{
public static readonly DependencyProperty SearchTextProperty =
DependencyProperty.Register(nameof(SearchText), typeof(string), typeof(SearchBox),
new FrameworkPropertyMetadata("", FrameworkPropertyMetadataOptions.BindsTwoWayByDefault));
public string SearchText
{
get => (string)GetValue(SearchTextProperty);
set => SetValue(SearchTextProperty, value);
}
public SearchBox() => InitializeComponent();
}
// SearchBox.xaml: bind the inner text box to the control's own property,
// not to an inherited DataContext you do not control
// <TextBox Text="{Binding SearchText, RelativeSource={RelativeSource AncestorType=local:SearchBox}, UpdateSourceTrigger=PropertyChanged}" />💡
A
UserControl inherits the ambient DataContext, which is convenient inside one screen and a trap inside a reusable one: consumers will set a DataContext and your internal bindings will break. Bind inner elements to the control with RelativeSource AncestorType for anything reusable.FAQ
Where does Generic.xaml live?
In a
Themes folder at the root of the control library, with ThemeInfo declared in AssemblyInfo. Its build action must be Page, and the file name has to be exactly Generic.xaml.How do I let consumers restyle my control?
Expose dependency properties for the important values and keep the template in the default style. Consumers then override single properties, or supply a whole template, without touching your code.
Related
Dependency properties and routed events Resources, styles, templates and themes
Last refreshed 2026-09-18.