How to inherit all properties of a Control with a TemplateBinding
Asked Answered
S

2

7

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?

Silique answered 20/8, 2014 at 9:48 Comment(3)
No there isn't... you have to set every property that you want to use.Chinchin
Really? :-( Well, I guess a negative answer is also an answer. No work-arounds either?Silique
ALthough you create your own TextBox class it does not differ from ordinary situation. You override default template and in order to acquire any value from parent's property you need to do TemplateBinding.Hecklau
N
4

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>
Nostalgia answered 20/8, 2014 at 10:12 Comment(2)
Could you maybe elaborate on this a little bit? I don't entirely understand where this code is and/or can be editted. Cause in the TextBox Style there is no Property called Border that can be editted (only BorderThickness and BorderBrush).Silique
In your template instead of using another TextBox you should use the ScrollViewer with special name "PART_ContentHost". See my edit.Nostalgia
C
3

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 Bindings, 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'.

Chinchin answered 20/8, 2014 at 10:22 Comment(1)
TemplateBinding and RelativeSource.TemplatedParent are not same. First gets resolved at compile time and other at run time.Merchantable

© 2022 - 2024 — McMap. All rights reserved.