我正在尝试重新模板TextBox
以使两个边框之间有填充;但是,即使我明确地将PART _ ContentHost
上的填充设置为零,控件的填充也始终应用于内部ScrollViewer
。
小例子:
<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"/>
结果是一个文本框,如下所示:[15 [15 Textfield 15] 15]
虽然我只期望:[15 [ Textfield ] 15]
如何正确强制PART_ContentHost(scrollviewer / textfield)填充为零?
答案 0 :(得分:7)
除非有人对此行为有更好的解释,否则这对我来说真的是一个奇怪的错误。
ScrollViewer
(PART_ContentHost)内部使用Template
之类的内容:
<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>
有趣的是:
<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}" />
现在要解决您的问题,您只需将保证金设置为0而不是{TemplateBinding Padding}
,您就可以获得所需的输出。
但为什么我们需要这样做?
TemplateBinding Padding
似乎忽略了直接在内部范围内的ScrollViewer
上设置的值,并选择从父(Button
)继承的Padding值,即15。 / p>
好的,这很奇怪,但更糟糕的是它只适用于Padding。直接在Foreground
上设置时,Background
,Margin
,ScrollViewer
都可以覆盖TextBox
的字段。我甚至要确认将Padding
设置中的TextBox
直接移动到默认样式设置器中,以查看是否存在某些优先级问题。
似乎没有。得到相同的输出。
填充在System.Windows.Controls.Control
中定义,它与前景相同,背景来自ScrollViewer
继承。不确定为什么单独填充表现不同。
我也尝试将演示者更改为
<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}" />
它打印的是15,15,15,15。不对Margin
。
与{Binding RelativeSource={RelativeSource FindAncestor, AncestorType={x:Type ScrollViewer}}, Path=Padding}
绑定相同的效果。
我看到一篇帖子说ScrollViewer
没有将设置的属性传递给它的孩子。真的不明白,因为如果是这样的情况Background
,Margin
怎么样才能超越?关于Padding
有什么特别之处?如果它是有效的行为,我并没有真正看到如何在不模仿ScrollViewer
的情况下摆脱这种行为,这是一个令人困惑的实现。
答案 1 :(得分:3)
这种奇怪的行为来自TextBoxBase
。
它会覆盖某些依赖项属性的元数据,并覆盖它的Padding
属性:
Control.PaddingProperty.OverrideMetadata(
typeof (TextBoxBase),
new FrameworkPropertyMetadata(
new PropertyChangedCallback(TextBoxBase.OnScrollViewerPropertyChanged)));
如果您查看OnScrollViewerPropertyChanged
处理程序,您会注意到它将已更改属性的值传递给它的ScrollViewer:
if (newValue == DependencyProperty.UnsetValue)
textBoxBase.ScrollViewer.ClearValue(e.Property);
else
textBoxBase.ScrollViewer.SetValue(e.Property, newValue);
因此,无论您在控制模板中设置什么Padding值,TextBox都会在运行时使用本地值覆盖它。
要补偿此填充,您可以在模板中为ScrollViewer设置负边距:
<ScrollViewer
x:Name="PART_ContentHost"
Margin="{TemplateBinding Padding, Converter={StaticResource InvertThicknessConverter}}"
/>
其中InvertThicknessConverter
是值转换器,它取消了传递的厚度值的每个分量:
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);
}
}
答案 2 :(得分:1)
我在#include <iostream>
上设置Padding
属性时遇到此问题,并通过指定填充属性close但不等于零来解决此问题:
DataGridColumnHeader