I am trying to re-template a TextBox
to have two borders with padding in-between; however, even when I explicitly set the padding on the PART_ContentHost
to zero, the padding for the control is always being applied to the inner ScrollViewer
as well.
Small example:
<Style TargetType="{x:Type TextBox}">
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="{x:Type TextBox}">
<Border Padding="{TemplateBinding Padding}"
BorderThickness="1"
BorderBrush="Black"
Background="LightBlue">
<ScrollViewer Padding="0" Margin="0" Background="Turquoise"
x:Name="PART_ContentHost" />
</Border>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
<TextBox Padding="15"/>
The result is a textbox that looks like: [15[15 Textfield 15]15]
while I expected just: [15[ Textfield ]15]
How do I properly force the PART_ContentHost ( scrollviewer / textfield) to have a zero padding?
This really looks like a weird bug to me unless someone has a better explanation for this behavior.
ScrollViewer
(PART_ContentHost) internally uses a Template
like:
<ControlTemplate TargetType="{x:Type ScrollViewer}">
<Grid x:Name="Grid"
Background="{TemplateBinding Background}">
<Grid.ColumnDefinitions>
<ColumnDefinition Width="*" />
<ColumnDefinition Width="Auto" />
</Grid.ColumnDefinitions>
<Grid.RowDefinitions>
<RowDefinition Height="*" />
<RowDefinition Height="Auto" />
</Grid.RowDefinitions>
<Rectangle x:Name="Corner"
Grid.Row="1"
Grid.Column="1"
Fill="{DynamicResource {x:Static SystemColors.ControlBrushKey}}" />
<ScrollContentPresenter x:Name="PART_ScrollContentPresenter"
Grid.Row="0"
Grid.Column="0"
Margin="{TemplateBinding Padding}"
CanContentScroll="{TemplateBinding CanContentScroll}"
CanHorizontallyScroll="False"
CanVerticallyScroll="False"
Content="{TemplateBinding Content}"
ContentTemplate="{TemplateBinding ContentTemplate}" />
<ScrollBar x:Name="PART_VerticalScrollBar"
Grid.Row="0"
Grid.Column="1"
AutomationProperties.AutomationId="VerticalScrollBar"
Cursor="Arrow"
Maximum="{TemplateBinding ScrollableHeight}"
Minimum="0"
ViewportSize="{TemplateBinding ViewportHeight}"
Visibility="{TemplateBinding ComputedVerticalScrollBarVisibility}"
Value="{Binding VerticalOffset,
Mode=OneWay,
RelativeSource={RelativeSource TemplatedParent}}" />
<ScrollBar x:Name="PART_HorizontalScrollBar"
Grid.Row="1"
Grid.Column="0"
AutomationProperties.AutomationId="HorizontalScrollBar"
Cursor="Arrow"
Maximum="{TemplateBinding ScrollableWidth}"
Minimum="0"
Orientation="Horizontal"
ViewportSize="{TemplateBinding ViewportWidth}"
Visibility="{TemplateBinding ComputedHorizontalScrollBarVisibility}"
Value="{Binding HorizontalOffset,
Mode=OneWay,
RelativeSource={RelativeSource TemplatedParent}}" />
</Grid>
</ControlTemplate>
The interesting bit is:
<ScrollContentPresenter x:Name="PART_ScrollContentPresenter"
Grid.Row="0"
Grid.Column="0"
Margin="{TemplateBinding Padding}"
CanContentScroll="{TemplateBinding CanContentScroll}"
CanHorizontallyScroll="False"
CanVerticallyScroll="False"
Content="{TemplateBinding Content}"
ContentTemplate="{TemplateBinding ContentTemplate}" />
Now to fix your issue you can just set the Margin there to 0 instead of {TemplateBinding Padding}
and you'll get your desired output.
But why did we need to do that?
TemplateBinding Padding
seems to be ignoring the value set directly on the ScrollViewer
which is in the inner scope and picks the Padding value inherited from the Parent(Button
) which is 15.
Ok that's weird, but what's worse is it's only for Padding. Foreground
, Background
, Margin
are all fine when set directly on ScrollViewer
they override the TextBox
's fields. I even to confirm moved the Padding
set directly on the TextBox
in usage to a default Style setter, to see if some precedence case was the problem.
It did not seem to be. Got same output.
Padding is defined in System.Windows.Controls.Control
which is the same class Foreground and Background are from that ScrollViewer
inherits. Not sure why padding alone is behaving differently.
I also tried changing the presenter to something like
<ScrollContentPresenter x:Name="PART_ScrollContentPresenter"
Grid.Row="0"
Grid.Column="0"
Margin="{TemplateBinding Margin}"
CanContentScroll="{TemplateBinding CanContentScroll}"
CanHorizontallyScroll="False"
CanVerticallyScroll="False"
Content="{TemplateBinding Padding}"
ContentTemplate="{TemplateBinding ContentTemplate}" />
It print's 15,15,15,15. Doesn't do that for the Margin
.
Same effect with a {Binding RelativeSource={RelativeSource FindAncestor, AncestorType={x:Type ScrollViewer}}, Path=Padding}
binding.
I saw one post saying ScrollViewer
does not pass properties set on it to it's children. Don't really get that since if that was the case how did Background
, Margin
and sorts be fine to over-ride? Whats so special about Padding
? If it is valid behavior, I don't really see how to get rid of that behavior without Templating the ScrollViewer
as well and it's one confusing implementation.