arrow-left arrow-right brightness-2 chevron-left chevron-right circle-half-full dots-horizontal facebook-box facebook loader magnify menu-down RSS star Twitter twitter GitHub white-balance-sunny window-close
WPF TemplateParts
2 min read

WPF TemplateParts

This is an old post and doesn't necessarily reflect my current thinking on a topic, and some links or images may not work. The text is preserved here for posterity.

I just answered a question on StackOverflow, where my answer involved using named controls in a ControlTemplate. It occurred to me that many WPF developers might not be aware of the feature, even though they've probably used it before.

Suppose you're an intern at Microsoft, and you've been told to implement the TextBox feature for WPF. It's complicated:

  • You need to handle selection of text, including commands like Ctrl+A
  • You need to handle copy/paste
  • You need to handle multiple languages and input methods
  • You need to position a caret between letters to show where the next keypress will be inserted

That's complicated enough. Then the pesky Expression Blend team approach you and add to the list:

  • Users should be able to override everything about how the TextBox is displayed

Let's take a TextBox, and add a custom control template, to make it look like a Search box:

<TextBox>
    <TextBox.Template>
        <ControlTemplate TargetType="TextBox">
            <Border>
                <DockPanel>
                    <Image Source="Search.png" DockPanel.Dock="Right" />
                    <ScrollViewer />
                </DockPanel>
            </Border>
        </ControlTemplate>
    </TextBox.Template>
</TextBox>

If you run the example above, it will work, but you won't be able to type in the TextBox.

Now, putting your intern cap on, you need to implement the 'caret' feature - you have to put a vertical bar somewhere to show where the next character will be inserted.

Where would you render the caret? On top of the Image? In the ScrollViewer? What if I had added 5 ScrollViewers? Clearly, you need an anchor point - a place where you know the text should go.

So the TextBox makes an assumption. You can do anything you like in the ControlTemplate, but you have to remember to add a control with a specific name - in this case, PART_ContentHost.

Let's fix our template:

<TextBox>
    <TextBox.Template>
        <ControlTemplate TargetType="TextBox">
            <Border>
                <DockPanel>
                    <Image Source="Search.png" DockPanel.Dock="Right" />
                    <ScrollViewer Name="PART_ContentHost" />
                </DockPanel>
            </Border>
        </ControlTemplate>
    </TextBox.Template>
</TextBox>

Now I can type in the TextBox.

The reason for this is that inside the TextBox, there's some code that looks (simplified) like this:

public override void OnApplyTemplate()
{
    base.OnApplyTemplate();

    anchor = Template.FindName("PART_ContentHost", this);
    // Store the anchor, so we can position the caret over the top of it later
}

Of course it's messier, since the Template could be set to null, and you might have to unhook events on the old anchor, but you get the idea.

How do you know about these magical strings, like PART_ContentHost? They are applied using the TemplatePart attribute.

TextBoxBase, for example, is declared something like this:

[TemplatePart(Name="PART_ContentHost", Type=typeof(FrameworkElement))]
public abstract class TextBoxBase : A500TypeDeepHierarchyOfClasses, IButNoInterfaces

You'll also usually find them in the documentation - for an example, see the TextBox Parts section on the TextBox styling documentation.

Paul Stovell's Blog

Hello, I'm Paul Stovell

I'm a Brisbane-based software developer, and founder of Octopus Deploy, a DevOps automation software company. This is my personal blog where I write about my journey with Octopus and software development.

I write new blog posts about once a month. Subscribe and I'll send you an email when I publish something new.

Subscribe

Comments