Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to inherit all properties of a Control with a TemplateBinding

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?

like image 458
Yellow Avatar asked Aug 20 '14 09:08

Yellow


2 Answers

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>
like image 60
Novitchi S Avatar answered Oct 12 '22 22:10

Novitchi S


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

like image 21
Sheridan Avatar answered Oct 12 '22 22:10

Sheridan