I am trying make my own version of a control, say a TextBox
, to which I want to add some custom UI. I am want to inherit from this class, so that the user can still call my special text box in the same way he would call a regular one. I have got the following:
// MySpecialTextBox.cs
public class MySpecialTextBox : TextBox
{
static MySpecialTextBox () { }
/* Some special properties */
}
In XAML:
<Style TargetType="{x:Type MySpecialTextBox}">
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="{x:Type MySpecialTextBox}">
<StackPanel>
<Rectangle Width="100" Height="3" Fill="Pink" /> <!-- My own fancy lay-out -->
<TextBox Text="{TemplateBinding Text}"/> <!-- This text box should 'inherit' ALL the properties from the caller -->
</StackPanel>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
My problem here is that now, the TextBox
only gets the properties you explicitly set in this template, in this case, only Text
. I would like to also bind to Background
, Foreground
, and every other possible property. Obviously I could do something like:
<ControlTemplate TargetType="{x:Type MySpecialTextBox}">
<StackPanel>
<Rectangle Width="100" Height="3" Fill="Pink" />
<TextBox Text="{TemplateBinding Text}"
Background="{TemplateBinding Background}"
Foreground="{TemplateBinding Foreground}"
AcceptsReturn="{TemplateBinding AcceptsReturn}"
AllowDrop="{TemplateBinding AllowDrop}"
<!-- ETCETERA -->
/>
</StackPanel>
</ControlTemplate>
listing all properties, but that feels horribly inefficient. Is there a way to bind to the entire parent in one go?
It is not a good idea to place another TextBox
element in a template for another TextBox
. What you should do is always edit the default template for a control when you are overriding its template.
So, in the default template for a TextBox
you will find the following element:
<Border x:Name="border" BorderBrush="{TemplateBinding BorderBrush}" BorderThickness="{TemplateBinding BorderThickness}" Background="{TemplateBinding Background}" SnapsToDevicePixels="True">
<ScrollViewer x:Name="PART_ContentHost" Focusable="False" HorizontalScrollBarVisibility="Hidden" VerticalScrollBarVisibility="Hidden"/>
</Border>
This border is responsible for drawing the TextBox
. If you use this part instead of your inner TextBox
you will have all other TextBox
properties "Inherited".
Edit:
The full template should be:
<Style TargetType="{x:Type my:MySpecialTextBox}">
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="{x:Type my:MySpecialTextBox}">
<StackPanel>
<Rectangle Width="100" Height="3" Fill="Pink" />
<!-- My own fancy lay-out -->
<Border x:Name="border" BorderBrush="{TemplateBinding BorderBrush}" BorderThickness="{TemplateBinding BorderThickness}" Background="{TemplateBinding Background}" SnapsToDevicePixels="True">
<ScrollViewer x:Name="PART_ContentHost" Focusable="False" HorizontalScrollBarVisibility="Hidden" VerticalScrollBarVisibility="Hidden"/>
</Border>
</StackPanel>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
A TemplateBinding
is an optimized form of a standard Binding
and roughly equivalent to using a Binding
with the RelativeSource
property set to RelativeSource.TemplatedParent
. As such and like standard Binding
s, can only be set on a single property. From the TemplateBindingExtension
Class page on MSDN:
You use
TemplateBinding
in template to bind to a value on the control the template is applied to.
From the TemplateBinding
Markup Extension page on MSDN:
...
TemplateBinding
has only one settable property...
This means that we cannot supply more than one property name to it and so it only works with a single property. Unfortunately, there is no convenient SuperTemplateBinding
that affects all properties, but even if there were, I can't imagine any easy way to map which properties should bind to which template properties. The answer is therefore 'No, there is no shortcut to inheriting property values from a templated parent control'.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With