TextBox模板填充问题

时间:2013-05-07 17:07:45

标签: wpf textbox wpf-controls wpf-4.0

我正在尝试重新模板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)填充为零?

3 个答案:

答案 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上设置时,BackgroundMarginScrollViewer都可以覆盖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没有将设置的属性传递给它的孩子。真的不明白,因为如果是这样的情况BackgroundMargin怎么样才能超越?关于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