Problem populating a TextBox template

I am trying to reformat a TextBox to have two borders with an intermediate span; however, even when I explicitly set the registration on PART_ ContentHost to zero, the padding for the control always applies to the internal ScrollViewer .

A 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"/> 

As a result, a text field appears that looks like this: [15 [15 Text field 15]

while I was expecting: [15 [ Text box ] 15]

How to get PART_ContentHost (scrollviewer / text box) to have zero padding?

+6
source share
3 answers

It really looks like a strange mistake for me if someone has no better explanation for this behavior.

ScrollViewer (PART_ContentHost) internally uses Template as:

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

Interesting bit:

 <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 problem, you can simply set it to 0 instead of {TemplateBinding Padding} , and you will get the desired result.

But why do we need this?

TemplateBinding Padding seems to ignore the value set directly to the ScrollViewer , which is located in the inner area, and selects the ScrollViewer value inherited from the parent ( Button ), which is 15.

Well, that’s weird, but worse, this is just for Padding. Foreground , Background , Margin everything is fine, if they are installed directly on the ScrollViewer , they override the TextBox fields. I even confirmed moving Padding , installed directly in TextBox to use, to the default style installer to see if there was a priority issue.

This does not seem to be the case. Got the same conclusion.

Filling is defined in System.Windows.Controls.Control , which is the same class as Foreground and Background, from this inherited ScrollViewer . Not sure if only indents behave 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}" /> 

He prints 15,15,15,15. Doesn't do this for Margin .

The same effect with binding {Binding RelativeSource={RelativeSource FindAncestor, AncestorType={x:Type ScrollViewer}}, Path=Padding} .

I saw one message in which ScrollViewer does not pass on the properties set on it to children. Actually it didn’t work, since if it were like Background , Margin and sortings would be perfect for an excessive ride? What is special about Padding ? If this is a valid behavior, I really don't see how to get rid of this behavior without the Templating ScrollViewer as well, and this is one confusing implementation.

+6
source

This strange behavior comes from TextBoxBase . It overrides metadata for some of its dependency properties, and for the Padding property, it looks like this:

 Control.PaddingProperty.OverrideMetadata( typeof (TextBoxBase), new FrameworkPropertyMetadata( new PropertyChangedCallback(TextBoxBase.OnScrollViewerPropertyChanged))); 

If you look at the OnScrollViewerPropertyChanged handler, you will notice that it passes the value of the modified ScrollViewer property:

  if (newValue == DependencyProperty.UnsetValue) textBoxBase.ScrollViewer.ClearValue(e.Property); else textBoxBase.ScrollViewer.SetValue(e.Property, newValue); 

So, no matter what Padding value you set in the control template, it will be overwritten with the local TextBox value at run time.

To compensate for this addition, you can set a negative margin for ScrollViewer in the template:

 <ScrollViewer x:Name="PART_ContentHost" Margin="{TemplateBinding Padding, Converter={StaticResource InvertThicknessConverter}}" /> 

where InvertThicknessConverter is a value converter that negates each component of the passed thickness value:

 public class InvertThicknessConverter: IValueConverter { public object Convert(object value, Type targetType, object parameter, CultureInfo culture) { if (value is Thickness) return InvertThickness((Thickness)value); return value; } public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture) { if (value is Thickness) return InvertThickness((Thickness)value); return value; } private static Thickness InvertThickness(Thickness value) { return new Thickness(-value.Left, -value.Top, -value.Right, -value.Bottom); } } 
+3
source

I ran into this problem when setting the Padding property to a DataGridColumnHeader and circumvented it by specifying a padding property, but not non-zero:

 <Style x:Key="CustomHeader" TargetType="DataGridColumnHeader" BasedOn="{StaticResource {x:Type DataGridColumnHeader}}"> <!-- Why you might ask do we need a value that is near but not actually 0? No idea, but if it is not there, "4" is used. --> <!-- See http://stackoverflow.com/questions/16424739/textbox-template-padding-issue --> <Setter Property="Padding" Value="0,0,0.000000000001,0" /> </Style> 
0
source

Source: https://habr.com/ru/post/944461/


All Articles