Search code examples
.netwpfcustom-controlsdesktop-application

Set the default Control Template of a Custom Control without using a Style


I'm looking for a way to more strongly bind the default Control Template of my custom control to the control instead of using a Style. I'll show my simplest example here to explain the need:

    public class UnitTextBox : TextBox
    {
        public string UnitLabel {...}
        public static readonly DependencyProperty UnitLabelProperty =
            DependencyProperty.Register("UnitLabel", typeof(string), typeof(UnitTextBox), new PropertyMetadata(""));
    }

This control just adds an extra string property for a unit label (like millimeters), given the following simple control template derived from TextBox, it puts a unit label within the text box to keep things nice and clean and properly dimensioned:

    <ControlTemplate x:Key="UnitTextBoxTemplate" TargetType="{x:Type local:UnitTextBox}">
        <Border x:Name="border" BorderBrush="{TemplateBinding BorderBrush}" BorderThickness="{TemplateBinding BorderThickness}" Background="{TemplateBinding Background}" SnapsToDevicePixels="True">
            <DockPanel>
                <TextBlock DockPanel.Dock="Right" Text="{TemplateBinding UnitLabel}" VerticalAlignment="Center" HorizontalAlignment="Right" Margin="8,0,2,0" Focusable="False" IsHitTestVisible="False" Background="Transparent" Foreground="{TemplateBinding Foreground}"
                            FontSize="{TemplateBinding FontSize}" FontStyle="{TemplateBinding FontStyle}"   />
                <ScrollViewer x:Name="PART_ContentHost" Focusable="False" HorizontalScrollBarVisibility="Hidden" VerticalScrollBarVisibility="Hidden"/>
            </DockPanel>
        </Border>
...(triggers)
    </ControlTemplate>

All of that works wonderfully, however this control is basically still a TextBox, and I have lots of styles for TextBoxes already that I use, so I don't want to use a default style to apply the template:

    <Style  TargetType="{x:Type local:UnitTextBox}">
        <Setter Property="Template" Value="{DynamicResource UnitTextBoxTemplate}"/>
    </Style>

Doing the above means that I can't use my RegularTextBox style which works just fine for my UnitTextBox. So instead, every time I wish to add a UnitTextBox I must explicitly specify the template that makes it function properly:

  <ctrl:UnitTextBox Template="{DynamicResource UnitTextBoxTemplate}" UnitLabel="mm/min" 
                    Style="{StaticResource RegularTextBox}"/>

I've been working with WPF for ages, but I do relatively few custom controls, and have suffered with they Default Style method when I do, but I'm thinking there must be a better way, but google-fu has failed me time and again.

I'm hoping there is some way to assign the template in the constructor/Init routine of UnitTextBox, but I'm struggling to reach the resource dictionary from code; so I'm wondering if there's a more esoteric way to do this.


Solution

  • If you don't want to define a default Style, you may handle the Loaded event in your control class and set the Template property in the event handler.

    public UnitTextBox() : base()
    {
        this.Loaded += UnitTextBox_Loaded;
    }
    
    private void UnitTextBox_Loaded(object sender, RoutedEventArgs e)
    {
        var res = this.FindResource("UnitTextBoxTemplate");
        this.Template = res as ControlTemplate;
    }